Skip to content

Commit

Permalink
#1942 WIP conversion of pointer-position packets
Browse files Browse the repository at this point in the history
using a more generic and flexible packet format
  • Loading branch information
totaam committed Jan 3, 2023
1 parent 2d0d919 commit 02eb6d5
Show file tree
Hide file tree
Showing 16 changed files with 135 additions and 62 deletions.
26 changes: 25 additions & 1 deletion xpra/client/client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,15 @@ def defaults_init(self):
self._protocol = None
self._priority_packets = []
self._ordinary_packets = []
self._pointer_sequence = {}
self._mouse_position = None
self._mouse_position_pending = None
self._mouse_position_send_time = 0
self._mouse_position_delay = MOUSE_DELAY
self._mouse_position_timer = 0
self._aliases = {}
#server state and caps:
self.server_packet_handlers = [] #["pointer"]
self.connection_established = False
self.completed_startup = False
self.uuid = get_user_uuid()
Expand Down Expand Up @@ -543,7 +545,29 @@ def send_positional(self, packet):
self.cancel_send_mouse_position_timer()
self.have_more()

def send_mouse_position(self, packet):
def next_pointer_sequence(self, device_id):
if device_id<0:
#unspecified device, don't bother with sequence numbers
return 0
seq = self._pointer_sequence.get(device_id, 0)+1
self._pointer_sequence[device_id] = seq
return seq

def send_mouse_position(self, device_id, wid, pos, modifiers=None, buttons=None, props=None):
if "pointer" in self.server_packet_handlers:
#v5 packet type, most attributes are optional:
attrs = props or {}
if modifiers is not None:
attrs["modifiers"] = modifiers
if buttons is not None:
attrs["buttons"] = buttons
seq = self.next_pointer_sequence(device_id)
packet = ("pointer", device_id, seq, wid, pos, attrs)
else:
#pre v5 packet format:
packet = ("pointer-position", wid, pos, modifiers or (), buttons or ())
if props:
packet += props.values()
if self._mouse_position_timer:
self._mouse_position_pending = packet
return
Expand Down
4 changes: 2 additions & 2 deletions xpra/client/client_window_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -941,8 +941,8 @@ def _do_motion_notify_event(self, event):
wid = self.get_mouse_event_wid(*pointer_data)
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_data, modifiers, buttons)
packet = ("pointer-position", wid, pointer_data, modifiers, buttons)
self._client.send_mouse_position(packet)
device_id = 0
self._client.send_mouse_position(device_id, wid, pointer_data, modifiers, buttons)

def _device_info(self, event):
try:
Expand Down
6 changes: 2 additions & 4 deletions xpra/client/mixins/window_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,10 +576,8 @@ def tray_mouseover(x, y):
traylog("tray_mouseover(%s, %s) tray=%s", x, y, tray)
if tray:
modifiers = self.get_current_modifiers()
buttons = []
pointer_packet = ["pointer-position", wid, self.cp(x, y), modifiers, buttons]
traylog("pointer_packet=%s", pointer_packet)
self.send_mouse_position(pointer_packet)
device_id = -1
self.send_mouse_position(device_id, wid, self.cp(x, y), modifiers)
def do_tray_geometry(*args):
#tell the "ClientTray" where it now lives
#which should also update the location on the server if it has changed
Expand Down
10 changes: 7 additions & 3 deletions xpra/net/quic/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

HttpConnection = Union[H0Connection, H3Connection]

DATAGRAM_PACKET_TYPES = os.environ.get("XPRA_QUIC_DATAGRAM_PACKET_TYPES", "pointer-position").split(",")
DATAGRAM_PACKET_TYPES = os.environ.get("XPRA_QUIC_DATAGRAM_PACKET_TYPES", "pointer").split(",")


class XpraQuicConnection(Connection):
Expand Down Expand Up @@ -80,7 +80,11 @@ def close(self):
def send_close(self, code : int = 1000, reason : str = ""):
if self.accepted:
data = close_packet(code, reason)
self.write(data, "close")
try:
self.write(data, "close")
except AssertionError as e:
#probably already closed
log(f"send_close: {e}")
else:
self.send_headers(self.stream_id, headers={":status" : code})
self.transmit()
Expand All @@ -104,7 +108,7 @@ def stream_write(self, buf, packet_type):
log.warn(f"Warning: missing packet type for {data}")
if packet_type in DATAGRAM_PACKET_TYPES:
self.connection.send_datagram(flow_id=self.stream_id, data=data)
log(f"sending {packet_type} using datagram")
log.warn(f"sending {packet_type} using datagram")
return len(buf)
stream_id = self.get_packet_stream_id(packet_type)
log("XpraQuicConnection.stream_write(%s, %s) using stream id %s",
Expand Down
5 changes: 3 additions & 2 deletions xpra/platform/darwin/shadow_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def stop_refresh(self, wid):
self.refresh_registered = False


def do_process_mouse_common(self, proto, wid, pointer, *args):
def do_process_mouse_common(self, proto, device_id, wid, pointer, props):
if proto not in self._server_sources:
return False
assert wid in self._id_to_window
Expand All @@ -183,7 +183,8 @@ def fake_key(self, keycode, press):

def do_process_button_action(self, proto, wid, button, pressed, pointer, modifiers, *args):
self._update_modifiers(proto, wid, modifiers)
pointer = self._process_mouse_common(proto, wid, pointer)
device_id = -1
pointer = self._process_mouse_common(proto, device_id, wid, pointer)
if pointer:
self.button_action(wid, pointer, button, pressed, -1, *args)

Expand Down
5 changes: 3 additions & 2 deletions xpra/platform/win32/shadow_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@ def get_pointer_position(self):
GetPhysicalCursorPos(byref(pos))
return pos.x, pos.y

def do_process_mouse_common(self, proto, wid, pointer, *_args):
def do_process_mouse_common(self, proto, device_id, wid, pointer, props):
ss = self._server_sources.get(proto)
if not ss:
return False
Expand Down Expand Up @@ -587,7 +587,8 @@ def fake_key(self, keycode, press):

def do_process_button_action(self, proto, wid, button, pressed, pointer, modifiers, *args):
self._update_modifiers(proto, wid, modifiers)
pointer = self._process_mouse_common(proto, wid, pointer)
device_id = -1
pointer = self._process_mouse_common(proto, device_id, wid, pointer)
if pointer:
self.get_server_source(proto).user_event()
self.button_action(wid, pointer, button, pressed, -1, *args)
Expand Down
26 changes: 14 additions & 12 deletions xpra/platform/xposix/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,8 +730,9 @@ def do_xi_motion(self, event, device):
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_data, modifiers, buttons)
packet = ["pointer-position", wid, pointer_data, modifiers, buttons] + self.get_pointer_extra_args(event)
client.send_mouse_position(packet)
device_id = 0
props = self.get_pointer_extra_args(event)
client.send_mouse_position(device_id, wid, pointer_data, modifiers, buttons, props)
#now see if we have anything to send as a wheel event:
if dx!=0 or dy!=0:
xinputlog("do_xi_motion(%s, %s) wheel deltas: dx=%i, dy=%i", event, device, dx, dy)
Expand All @@ -743,18 +744,19 @@ def intscaled(f):
return int(f*1000000), 1000000
def dictscaled(d):
return dict((k,intscaled(v)) for k,v in d.items())
raw_valuators = {}
#mouselog("raw(%s)=%s", raw_event_name, raw)
#IMPORTANT: do not change the insertion order of the keys in the props dictionary!
#(pre v5 servers rely on positional packets generated using a dictionary iterator)
props = {
"device" : event.device,
}
for k in ("x", "y", "x_root", "y_root"):
props[k] = intscaled(getattr(event, k))
props["valuators"] = dictscaled(event.valuators or {})
raw_event_name = event.name.replace("XI_", "XI_Raw") #ie: XI_Motion -> XI_RawMotion
raw = self.XI2.find_event(raw_event_name, event.serial)
#mouselog("raw(%s)=%s", raw_event_name, raw)
if raw:
raw_valuators = raw.raw_valuators
args = [event.device]
for x in ("x", "y", "x_root", "y_root"):
args.append(intscaled(getattr(event, x)))
for v in (event.valuators, raw_valuators):
args.append(dictscaled(v))
return args
props["raw-valuators"] = dictscaled(raw.raw_valuators if raw else {})
return props


class ClientExtras:
Expand Down
3 changes: 2 additions & 1 deletion xpra/server/dbus/dbus_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,8 @@ def SetKeyboardRepeat(self, repeat_delay, repeat_interval):
def MovePointer(self, wid, x, y):
wid, x, y = ni(wid), ni(x), ni(y)
self.log(".MovePointer(%i, %i, %i)", wid, x, y)
self.server._move_pointer(ni(wid), (ni(x), ni(y)))
device_id = -1
self.server._move_pointer(device_id, ni(wid), (ni(x), ni(y)))

@dbus.service.method(INTERFACE, in_signature='ib')
def MouseClick(self, button, pressed):
Expand Down
2 changes: 1 addition & 1 deletion xpra/server/gtk_server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ def set_desktop_geometry(self, width, height):
""" overriden by X11 seamless and desktop servers """


def _move_pointer(self, _wid, pos, *_args):
def _move_pointer(self, device_id, wid, pos, props=None):
x, y = pos
display = Gdk.Display.get_default()
display.warp_pointer(display.get_default_screen(), x, y)
Expand Down
51 changes: 43 additions & 8 deletions xpra/server/mixins/input_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def __init__(self):
self.input_devices = "auto"
self.input_devices_format = None
self.input_devices_data = None
self.pointer_sequence = {}

self.mod_meanings = {}
self.keyboard_config = None
Expand Down Expand Up @@ -329,10 +330,10 @@ def set_keymap(self, ss, force=False):

######################################################################
# pointer:
def _move_pointer(self, wid, pos, *args):
def _move_pointer(self, device_id, wid, pos, *args):
raise NotImplementedError()

def _adjust_pointer(self, proto, wid, pointer):
def _adjust_pointer(self, proto, device_id, wid, pointer):
#the window may not be mapped at the same location by the client:
ss = self.get_server_source(proto)
window = self._id_to_window.get(wid)
Expand All @@ -354,16 +355,15 @@ def _adjust_pointer(self, proto, wid, pointer):
return [ax, ay]+list(pointer[2:])
return pointer

def _process_mouse_common(self, proto, wid, opointer, *args):
pointer = self._adjust_pointer(proto, wid, opointer)
def _process_mouse_common(self, proto, device_id, wid, opointer, props=None):
pointer = self._adjust_pointer(proto, device_id, wid, opointer)
if not pointer:
return None
#TODO: adjust args too
if self.do_process_mouse_common(proto, wid, pointer, *args):
if self.do_process_mouse_common(proto, device_id, wid, pointer, props):
return pointer
return None

def do_process_mouse_common(self, proto, wid, pointer, *args):
def do_process_mouse_common(self, proto, device_id, wid, pointer, props):
return True

def _process_button_action(self, proto, packet):
Expand All @@ -385,6 +385,36 @@ def do_process_button_action(self, proto, wid, button, pressed, pointer, modifie
def _update_modifiers(self, proto, wid, modifiers):
""" servers subclasses may change the modifiers state """

def _process_pointer(self, proto, packet):
#v5 packet format
mouselog("_process_pointer(%s, %s) readonly=%s, ui_driver=%s",
proto, packet, self.readonly, self.ui_driver)
if self.readonly:
return
ss = self.get_server_source(proto)
if ss is None:
return
device_id, seq, wid, pdata, props = packet[1:6]
if device_id>=0:
highest_seq = self.pointer_sequence.get(device_id, 0)
if 0<=seq<=highest_seq:
mouselog(f"dropped outdated sequence {seq}, latest is {highest_seq}")
return
self.pointer_sequence[device_id] = seq
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()
self.last_mouse_user = ss.uuid
if self._process_mouse_common(proto, device_id, wid, pdata, props):
modifiers = props.get("modifiers")
if modifiers is not None:
self._update_modifiers(proto, wid, modifiers)


def _process_pointer_position(self, proto, packet):
mouselog("_process_pointer_position(%s, %s) readonly=%s, ui_driver=%s",
proto, packet, self.readonly, self.ui_driver)
Expand All @@ -402,7 +432,11 @@ def _process_pointer_position(self, proto, packet):
return
ss.user_event()
self.last_mouse_user = ss.uuid
if self._process_mouse_common(proto, wid, pdata, *packet[5:]):
props = {}
device_id = -1
if len(packet)>=6:
device_id = packet[5]
if self._process_mouse_common(proto, device_id, wid, pdata, props):
self._update_modifiers(proto, wid, modifiers)


Expand Down Expand Up @@ -439,6 +473,7 @@ def init_packet_handlers(self):
"keymap-changed" : self._process_keymap,
#mouse:
"button-action" : self._process_button_action,
"pointer" : self._process_pointer,
"pointer-position" : self._process_pointer_position,
#setup:
"input-devices" : self._process_input_devices,
Expand Down
3 changes: 2 additions & 1 deletion xpra/server/rfb/rfb_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,8 @@ def _process_rfb_PointerEvent(self, _proto, packet):
wid = self._get_rfb_desktop_wid()
def process_pointer_event():
mouselog("RFB PointerEvent(%#x, %s, %s) desktop wid=%s", buttons, x, y, wid)
self._move_pointer(wid, (x, y))
device_id = -1
self._move_pointer(device_id, wid, (x, y))
if buttons!=self.rfb_buttons:
#figure out which buttons have changed:
for button in range(8):
Expand Down
4 changes: 2 additions & 2 deletions xpra/server/shadow/gtk_shadow_server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,12 @@ def refresh_window_models(self):
self.refresh_window(window)


def _adjust_pointer(self, proto, wid, opointer):
def _adjust_pointer(self, proto, device_id, wid, opointer):
window = self._id_to_window.get(wid)
if not window:
self.suspend_cursor(proto)
return None
pointer = super()._adjust_pointer(proto, wid, opointer)
pointer = super()._adjust_pointer(proto, device_id, wid, opointer)
#the window may be at an offset (multi-window for multi-monitor):
wx, wy, ww, wh = window.get_geometry()
#or maybe the pointer is off-screen:
Expand Down
11 changes: 6 additions & 5 deletions xpra/x11/desktop/desktop_server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,8 @@ def _process_configure_window(self, proto, packet):
pwid = packet[10]
pointer = packet[11]
modifiers = packet[12]
if self._process_mouse_common(proto, pwid, pointer):
device_id = -1
if self._process_mouse_common(proto, device_id, pwid, pointer):
self._update_modifiers(proto, wid, modifiers)
#some "configure-window" packets are only meant for metadata updates:
skip_geometry = len(packet)>=10 and packet[9]
Expand All @@ -290,12 +291,12 @@ def _process_configure_window(self, proto, packet):
self.refresh_window_area(window, 0, 0, w, h)


def _adjust_pointer(self, proto, wid, pointer):
def _adjust_pointer(self, proto, device_id, wid, pointer):
window = self._id_to_window.get(wid)
if not window:
self.suspend_cursor(proto)
return None
pointer = super()._adjust_pointer(proto, wid, pointer)
pointer = super()._adjust_pointer(proto, device_id, wid, pointer)
#maybe the pointer is off-screen:
ww, wh = window.get_dimensions()
x, y = pointer[:2]
Expand All @@ -305,14 +306,14 @@ def _adjust_pointer(self, proto, wid, pointer):
self.restore_cursor(proto)
return pointer

def _move_pointer(self, wid, pos, *args):
def _move_pointer(self, device_id, wid, pos, props=None):
if wid>=0:
window = self._id_to_window.get(wid)
if not window:
mouselog("_move_pointer(%s, %s) invalid window id", wid, pos)
return
with xsync:
X11ServerBase._move_pointer(self, wid, pos, -1, *args)
X11ServerBase._move_pointer(self, device_id, wid, pos, props)


def _process_close_window(self, proto, packet):
Expand Down
7 changes: 4 additions & 3 deletions xpra/x11/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1060,8 +1060,9 @@ def _process_configure_window(self, proto, packet):
#some clients may send the OR window wid
#this causes focus issues (see #1999)
pwid = -1
device_id = -1
mouselog("configure pointer data: %s", (pwid, pointer, modifiers))
if self._process_mouse_common(proto, pwid, pointer):
if self._process_mouse_common(proto, device_id, pwid, pointer):
#only update modifiers if the window is in focus:
if self._has_focus==wid:
self._update_modifiers(proto, wid, modifiers)
Expand Down Expand Up @@ -1154,7 +1155,7 @@ def wn(w):

""" override so we can raise the window under the cursor
(gtk raise does not change window stacking, just focus) """
def _move_pointer(self, wid, pos, *args):
def _move_pointer(self, device_id, wid, pos, props=None):
if wid>0 and (self.last_raised!=wid or ALWAYS_RAISE_WINDOW):
window = self._lookup_window(wid)
if not window:
Expand All @@ -1164,7 +1165,7 @@ def _move_pointer(self, wid, pos, *args):
mouselog("raising %s", window)
with xswallow:
window.raise_window()
super()._move_pointer(wid, pos, *args)
super()._move_pointer(device_id, wid, pos, props)


def _process_close_window(self, proto, packet):
Expand Down
Loading

0 comments on commit 02eb6d5

Please sign in to comment.