Skip to content

Commit

Permalink
#1658 suspend and restore the cursor when crossing over to the paddin…
Browse files Browse the repository at this point in the history
…g area

git-svn-id: https://xpra.org/svn/Xpra/trunk@19621 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Jun 12, 2018
1 parent af148eb commit c9eafed
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 42 deletions.
25 changes: 25 additions & 0 deletions src/xpra/server/gtk_server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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()
Expand Down
3 changes: 3 additions & 0 deletions src/xpra/server/shadow/gtk_shadow_server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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):
Expand Down
88 changes: 46 additions & 42 deletions src/xpra/server/source/windows_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions src/xpra/x11/desktop_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down

0 comments on commit c9eafed

Please sign in to comment.