From 33631bbdedb3d16fca837464f43fbb9aba141ca8 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 31 Aug 2018 15:37:46 +0000 Subject: [PATCH] #1941 expose relative pointer position from clients if the server supports the new feature 'pointer.relative' git-svn-id: https://xpra.org/svn/Xpra/trunk@20252 3bb7dfac-3a0b-4e04-842a-767bc560f471 --- src/xpra/client/client_window_base.py | 18 ++++++++++----- src/xpra/client/gtk_base/gtk_client_base.py | 1 + .../client/gtk_base/gtk_client_window_base.py | 12 +++++++--- src/xpra/client/mixins/window_manager.py | 2 ++ src/xpra/platform/darwin/shadow_server.py | 6 ++--- src/xpra/platform/win32/shadow_server.py | 4 ++-- src/xpra/platform/xposix/gui.py | 7 ++++-- src/xpra/server/mixins/input_server.py | 12 ++++++---- src/xpra/server/source/windows_mixin.py | 5 ---- src/xpra/x11/server.py | 18 ++++++++------- src/xpra/x11/x11_server_core.py | 23 +++++++++++++++---- 11 files changed, 71 insertions(+), 37 deletions(-) diff --git a/src/xpra/client/client_window_base.py b/src/xpra/client/client_window_base.py index 8b9230428c..cd86de5b2c 100644 --- a/src/xpra/client/client_window_base.py +++ b/src/xpra/client/client_window_base.py @@ -687,11 +687,14 @@ def get_mouse_event_wid(self, _x, _y): def do_motion_notify_event(self, event): if self._client.readonly or self._client.server_readonly or not self._client.server_pointer: return - pointer, modifiers, buttons = self._pointer_modifiers(event) + pointer, relative_pointer, modifiers, buttons = self._pointer_modifiers(event) wid = self.get_mouse_event_wid(*pointer) - mouselog("do_motion_notify_event(%s) wid=%s / focus=%s / window wid=%i, device=%s, pointer=%s, modifiers=%s, buttons=%s", event, wid, self._client._focused, self._id, self._device_info(event), pointer, modifiers, buttons) - self._client.send_mouse_position(["pointer-position", wid, - pointer, modifiers, buttons]) + mouselog("do_motion_notify_event(%s) wid=%s / focus=%s / window wid=%i, device=%s, pointer=%s, relative pointer=%s, modifiers=%s, buttons=%s", event, wid, self._client._focused, self._id, self._device_info(event), pointer, relative_pointer, modifiers, buttons) + pdata = pointer + if self._client.server_pointer_relative: + pdata = list(pointer)+list(relative_pointer) + packet = ["pointer-position", wid, pdata, modifiers, buttons] + self._client.send_mouse_position(packet) def _device_info(self, event): try: @@ -702,7 +705,7 @@ def _device_info(self, event): def _button_action(self, button, event, depressed, *args): if self._client.readonly or self._client.server_readonly or not self._client.server_pointer: return - pointer, modifiers, buttons = self._pointer_modifiers(event) + pointer, relative_pointer, modifiers, buttons = self._pointer_modifiers(event) wid = self.get_mouse_event_wid(*pointer) mouselog("_button_action(%s, %s, %s) wid=%s / focus=%s / window wid=%i, device=%s, pointer=%s, modifiers=%s, buttons=%s", button, event, depressed, wid, self._client._focused, self._id, self._device_info(event), pointer, modifiers, buttons) #map wheel buttons via translation table to support inverted axes: @@ -719,8 +722,11 @@ def _button_action(self, button, event, depressed, *args): continue b = sb server_buttons.append(b) + pdata = pointer + if self._client.server_pointer_relative: + pdata = list(pointer)+list(relative_pointer) def send_button(pressed): - self._client.send_button(wid, server_button, pressed, pointer, modifiers, server_buttons, *args) + self._client.send_button(wid, server_button, pressed, pdata, modifiers, server_buttons, *args) pressed_state = self.button_state.get(button, False) if SIMULATE_MOUSE_DOWN and pressed_state is False and depressed is False: mouselog("button action: simulating a missing mouse-down event for window %s before sending the mouse-up event", wid) diff --git a/src/xpra/client/gtk_base/gtk_client_base.py b/src/xpra/client/gtk_base/gtk_client_base.py index 3a503ae893..bb88207a4c 100644 --- a/src/xpra/client/gtk_base/gtk_client_base.py +++ b/src/xpra/client/gtk_base/gtk_client_base.py @@ -714,6 +714,7 @@ def make_hello(self): capabilities["metadata.supported"] = ms updict(capabilities, "pointer", { "grabs" : True, + "relative" : True, }) updict(capabilities, "window", { "initiate-moveresize" : True, diff --git a/src/xpra/client/gtk_base/gtk_client_window_base.py b/src/xpra/client/gtk_base/gtk_client_window_base.py index 2878ac7610..67ce8e1bdf 100644 --- a/src/xpra/client/gtk_base/gtk_client_window_base.py +++ b/src/xpra/client/gtk_base/gtk_client_window_base.py @@ -1832,7 +1832,7 @@ def do_delete_event(self, event): return True - def _pointer(self, x, y): + def _offset_pointer(self, x, y): if self.window_offset: x -= self.window_offset[0] y -= self.window_offset[1] @@ -1841,13 +1841,19 @@ def _pointer(self, x, y): def _get_pointer(self, event): return event.x_root, event.y_root + def _get_relative_pointer(self, event): + return event.x, event.y + def _pointer_modifiers(self, event): x, y = self._get_pointer(event) - pointer = self._pointer(x, y) + rx, ry = self._get_relative_pointer(event) + #adjust for window offset: + pointer = self._offset_pointer(x, y) + relative_pointer = self._offset_pointer(rx, ry) #FIXME: state is used for both mods and buttons?? modifiers = self._client.mask_to_names(event.state) buttons = self._event_buttons(event) - v = pointer, modifiers, buttons + v = pointer, relative_pointer, modifiers, buttons mouselog("pointer_modifiers(%s)=%s (x_root=%s, y_root=%s, window_offset=%s)", event, v, event.x_root, event.y_root, self.window_offset) return v diff --git a/src/xpra/client/mixins/window_manager.py b/src/xpra/client/mixins/window_manager.py index d6847e2056..a0fa49f417 100644 --- a/src/xpra/client/mixins/window_manager.py +++ b/src/xpra/client/mixins/window_manager.py @@ -95,6 +95,7 @@ def __init__(self): self.server_input_devices = None self.server_precise_wheel = False + self.server_pointer_relative = False self.input_devices = "auto" self.overlay_image = None @@ -305,6 +306,7 @@ def parse_server_capabilities(self): #input devices: self.server_input_devices = c.strget("input-devices") self.server_precise_wheel = c.boolget("wheel.precise", False) + self.server_pointer_relative = c.boolget("pointer.relative", False) return True diff --git a/src/xpra/platform/darwin/shadow_server.py b/src/xpra/platform/darwin/shadow_server.py index 6f459f9fce..eea412888a 100644 --- a/src/xpra/platform/darwin/shadow_server.py +++ b/src/xpra/platform/darwin/shadow_server.py @@ -162,7 +162,7 @@ def stop_refresh(self, wid): def do_process_mouse_common(self, proto, wid, pointer, *args): assert proto in self._server_sources assert wid in self._id_to_window - CG.CGWarpMouseCursorPosition(pointer) + CG.CGWarpMouseCursorPosition(pointer[:2]) return pointer def fake_key(self, keycode, press): @@ -187,8 +187,8 @@ def button_action(self, pointer, button, pressed, _deviceid=-1, *args): args = [] for i in range(button): args.append(i==(button-1) and pressed) - log("CG.CGPostMouseEvent(%s, %s, %s, %s)", pointer, 1, button, args) - CG.CGPostMouseEvent(pointer, 1, button, *args) + log("CG.CGPostMouseEvent(%s, %s, %s, %s)", pointer[:2], 1, button, args) + CG.CGPostMouseEvent(pointer[:2], 1, button, *args) else: if not pressed: #we don't simulate press/unpress diff --git a/src/xpra/platform/win32/shadow_server.py b/src/xpra/platform/win32/shadow_server.py index 634404788e..89235b9fe4 100644 --- a/src/xpra/platform/win32/shadow_server.py +++ b/src/xpra/platform/win32/shadow_server.py @@ -472,7 +472,7 @@ def get_pointer_position(self): def do_process_mouse_common(self, proto, wid, pointer, *_args): #adjust pointer position for offset in client: try: - x, y = pointer + x, y = pointer[:2] assert SetPhysicalCursorPos(x, y) except Exception as e: log("SetPhysicalCursorPos%s failed", pointer, exc_info=True) @@ -521,7 +521,7 @@ def button_action(self, pointer, button, pressed, deviceid=-1, *args): elif event is NOEVENT: return dwFlags, dwData = event - x, y = pointer + x, y = pointer[:2] mouse_event(dwFlags, x, y, dwData, 0) def make_hello(self, source): diff --git a/src/xpra/platform/xposix/gui.py b/src/xpra/platform/xposix/gui.py index 6f74d4a736..52601db4e0 100644 --- a/src/xpra/platform/xposix/gui.py +++ b/src/xpra/platform/xposix/gui.py @@ -655,7 +655,7 @@ def do_xi_motion(self, event, device): client = window._client if client.readonly: return - pointer, modifiers, buttons = window._pointer_modifiers(event) + pointer, relative_pointer, modifiers, buttons = window._pointer_modifiers(event) wid = self.window.get_mouse_event_wid(*pointer) #log("server_input_devices=%s, server_precise_wheel=%s", client.server_input_devices, client.server_precise_wheel) valuators = event.valuators @@ -701,7 +701,10 @@ def do_xi_motion(self, event, device): #send plain motion first, if any: if unused_valuators: xinputlog("do_xi_motion(%s, %s) wid=%s / focus=%s / window wid=%i, device=%s, pointer=%s, modifiers=%s, buttons=%s", event, device, wid, window._client._focused, window._id, event.device, pointer, modifiers, buttons) - packet = ["pointer-position", wid, pointer, modifiers, buttons] + self.get_pointer_extra_args(event) + pdata = pointer + if client.server_pointer_relative: + pdata = list(pointer)+list(relative_pointer) + packet = ["pointer-position", wid, pdata, modifiers, buttons] + self.get_pointer_extra_args(event) client.send_mouse_position(packet) #now see if we have anything to send as a wheel event: if dx!=0 or dy!=0: diff --git a/src/xpra/server/mixins/input_server.py b/src/xpra/server/mixins/input_server.py index 9a283cb8cc..cc2810e64e 100644 --- a/src/xpra/server/mixins/input_server.py +++ b/src/xpra/server/mixins/input_server.py @@ -67,10 +67,11 @@ def get_ui_info(self, proto, client_uuids=None, *args): } return info - def get_server_features(self, source=None): + def get_server_features(self, _source=None): return { "toggle_keyboard_sync" : True, "input-devices" : self.input_devices, + "pointer.relative" : True, } def get_caps(self, _source): @@ -339,7 +340,7 @@ def _adjust_pointer(self, proto, wid, pointer): if wx!=cx or wy!=cy: dx, dy = wx-cx, wy-cy if dx!=0 or dy!=0: - px, py = pointer + px, py = pointer[:2] ax, ay = px+dx, py+dy mouselog("client %2i: server window position: %12s, client window position: %24s, pointer=%s, adjusted: %s", ss.counter, pos, mapped_at, pointer, (ax, ay)) return ax, ay @@ -382,12 +383,15 @@ def _process_pointer_position(self, proto, packet): ss = self._server_sources.get(proto) if ss is None: return - wid, pointer, modifiers = packet[1:4] + wid, pdata, modifiers = packet[1:4] + pointer = pdata[:2] + if ss.pointer_relative and len(pdata)>=4: + ss.mouse_last_relative_position = pdata[2:4] ss.mouse_last_position = pointer if self.ui_driver and self.ui_driver!=ss.uuid: return ss.user_event() - if self._process_mouse_common(proto, wid, pointer, *packet[5:]): + if self._process_mouse_common(proto, wid, pdata, *packet[5:]): self._update_modifiers(proto, wid, modifiers) diff --git a/src/xpra/server/source/windows_mixin.py b/src/xpra/server/source/windows_mixin.py index 7a21fd18ab..a6c171fccd 100644 --- a/src/xpra/server/source/windows_mixin.py +++ b/src/xpra/server/source/windows_mixin.py @@ -54,9 +54,6 @@ def init_from(self, _protocol, server): def init_state(self): #WindowSource for each Window ID self.window_sources = {} - # mouse echo: - self.mouse_show = False - self.mouse_last_position = None self.window_frame_sizes = {} self.suspended = False @@ -123,8 +120,6 @@ def parse_client_caps(self, c): self.send_cursors = self.send_windows and c.boolget("cursors") self.cursor_encodings = c.strlistget("encodings.cursor") self.send_bell = c.boolget("bell") - self.mouse_show = c.boolget("mouse.show") - self.mouse_last_position = c.intpair("mouse.initial-position") self.window_initiate_moveresize = c.boolget("window.initiate-moveresize") self.system_tray = c.boolget("system_tray") self.metadata_supported = c.strlistget("metadata.supported", DEFAULT_METADATA_SUPPORTED) diff --git a/src/xpra/x11/server.py b/src/xpra/x11/server.py index d072e23db8..14ec48b6b1 100644 --- a/src/xpra/x11/server.py +++ b/src/xpra/x11/server.py @@ -1211,14 +1211,16 @@ def paint_grey_rect(shade, x, y, w, h): else: cr.stroke() #FIXME: use server mouse position, and use current cursor shape - if ss and ss.mouse_last_position and ss.mouse_last_position!=(0, 0): - x, y = ss.mouse_last_position - cr.set_source_rgb(1.0, 0.5, 0.7) - cr.new_path() - cr.arc(x, y, 10.0, 0, 2.0 * math.pi) - cr.stroke_preserve() - cr.set_source_rgb(0.3, 0.4, 0.6) - cr.fill() + if ss: + mlp = getattr(ss, "mouse_last_position", (0, 0)) + if mlp!=(0, 0): + x, y = mlp + cr.set_source_rgb(1.0, 0.5, 0.7) + cr.new_path() + cr.arc(x, y, 10.0, 0, 2.0 * math.pi) + cr.stroke_preserve() + cr.set_source_rgb(0.3, 0.4, 0.6) + cr.fill() return False diff --git a/src/xpra/x11/x11_server_core.py b/src/xpra/x11/x11_server_core.py index 2cb5e23c52..3530848717 100644 --- a/src/xpra/x11/x11_server_core.py +++ b/src/xpra/x11/x11_server_core.py @@ -868,12 +868,27 @@ def get_pointer_device(self, deviceid): return device + def _get_pointer_abs_coordinates(self, wid, pos): + #simple absolute coordinates + x, y = pos[:2] + from xpra.server.mixins.window_server import WindowServer + if len(pos)>=4 and isinstance(self, WindowServer): + #relative coordinates + model = self._id_to_window.get(wid) + if model: + rx, ry = pos[2:4] + geom = model.get_geometry() + x = geom[0]+rx + y = geom[1]+ry + log("_get_pointer_abs_coordinates(%i, %s)=%s window geometry=%s", wid, pos, (x, y), geom) + return x, y + def _move_pointer(self, wid, pos, deviceid=-1, *args): #(this is called within an xswallow context) screen_no = self.get_screen_number(wid) device = self.get_pointer_device(deviceid) - mouselog("move_pointer(%s, %s, %s) screen_no=%i, device=%s", wid, pos, deviceid, screen_no, device) - x, y = pos + x, y = self._get_pointer_abs_coordinates(wid, pos) + mouselog("move_pointer(%s, %s, %s) screen_no=%i, device=%s, position=%s", wid, pos, deviceid, screen_no, device, (x, y)) try: device.move_pointer(screen_no, x, y, *args) except Exception as e: @@ -881,7 +896,7 @@ def _move_pointer(self, wid, pos, deviceid=-1, *args): mouselog.error(" %s", e) def do_process_mouse_common(self, proto, wid, pointer, deviceid=-1, *args): - log("do_process_mouse_common%s", tuple([proto, wid, pointer, deviceid]+list(args))) + mouselog("do_process_mouse_common%s", tuple([proto, wid, pointer, deviceid]+list(args))) if self.readonly: return None pos = self.root_window.get_pointer()[-3:-1] @@ -890,7 +905,7 @@ def do_process_mouse_common(self, proto, wid, pointer, deviceid=-1, *args): ss = self._server_sources.get(proto) if ss: uuid = ss.uuid - if pos!=pointer or self.input_devices=="xi": + if pos!=pointer[:2] or self.input_devices=="xi": self.last_mouse_user = uuid with xswallow: self._move_pointer(wid, pointer, deviceid, *args)