From c9eafedd7401cdab305144e89c11604fdb0b5184 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 12 Jun 2018 13:15:46 +0000 Subject: [PATCH] #1658 suspend and restore the cursor when crossing over to the padding area git-svn-id: https://xpra.org/svn/Xpra/trunk@19621 3bb7dfac-3a0b-4e04-842a-767bc560f471 --- src/xpra/server/gtk_server_base.py | 25 ++++++ .../server/shadow/gtk_shadow_server_base.py | 3 + src/xpra/server/source/windows_mixin.py | 88 ++++++++++--------- src/xpra/x11/desktop_server.py | 3 + 4 files changed, 77 insertions(+), 42 deletions(-) diff --git a/src/xpra/server/gtk_server_base.py b/src/xpra/server/gtk_server_base.py index 1ae9f8194d..7b7a337a1f 100644 --- a/src/xpra/server/gtk_server_base.py +++ b/src/xpra/server/gtk_server_base.py @@ -46,6 +46,7 @@ def __init__(self): self.idle_add = glib.idle_add self.timeout_add = glib.timeout_add self.source_remove = glib.source_remove + self.cursor_suspended = False ServerBase.__init__(self) def watch_keymap_changes(self): @@ -93,6 +94,30 @@ def get_ui_info(self, proto, *args): info.setdefault("cursor", {}).update(self.get_ui_cursor_info()) return info + + def suspend_cursor(self, proto): + #this is called by shadow and desktop servers + #when we're receiving pointer events but the pointer + #is no longer over the active window area, + #so we have to tell the client to switch back to the default cursor + if self.cursor_suspended: + return + self.cursor_suspended = True + ss = self._server_sources.get(proto) + if ss: + ss.cancel_cursor_timer() + ss.send_empty_cursor() + + def restore_cursor(self, proto): + #see suspend_cursor + if not self.cursor_suspended: + return + self.cursor_suspended = False + ss = self._server_sources.get(proto) + if ss: + ss.send_cursor() + + def send_initial_cursors(self, ss, _sharing=False): #cursors: get sizes and send: display = display_get_default() diff --git a/src/xpra/server/shadow/gtk_shadow_server_base.py b/src/xpra/server/shadow/gtk_shadow_server_base.py index b2a84aebba..9123c50dac 100644 --- a/src/xpra/server/shadow/gtk_shadow_server_base.py +++ b/src/xpra/server/shadow/gtk_shadow_server_base.py @@ -148,6 +148,7 @@ def makeRootWindowModels(self): def _adjust_pointer(self, proto, wid, pointer): window = self._id_to_window.get(wid) if not window: + self.suspend_cursor(proto) return None pointer = super(GTKShadowServerBase, self)._adjust_pointer(proto, wid, pointer) #the window may be at an offset (multi-window for multi-monitor): @@ -157,7 +158,9 @@ def _adjust_pointer(self, proto, wid, pointer): ax = x+wx ay = y+wy if ax<0 or ax>=ww or ay<0 or ay>=wh: + self.suspend_cursor(proto) return None + self.restore_cursor(proto) return ax, ay def get_pointer_position(self): diff --git a/src/xpra/server/source/windows_mixin.py b/src/xpra/server/source/windows_mixin.py index 8d9816372f..44efdcf010 100644 --- a/src/xpra/server/source/windows_mixin.py +++ b/src/xpra/server/source/windows_mixin.py @@ -240,50 +240,54 @@ def cancel_cursor_timer(self): def do_send_cursor(self, delay): self.cursor_timer = None cd = self.get_cursor_data_cb() - if cd and cd[0]: - cursor_data = list(cd[0]) - cursor_sizes = cd[1] - #skip first two fields (if present) as those are coordinates: - if self.last_cursor_sent and self.last_cursor_sent[2:9]==cursor_data[2:9]: - cursorlog("do_send_cursor(..) cursor identical to the last one we sent, nothing to do") - return - self.last_cursor_sent = cursor_data[:9] - w, h, _xhot, _yhot, serial, pixels, name = cursor_data[2:9] - #compress pixels if needed: - encoding = None - if pixels is not None: - #convert bytearray to string: - cpixels = strtobytes(pixels) - if "png" in self.cursor_encodings: - from xpra.codecs.loader import get_codec - PIL = get_codec("PIL") - assert PIL - cursorlog("do_send_cursor() loading %i bytes of cursor pixel data for %ix%i cursor named '%s'", len(cpixels), w, h, name) - img = PIL.Image.frombytes("RGBA", (w, h), cpixels, "raw", "BGRA", w*4, 1) - buf = BytesIOClass() - img.save(buf, "PNG") - pngdata = buf.getvalue() - buf.close() - cpixels = Compressed("png cursor", pngdata, can_inline=True) - encoding = "png" - if SAVE_CURSORS: - with open("raw-cursor-%#x.png" % serial, "wb") as f: - f.write(pngdata) - elif len(cpixels)>=256 and ("raw" in self.cursor_encodings or not self.cursor_encodings): - cpixels = self.compressed_wrapper("cursor", pixels) - cursorlog("do_send_cursor(..) pixels=%s ", cpixels) - encoding = "raw" - cursor_data[7] = cpixels - cursorlog("do_send_cursor(..) %sx%s %s cursor name='%s', serial=%#x with delay=%s (cursor_encodings=%s)", w, h, (encoding or "empty"), name, serial, delay, self.cursor_encodings) - args = list(cursor_data[:9]) + [cursor_sizes[0]] + list(cursor_sizes[1]) - if self.cursor_encodings and encoding: - args = [encoding] + args - else: - cursorlog("do_send_cursor(..) sending empty cursor with delay=%s", delay) - args = [""] - self.last_cursor_sent = None + if not cd or not cd[0]: + self.send_empty_cursor() + return + cursor_data = list(cd[0]) + cursor_sizes = cd[1] + #skip first two fields (if present) as those are coordinates: + if self.last_cursor_sent and self.last_cursor_sent[2:9]==cursor_data[2:9]: + cursorlog("do_send_cursor(..) cursor identical to the last one we sent, nothing to do") + return + self.last_cursor_sent = cursor_data[:9] + w, h, _xhot, _yhot, serial, pixels, name = cursor_data[2:9] + #compress pixels if needed: + encoding = None + if pixels is not None: + #convert bytearray to string: + cpixels = strtobytes(pixels) + if "png" in self.cursor_encodings: + from xpra.codecs.loader import get_codec + PIL = get_codec("PIL") + assert PIL + cursorlog("do_send_cursor() loading %i bytes of cursor pixel data for %ix%i cursor named '%s'", len(cpixels), w, h, name) + img = PIL.Image.frombytes("RGBA", (w, h), cpixels, "raw", "BGRA", w*4, 1) + buf = BytesIOClass() + img.save(buf, "PNG") + pngdata = buf.getvalue() + buf.close() + cpixels = Compressed("png cursor", pngdata, can_inline=True) + encoding = "png" + if SAVE_CURSORS: + with open("raw-cursor-%#x.png" % serial, "wb") as f: + f.write(pngdata) + elif len(cpixels)>=256 and ("raw" in self.cursor_encodings or not self.cursor_encodings): + cpixels = self.compressed_wrapper("cursor", pixels) + cursorlog("do_send_cursor(..) pixels=%s ", cpixels) + encoding = "raw" + cursor_data[7] = cpixels + cursorlog("do_send_cursor(..) %sx%s %s cursor name='%s', serial=%#x with delay=%s (cursor_encodings=%s)", w, h, (encoding or "empty"), name, serial, delay, self.cursor_encodings) + args = list(cursor_data[:9]) + [cursor_sizes[0]] + list(cursor_sizes[1]) + if self.cursor_encodings and encoding: + args = [encoding] + args self.send_more("cursor", *args) + def send_empty_cursor(self): + cursorlog("send_empty_cursor(..)") + self.last_cursor_sent = None + self.send_more("cursor", "") + + def bell(self, wid, device, percent, pitch, duration, bell_class, bell_id, bell_name): if not self.send_bell or self.suspended or not self.hello_sent: diff --git a/src/xpra/x11/desktop_server.py b/src/xpra/x11/desktop_server.py index 5926292cc4..b66ee1ea78 100644 --- a/src/xpra/x11/desktop_server.py +++ b/src/xpra/x11/desktop_server.py @@ -542,13 +542,16 @@ def _process_configure_window(self, proto, packet): def _adjust_pointer(self, proto, wid, pointer): window = self._id_to_window.get(wid) if not window: + self.suspend_cursor(proto) return None pointer = super(XpraDesktopServer, self)._adjust_pointer(proto, wid, pointer) #maybe the pointer is off-screen: ww, wh = window.get_dimensions() x, y = pointer if x<0 or x>=ww or y<0 or y>=wh: + self.suspend_cursor(proto) return None + self.restore_cursor(proto) return pointer def _move_pointer(self, wid, pos, *args):