| 1 | #!/usr/bin/env python |
| 2 | # This file is part of Xpra. |
| 3 | # Copyright (C) 2020 Antoine Martin <antoine@xpra.org> |
| 4 | # Xpra is released under the terms of the GNU GPL v2, or, at your option, any |
| 5 | # later version. See the file COPYING for details. |
| 6 | |
| 7 | import sys |
| 8 | import cairo |
| 9 | from time import sleep |
| 10 | |
| 11 | import gi |
| 12 | gi.require_version("Gtk", "3.0") |
| 13 | gi.require_version("Gdk", "3.0") |
| 14 | from gi.repository import Gtk, Gdk, GLib |
| 15 | |
| 16 | |
| 17 | class ReplayScroll(Gtk.Window): |
| 18 | |
| 19 | def __init__(self): |
| 20 | super().__init__() |
| 21 | |
| 22 | self.last = None |
| 23 | self.image = None |
| 24 | self.scrolls = [] |
| 25 | |
| 26 | self.darea = Gtk.DrawingArea() |
| 27 | self.darea.connect("draw", self.on_draw) |
| 28 | self.darea.set_size_request(1024, 1024) |
| 29 | self.add(self.darea) |
| 30 | |
| 31 | self.set_title("Replay Scroll ") |
| 32 | self.set_position(Gtk.WindowPosition.CENTER) |
| 33 | self.connect("delete-event", Gtk.main_quit) |
| 34 | self.show_all() |
| 35 | |
| 36 | def load_image(self, filename): |
| 37 | self.last = self.image |
| 38 | self.image = cairo.ImageSurface.create_from_png(filename) |
| 39 | self.resize(self.image.get_width(), self.image.get_height()) |
| 40 | self.darea.queue_draw() |
| 41 | |
| 42 | def step(self, message): |
| 43 | print(message) |
| 44 | self.darea.queue_draw() |
| 45 | |
| 46 | def apply_scroll(self, filename): |
| 47 | with open(filename, "rb") as f: |
| 48 | data = f.read() |
| 49 | self.scrolls = [] |
| 50 | for line in data.decode().splitlines(): |
| 51 | scroll = [int(x.strip()) for x in line.split(",")] |
| 52 | self.scrolls.append(scroll) |
| 53 | self.clear() |
| 54 | |
| 55 | def clear(self): |
| 56 | self.last = self.image |
| 57 | w, h = self.image.get_width(), self.image.get_height() |
| 58 | self.image = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h) |
| 59 | cr = cairo.Context(self.image) |
| 60 | cr.set_operator(cairo.OPERATOR_CLEAR) |
| 61 | cr.set_source_rgba(1, 1, 1, 1) |
| 62 | cr.rectangle(0, 0, w, h) |
| 63 | cr.fill() |
| 64 | self.image.flush() |
| 65 | self.step("clear") |
| 66 | GLib.timeout_add(1000, self.copy) |
| 67 | |
| 68 | def copy(self): |
| 69 | cr = cairo.Context(self.image) |
| 70 | cr.set_operator(cairo.OPERATOR_SOURCE) |
| 71 | cr.set_source_surface(self.last, 0, 0) |
| 72 | cr.paint() |
| 73 | self.image.flush() |
| 74 | self.step("copy") |
| 75 | GLib.timeout_add(1000, self.paint_scroll) |
| 76 | |
| 77 | def paint_scroll(self): |
| 78 | if not self.scrolls: |
| 79 | return False |
| 80 | scroll = self.scrolls[0] |
| 81 | sx,sy,sw,sh,xdelta,ydelta = scroll |
| 82 | self.scrolls = self.scrolls[1:] |
| 83 | self.step("scroll: %s" % (scroll)) |
| 84 | |
| 85 | cr = cairo.Context(self.image) |
| 86 | cr.set_operator(cairo.OPERATOR_SOURCE) |
| 87 | cr.set_source_surface(self.last, xdelta, ydelta) |
| 88 | x = sx+xdelta |
| 89 | y = sy+ydelta |
| 90 | cr.rectangle(x, y, sw, sh) |
| 91 | cr.fill() |
| 92 | if True: |
| 93 | color = 0, 0, 0, 0.6 |
| 94 | cr.set_line_width(1) |
| 95 | cr.set_source_rgba(*color) |
| 96 | cr.rectangle(x, y, sw, sh) |
| 97 | cr.stroke() |
| 98 | del cr |
| 99 | self.image.flush() |
| 100 | return bool(self.scrolls) |
| 101 | |
| 102 | |
| 103 | def on_draw(self, wid, cr): |
| 104 | if self.image: |
| 105 | cr.set_source_surface(self.image, 0, 0) |
| 106 | cr.paint() |
| 107 | |
| 108 | def main(): |
| 109 | app = ReplayScroll() |
| 110 | def update_image(): |
| 111 | for x in sys.argv[1:]: |
| 112 | if x.endswith(".png"): |
| 113 | app.load_image(x) |
| 114 | else: |
| 115 | app.apply_scroll(x) |
| 116 | GLib.timeout_add(1000, update_image) |
| 117 | Gtk.main() |
| 118 | |
| 119 | if __name__ == "__main__": |
| 120 | main() |