Ticket #173: xi-events-v11.patch
File xi-events-v11.patch, 79.0 KB (added by , 4 years ago) |
---|
-
etc/xpra/xorg.conf
7 7 Option "DontVTSwitch" "true" 8 8 Option "AllowMouseOpenFail" "true" 9 9 Option "PciForceNone" "true" 10 Option "AutoEnableDevices" " false"11 Option "AutoAddDevices" " false"10 Option "AutoEnableDevices" "true" 11 Option "AutoAddDevices" "true" 12 12 EndSection 13 13 14 14 Section "Device" -
setup.py
914 914 "xpra/x11/bindings/core_bindings.c", 915 915 "xpra/x11/bindings/posix_display_source.c", 916 916 "xpra/x11/bindings/ximage.c", 917 "xpra/x11/bindings/xi2_bindings.c", 917 918 "xpra/platform/win32/propsys.cpp", 918 919 "xpra/platform/darwin/gdk_bindings.c", 919 920 "xpra/net/bencode/cython_bencode.c", … … 1734 1735 ["xpra/x11/bindings/ximage.pyx"], 1735 1736 **pkgconfig("x11", "xcomposite", "xdamage", "xext") 1736 1737 )) 1738 cython_add(Extension("xpra.x11.bindings.xi2_bindings", 1739 ["xpra/x11/bindings/xi2_bindings.pyx"], 1740 **pkgconfig("x11", "xi") 1741 )) 1737 1742 1738 1743 toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11") 1739 1744 if gtk_x11_ENABLED: -
xpra/client/client_base.py
234 234 self._protocol.large_packets.append("keymap-changed") 235 235 self._protocol.large_packets.append("server-settings") 236 236 self._protocol.large_packets.append("logging") 237 self._protocol.large_packets.append("input-devices") 237 238 self._protocol.set_compression_level(self.compression_level) 238 239 self._protocol.receive_aliases.update(self._aliases) 239 240 self._protocol.enable_default_encoder() -
xpra/client/client_window_base.py
659 659 except: 660 660 return "" 661 661 662 def _button_action(self, button, event, depressed ):662 def _button_action(self, button, event, depressed, *args): 663 663 if self._client.readonly: 664 664 return 665 665 pointer, modifiers, buttons = self._pointer_modifiers(event) … … 680 680 b = sb 681 681 server_buttons.append(b) 682 682 def send_button(pressed): 683 self._client.send_button(wid, server_button, pressed, pointer, modifiers, server_buttons )683 self._client.send_button(wid, server_button, pressed, pointer, modifiers, server_buttons, *args) 684 684 pressed_state = self.button_state.get(button, False) 685 685 if SIMULATE_MOUSE_DOWN and pressed_state is False and depressed is False: 686 686 mouselog("button action: simulating a missing mouse-down event for window %s before sending the mouse-up event", wid) -
xpra/client/gtk_base/gtk_client_window_base.py
933 933 934 934 935 935 def do_motion_notify_event(self, event): 936 log.info("do_motion_notify_event") 936 937 if self.moveresize_event: 937 x_root, y_root, direction, button, start_buttons, wx, wy, ww, wh = self.moveresize_event 938 dirstr = MOVERESIZE_DIRECTION_STRING.get(direction, direction) 939 buttons = self._event_buttons(event) 940 if start_buttons is None: 941 #first time around, store the buttons 942 start_buttons = buttons 943 self.moveresize_event[4] = buttons 944 if (button>0 and button not in buttons) or (button==0 and start_buttons!=buttons): 945 geomlog("%s for window button %i is no longer pressed (buttons=%s) cancelling moveresize", dirstr, button, buttons) 946 self.moveresize_event = None 947 else: 948 x = event.x_root 949 y = event.y_root 950 dx = x-x_root 951 dy = y-y_root 952 #clamp resizing using size hints, 953 #or sane defaults: minimum of (1x1) and maximum of (2*15x2*25) 954 minw = self.geometry_hints.get("min_width", 1) 955 minh = self.geometry_hints.get("min_height", 1) 956 maxw = self.geometry_hints.get("max_width", 2**15) 957 maxh = self.geometry_hints.get("max_height", 2**15) 958 geomlog("%s: min=%ix%i, max=%ix%i, window=%ix%i, delta=%ix%i", dirstr, minw, minh, maxw, maxh, ww, wh, dx, dy) 959 if direction in (MOVERESIZE_SIZE_BOTTOMRIGHT, MOVERESIZE_SIZE_BOTTOM, MOVERESIZE_SIZE_BOTTOMLEFT): 960 #height will be set to: wh+dy 961 dy = max(minh-wh, dy) 962 dy = min(maxh-wh, dy) 963 elif direction in (MOVERESIZE_SIZE_TOPRIGHT, MOVERESIZE_SIZE_TOP, MOVERESIZE_SIZE_TOPLEFT): 964 #height will be set to: wh-dy 965 dy = min(wh-minh, dy) 966 dy = max(wh-maxh, dy) 967 if direction in (MOVERESIZE_SIZE_BOTTOMRIGHT, MOVERESIZE_SIZE_RIGHT, MOVERESIZE_SIZE_TOPRIGHT): 968 #width will be set to: ww+dx 969 dx = max(minw-ww, dx) 970 dx = min(maxw-ww, dx) 971 elif direction in (MOVERESIZE_SIZE_BOTTOMLEFT, MOVERESIZE_SIZE_LEFT, MOVERESIZE_SIZE_TOPLEFT): 972 #width will be set to: ww-dx 973 dx = min(ww-minw, dx) 974 dx = max(ww-maxw, dx) 975 #calculate move + resize: 976 if direction==MOVERESIZE_MOVE: 977 data = (wx+dx, wy+dy), None 978 elif direction==MOVERESIZE_SIZE_BOTTOMRIGHT: 979 data = None, (ww+dx, wh+dy) 980 elif direction==MOVERESIZE_SIZE_BOTTOM: 981 data = None, (ww, wh+dy) 982 elif direction==MOVERESIZE_SIZE_BOTTOMLEFT: 983 data = (wx+dx, wy), (ww-dx, wh+dy) 984 elif direction==MOVERESIZE_SIZE_RIGHT: 985 data = None, (ww+dx, wh) 986 elif direction==MOVERESIZE_SIZE_LEFT: 987 data = (wx+dx, wy), (ww-dx, wh) 988 elif direction==MOVERESIZE_SIZE_TOPRIGHT: 989 data = (wx, wy+dy), (ww+dx, wh-dy) 990 elif direction==MOVERESIZE_SIZE_TOP: 991 data = (wx, wy+dy), (ww, wh-dy) 992 elif direction==MOVERESIZE_SIZE_TOPLEFT: 993 data = (wx+dx, wy+dy), (ww-dx, wh-dy) 994 else: 995 #not handled yet! 996 data = None 997 geomlog("%s for window %ix%i: started at %s, now at %s, delta=%s, button=%s, buttons=%s, data=%s", dirstr, ww, wh, (x_root, y_root), (x, y), (dx, dy), button, buttons, data) 998 if data: 999 #modifying the window is slower than moving the pointer, 1000 #do it via a timer to batch things together 1001 self.moveresize_data = data 1002 if self.moveresize_timer is None: 1003 self.moveresize_timer = self.timeout_add(20, self.do_moveresize) 938 self.motion_moveresize(event) 1004 939 ClientWindowBase.do_motion_notify_event(self, event) 1005 940 941 def motion_moveresize(self, event): 942 x_root, y_root, direction, button, start_buttons, wx, wy, ww, wh = self.moveresize_event 943 dirstr = MOVERESIZE_DIRECTION_STRING.get(direction, direction) 944 buttons = self._event_buttons(event) 945 if start_buttons is None: 946 #first time around, store the buttons 947 start_buttons = buttons 948 self.moveresize_event[4] = buttons 949 if (button>0 and button not in buttons) or (button==0 and start_buttons!=buttons): 950 geomlog("%s for window button %i is no longer pressed (buttons=%s) cancelling moveresize", dirstr, button, buttons) 951 self.moveresize_event = None 952 else: 953 x = event.x_root 954 y = event.y_root 955 dx = x-x_root 956 dy = y-y_root 957 #clamp resizing using size hints, 958 #or sane defaults: minimum of (1x1) and maximum of (2*15x2*25) 959 minw = self.geometry_hints.get("min_width", 1) 960 minh = self.geometry_hints.get("min_height", 1) 961 maxw = self.geometry_hints.get("max_width", 2**15) 962 maxh = self.geometry_hints.get("max_height", 2**15) 963 geomlog("%s: min=%ix%i, max=%ix%i, window=%ix%i, delta=%ix%i", dirstr, minw, minh, maxw, maxh, ww, wh, dx, dy) 964 if direction in (MOVERESIZE_SIZE_BOTTOMRIGHT, MOVERESIZE_SIZE_BOTTOM, MOVERESIZE_SIZE_BOTTOMLEFT): 965 #height will be set to: wh+dy 966 dy = max(minh-wh, dy) 967 dy = min(maxh-wh, dy) 968 elif direction in (MOVERESIZE_SIZE_TOPRIGHT, MOVERESIZE_SIZE_TOP, MOVERESIZE_SIZE_TOPLEFT): 969 #height will be set to: wh-dy 970 dy = min(wh-minh, dy) 971 dy = max(wh-maxh, dy) 972 if direction in (MOVERESIZE_SIZE_BOTTOMRIGHT, MOVERESIZE_SIZE_RIGHT, MOVERESIZE_SIZE_TOPRIGHT): 973 #width will be set to: ww+dx 974 dx = max(minw-ww, dx) 975 dx = min(maxw-ww, dx) 976 elif direction in (MOVERESIZE_SIZE_BOTTOMLEFT, MOVERESIZE_SIZE_LEFT, MOVERESIZE_SIZE_TOPLEFT): 977 #width will be set to: ww-dx 978 dx = min(ww-minw, dx) 979 dx = max(ww-maxw, dx) 980 #calculate move + resize: 981 if direction==MOVERESIZE_MOVE: 982 data = (wx+dx, wy+dy), None 983 elif direction==MOVERESIZE_SIZE_BOTTOMRIGHT: 984 data = None, (ww+dx, wh+dy) 985 elif direction==MOVERESIZE_SIZE_BOTTOM: 986 data = None, (ww, wh+dy) 987 elif direction==MOVERESIZE_SIZE_BOTTOMLEFT: 988 data = (wx+dx, wy), (ww-dx, wh+dy) 989 elif direction==MOVERESIZE_SIZE_RIGHT: 990 data = None, (ww+dx, wh) 991 elif direction==MOVERESIZE_SIZE_LEFT: 992 data = (wx+dx, wy), (ww-dx, wh) 993 elif direction==MOVERESIZE_SIZE_TOPRIGHT: 994 data = (wx, wy+dy), (ww+dx, wh-dy) 995 elif direction==MOVERESIZE_SIZE_TOP: 996 data = (wx, wy+dy), (ww, wh-dy) 997 elif direction==MOVERESIZE_SIZE_TOPLEFT: 998 data = (wx+dx, wy+dy), (ww-dx, wh-dy) 999 else: 1000 #not handled yet! 1001 data = None 1002 geomlog("%s for window %ix%i: started at %s, now at %s, delta=%s, button=%s, buttons=%s, data=%s", dirstr, ww, wh, (x_root, y_root), (x, y), (dx, dy), button, buttons, data) 1003 if data: 1004 #modifying the window is slower than moving the pointer, 1005 #do it via a timer to batch things together 1006 self.moveresize_data = data 1007 if self.moveresize_timer is None: 1008 self.moveresize_timer = self.timeout_add(20, self.do_moveresize) 1009 1006 1010 def do_moveresize(self): 1007 1011 self.moveresize_timer = None 1008 1012 mrd = self.moveresize_data … … 1323 1327 def _pointer_modifiers(self, event): 1324 1328 x, y = self._get_pointer(event) 1325 1329 pointer = self._pointer(x, y) 1330 #FIXME: state is used for both mods and buttons?? 1326 1331 modifiers = self._client.mask_to_names(event.state) 1327 1332 buttons = self._event_buttons(event) 1328 1333 v = pointer, modifiers, buttons -
xpra/client/ui_client_base.py
266 266 self.server_is_desktop = False 267 267 self.server_supports_sharing = False 268 268 self.server_supports_window_filters = False 269 self.server_input_devices = None 269 270 #what we told the server about our encoding defaults: 270 271 self.encoding_defaults = {} 271 272 … … 424 425 self.client_supports_sharing = opts.sharing 425 426 self.log_both = (opts.remote_logging or "").lower()=="both" 426 427 self.client_supports_remote_logging = self.log_both or parse_bool("remote-logging", opts.remote_logging) 428 self.input_devices = opts.input_devices 427 429 #mouse wheel: 428 430 mw = (opts.mousewheel or "").lower().replace("-", "") 429 431 if mw not in FALSE_OPTIONS: … … 1201 1203 def scale_pointer(self, pointer): 1202 1204 return int(pointer[0]/self.xscale), int(pointer[1]/self.yscale) 1203 1205 1204 def send_button(self, wid, button, pressed, pointer, modifiers, buttons): 1205 def send_button(state): 1206 self.send_positional(["button-action", wid, 1207 button, state, 1208 pointer, modifiers, buttons]) 1206 def send_button(self, wid, button, pressed, pointer, modifiers, buttons, *args): 1209 1207 pressed_state = self._button_state.get(button, False) 1210 1208 if PYTHON3 and WIN32 and pressed_state==pressed: 1211 1209 mouselog("button action: unchanged state, ignoring event") 1212 1210 return 1213 1211 self._button_state[button] = pressed 1214 send_button(pressed) 1212 packet = ["button-action", wid, 1213 button, pressed, 1214 pointer, modifiers, buttons] + list(args) 1215 log.info("button packet: %s", packet) 1216 self.send_positional(packet) 1215 1217 1216 1218 1217 1219 def window_keyboard_layout_changed(self, window): … … 1941 1943 if self.webcam_option=="on" or self.webcam_option.find("/dev/video")>=0: 1942 1944 self.start_sending_webcam() 1943 1945 1946 #input devices: 1947 self.server_input_devices = c.strget("input-devices") 1948 1944 1949 #sound: 1945 1950 self.server_pulseaudio_id = c.strget("sound.pulseaudio.id") 1946 1951 self.server_pulseaudio_server = c.strget("sound.pulseaudio.server") … … 2144 2149 log.warn("received invalid control command from server: %s", command) 2145 2150 2146 2151 2152 def send_input_devices(self, input_devices): 2153 assert self.server_input_devices 2154 self.send("input-devices", input_devices) 2155 2156 2147 2157 def start_sending_webcam(self): 2148 2158 with self.webcam_lock: 2149 2159 self.do_start_sending_webcam(self.webcam_option) -
xpra/log.py
264 264 ])), 265 265 ("X11", OrderedDict([ 266 266 ("x11" , "All X11 code"), 267 ("xinput" , "XInput bindings"), 267 268 ("bindings" , "X11 Cython bindings"), 268 269 ("core" , "X11 core bindings"), 269 270 ("randr" , "X11 RandR bindings"), -
xpra/platform/darwin/shadow_server.py
139 139 GTKShadowServerBase.stop_refresh(self) 140 140 141 141 142 def do_process_mouse_common(self, proto, wid, pointer ):142 def do_process_mouse_common(self, proto, wid, pointer, *args): 143 143 CG.CGWarpMouseCursorPosition(pointer) 144 144 145 145 def fake_key(self, keycode, press): -
xpra/platform/features.py
15 15 SYSTEM_TRAY_SUPPORTED = False 16 16 REINIT_WINDOWS = False 17 17 18 INPUT_DEVICES = ["auto"] 19 18 20 CLIPBOARDS = [] 19 21 CLIPBOARD_WANT_TARGETS = envbool("XPRA_CLIPBOARD_WANT_TARGETS") 20 22 CLIPBOARD_GREEDY = envbool("XPRA_CLIPBOARD_GREEDY") … … 62 64 "CLIPBOARD_NATIVE_CLASS", 63 65 "UI_THREAD_POLLING", 64 66 "CLIENT_MODULES", 67 "INPUT_DEVICES", 65 68 ] 66 69 from xpra.platform import platform_import 67 70 platform_import(globals(), "features", False, -
xpra/platform/win32/shadow_server.py
341 341 log("refresh()=%s", v) 342 342 return v 343 343 344 def do_process_mouse_common(self, proto, wid, pointer ):344 def do_process_mouse_common(self, proto, wid, pointer, *args): 345 345 #adjust pointer position for offset in client: 346 346 try: 347 347 SetCursorPos(*pointer) -
xpra/platform/xposix/features.py
21 21 22 22 DEFAULT_SSH_CMD = "ssh" 23 23 CLIPBOARDS=["CLIPBOARD", "PRIMARY", "SECONDARY"] 24 25 INPUT_DEVICES = ["auto", "xi"] -
xpra/platform/xposix/gui.py
16 16 dbuslog = Logger("posix", "dbus") 17 17 traylog = Logger("posix", "menu") 18 18 menulog = Logger("posix", "menu") 19 mouselog = Logger("posix", "mouse") 19 20 20 21 from xpra.os_util import strtobytes, bytestostr 21 from xpra.util import iround, envbool 22 from xpra.util import iround, envbool, csv 22 23 from xpra.gtk_common.gobject_compat import get_xid, is_gtk3 23 24 25 try: 26 from xpra.x11.bindings.window_bindings import X11WindowBindings 27 from xpra.x11.bindings.xi2_bindings import X11XI2Bindings #@UnresolvedImport 28 except Exception as e: 29 log.error("no X11 bindings", exc_info=True) 30 X11WindowBindings = None 31 X11XI2Bindings = None 32 24 33 device_bell = None 25 34 GTK_MENUS = envbool("XPRA_GTK_MENUS", False) 26 35 RANDR_DPI = envbool("XPRA_RANDR_DPI", True) … … 93 102 def _get_X11_window_property(xid, name, req_type): 94 103 try: 95 104 from xpra.gtk_common.error import xsync 96 from xpra.x11.bindings.window_bindings import X11WindowBindings, PropertyError #@UnresolvedImport 97 window_bindings = X11WindowBindings() 105 from xpra.x11.bindings.window_bindings import PropertyError #@UnresolvedImport 98 106 try: 107 X11Window = X11WindowBindings() 99 108 with xsync: 100 prop = window_bindings.XGetWindowProperty(xid, name, req_type)109 prop = X11Window.XGetWindowProperty(xid, name, req_type) 101 110 log("_get_X11_window_property(%#x, %s, %s)=%s, len=%s", xid, name, req_type, type(prop), len(prop or [])) 102 111 return prop 103 112 except PropertyError as e: … … 108 117 return None 109 118 def _get_X11_root_property(name, req_type): 110 119 try: 111 from xpra.x11.bindings.window_bindings import X11WindowBindings #@UnresolvedImport 112 window_bindings = X11WindowBindings() 113 root_xid = window_bindings.getDefaultRootWindow() 120 X11Window = X11WindowBindings() 121 root_xid = X11Window.getDefaultRootWindow() 114 122 return _get_X11_window_property(root_xid, name, req_type) 115 123 except Exception as e: 116 124 log.warn("Warning: failed to get X11 root property '%s'", name) … … 170 178 171 179 def _get_xsettings(): 172 180 try: 173 from xpra.x11.bindings.window_bindings import X11WindowBindings #@UnresolvedImport 174 window_bindings = X11WindowBindings() 181 X11Window = X11WindowBindings() 175 182 selection = "_XSETTINGS_S0" 176 owner = window_bindings.XGetSelectionOwner(selection)183 owner = X11Window.XGetSelectionOwner(selection) 177 184 if not owner: 178 185 return None 179 186 XSETTINGS = "_XSETTINGS_SETTINGS" 180 data = window_bindings.XGetWindowProperty(owner, XSETTINGS, XSETTINGS)187 data = X11Window.XGetWindowProperty(owner, XSETTINGS, XSETTINGS) 181 188 if not data: 182 189 return None 183 190 from xpra.x11.xsettings_prop import get_settings 184 return get_settings( window_bindings.get_display_name(), data)191 return get_settings(X11Window.get_display_name(), data) 185 192 except Exception as e: 186 193 log("_get_xsettings error: %s", e) 187 194 return None … … 461 468 try: 462 469 from xpra.x11.gtk2 import gdk_display_source 463 470 assert gdk_display_source 464 from xpra.x11.bindings.window_bindings import constants , X11WindowBindings#@UnresolvedImport471 from xpra.x11.bindings.window_bindings import constants #@UnresolvedImport 465 472 X11Window = X11WindowBindings() 466 473 root_xid = X11Window.getDefaultRootWindow() 467 474 if window: … … 499 506 _toggle_wm_state(window, "_NET_WM_STATE_SHADED", shaded) 500 507 501 508 509 510 WINDOW_ADD_HOOKS = [] 511 def add_window_hooks(window): 512 global WINDOW_ADD_HOOKS 513 for x in WINDOW_ADD_HOOKS: 514 x(window) 515 log.warn("add_window_hooks(%s) added %s", window, WINDOW_ADD_HOOKS) 516 517 WINDOW_REMOVE_HOOKS = [] 518 def remove_window_hooks(window): 519 global WINDOW_REMOVE_HOOKS 520 for x in WINDOW_REMOVE_HOOKS: 521 x(window) 522 log.warn("remove_window_hooks(%s) added %s", window, WINDOW_REMOVE_HOOKS) 523 524 502 525 def get_info(): 503 526 from xpra.platform.gui import get_info_base 504 527 i = get_info_base() … … 516 539 return i 517 540 518 541 542 class XI2_Window(object): 543 def __init__(self, window): 544 log.warn("XI2_Window(%s)", window) 545 self.XI2 = X11XI2Bindings() 546 self.X11Window = X11WindowBindings() 547 self.window = window 548 self.xid = window.get_window().xid 549 self.windows = () 550 window.connect("configure-event", self.configured) 551 self.configured() 552 #replace event handlers with XI2 version: 553 self.do_motion_notify_event = window.do_motion_notify_event 554 window.do_motion_notify_event = self.noop 555 window.do_button_press_event = self.noop 556 window.do_button_release_event = self.noop 557 window.do_scroll_event = self.noop 558 window.connect("destroy", self.cleanup) 559 560 def noop(self, *args): 561 pass 562 563 def cleanup(self, *args): 564 for window in self.windows: 565 self.XI2.disconnect(window) 566 567 def configured(self, *args): 568 self.windows = self.get_parent_windows(self.xid) 569 for window in self.windows: 570 self.XI2.connect(window, "XI_Motion", self.do_xi_motion) 571 self.XI2.connect(window, "XI_ButtonPress", self.do_xi_button) 572 self.XI2.connect(window, "XI_ButtonRelease", self.do_xi_button) 573 574 def get_parent_windows(self, oxid): 575 windows = [oxid] 576 root = self.X11Window.getDefaultRootWindow() 577 xid = oxid 578 while True: 579 xid = self.X11Window.getParent(xid) 580 if xid==0 or xid==root: 581 break 582 windows.append(xid) 583 log("get_parent_windows(%#x)=%s", oxid, csv(hex(x) for x in windows)) 584 return windows 585 586 587 def do_xi_button(self, event): 588 window = self.window 589 if self.window._client.readonly: 590 return 591 #TODO: server needs to tell us if it handles xinput: 592 server_input_devices = "auto" 593 if server_input_devices=="xinput": 594 #skip synthetic scroll events for two-finger scroll, 595 #as the server should synthesize them from the motion events 596 #those have the same serial: 597 matching_motion = self.XI2.find_event("XI_Motion", event.serial) 598 #maybe we need more to distinguish? 599 server_input_devices = "auto" 600 if matching_motion: 601 return 602 button = event.detail 603 depressed = (event.name == "XI_ButtonPress") 604 args = self.get_pointer_extra_args(event) 605 window._button_action(button, event, depressed, *args) 606 607 def do_xi_motion(self, event): 608 window = self.window 609 if window.moveresize_event: 610 window.motion_moveresize(event) 611 self.do_motion_notify_event(event) 612 return 613 if window._client.readonly: 614 return 615 log("do_motion_notify_event(%s)", event) 616 #find the motion events in the xi2 event list: 617 pointer, modifiers, buttons = window._pointer_modifiers(event) 618 wid = self.window.get_mouse_event_wid(*pointer) 619 mouselog("do_motion_notify_event(%s) wid=%s / focus=%s / window wid=%i, device=%s, pointer=%s, modifiers=%s, buttons=%s", event, wid, window._client._focused, self.window._id, event.device, pointer, modifiers, buttons) 620 packet = ["pointer-position", wid, pointer, modifiers, buttons] + self.get_pointer_extra_args(event) 621 #mouselog("motion packet: %s", packet) 622 window._client.send_mouse_position(packet) 623 624 def get_pointer_extra_args(self, event): 625 def intscaled(f): 626 return int(f*1000000), 1000000 627 def dictscaled(v): 628 return dict((k,intscaled(v)) for k,v in v.items()) 629 raw_valuators = {} 630 raw_event_name = event.name.replace("XI_", "XI_Raw") #ie: XI_Motion -> XI_RawMotion 631 raw = self.XI2.find_event(raw_event_name, event.serial) 632 #log.info("raw(%s)=%s", raw_event_name, raw) 633 if raw: 634 raw_valuators = raw.raw_valuators 635 args = [event.device] 636 for x in ("x", "y", "x_root", "y_root"): 637 args.append(intscaled(getattr(event, x))) 638 for v in [event.valuators, raw_valuators]: 639 args.append(dictscaled(v)) 640 return args 641 642 519 643 class ClientExtras(object): 520 644 def __init__(self, client, opts): 521 645 self.client = client … … 528 652 self.x11_filter = None 529 653 if client.xsettings_enabled: 530 654 self.setup_xprops() 655 if client.input_devices=="xi": 656 self.setup_xi() 531 657 self.setup_dbus_signals() 532 658 533 659 def ready(self): … … 565 691 bus._clean_up_signal_match(self.upower_sleeping_match) 566 692 if self.login1_match: 567 693 bus._clean_up_signal_match(self.login1_match) 694 global WINDOW_METHOD_OVERRIDES 695 WINDOW_METHOD_OVERRIDES = {} 568 696 569 697 def resuming_callback(self, *args): 570 698 eventlog("resuming_callback%s", args) … … 647 775 except ImportError as e: 648 776 log.error("failed to load X11 properties/settings bindings: %s - root window properties will not be propagated", e) 649 777 778 def setup_xi(self): 779 from xpra.gtk_common.error import xsync 780 def may_enable_xi2(): 781 if self.client.server_input_devices!="xi": 782 log.warn("Warning: server does not support xi input devices") 783 return 784 try: 785 with xsync: 786 assert X11WindowBindings, "no X11 window bindings" 787 assert X11XI2Bindings, "no XI2 window bindings" 788 X11XI2Bindings().gdk_inject() 789 self.init_x11_filter() 790 XI2 = X11XI2Bindings() 791 XI2.select_xi2_events() 792 devices = XI2.get_devices() 793 if devices: 794 self.client.send_input_devices(devices) 795 except Exception as e: 796 log("enable_xi2()", exc_info=True) 797 log.error("Error: cannot enable XI2 events") 798 log.error(" %s", e) 799 else: 800 #register our enhanced event handlers: 801 self.add_xi2_method_overrides() 802 with xsync: 803 try: 804 #this would trigger warnings with our temporary opengl windows: 805 #only enable it after we have connected: 806 self.client.after_handshake(may_enable_xi2) 807 except Exception as e: 808 log("setup_xi()", exc_info=True) 809 log.error("Error: failed to load the XI2 bindings") 810 log.error(" %s", e) 811 812 def add_xi2_method_overrides(self): 813 global WINDOW_ADD_HOOKS 814 WINDOW_ADD_HOOKS = [XI2_Window] 815 816 650 817 def _get_xsettings(self): 651 818 try: 652 819 return self._xsettings_watcher.get_settings() -
xpra/scripts/config.py
489 489 "dbus-launch" : str, 490 490 "webcam" : str, 491 491 "mousewheel" : str, 492 "input-devices" : str, 492 493 #ssl options: 493 494 "ssl" : str, 494 495 "ssl-key" : str, … … 619 620 "quality", "min-quality", "speed", "min-speed", 620 621 "compression_level", 621 622 "dpi", "video-scaling", "auto-refresh-delay", 622 "webcam", "mousewheel", " pings",623 "webcam", "mousewheel", "input-devices", "pings", 623 624 "tray", "keyboard-sync", "cursors", "bell", "notifications", 624 625 "xsettings", "system-tray", "sharing", 625 626 "delay-tray", "windows", "readonly", … … 811 812 "dbus-launch" : "dbus-launch --close-stderr", 812 813 "webcam" : ["auto", "no"][OSX], 813 814 "mousewheel" : "on", 815 "input-devices" : "auto", 814 816 #ssl options: 815 817 "ssl" : "auto", 816 818 "ssl-key" : "", -
xpra/scripts/main.py
562 562 group.add_option("--mousewheel", action="store", 563 563 dest="mousewheel", default=defaults.mousewheel, 564 564 help="Mouse wheel forwarding, can be used to disable the device or invert some axes. Default: %s." % defaults.webcam) 565 from xpra.platform.features import INPUT_DEVICES 566 if len(INPUT_DEVICES)>1: 567 group.add_option("--input-devices", action="store", metavar="APINAME", 568 dest="input_devices", default=defaults.input_devices, 569 help="Which API to use for input devices. Default: %s." % defaults.input_devices) 570 else: 571 ignore({"input-devices" : INPUT_DEVICES[0]}) 565 572 legacy_bool_parse("global-menus") 566 573 group.add_option("--global-menus", action="store", 567 574 dest="global_menus", default=defaults.global_menus, metavar="yes|no", -
xpra/server/gtk_server_base.py
168 168 pass 169 169 170 170 171 def _move_pointer(self, wid, pos ):171 def _move_pointer(self, wid, pos, *args): 172 172 x, y = pos 173 173 display = gdk.display_get_default() 174 174 display.warp_pointer(display.get_default_screen(), x, y) -
xpra/server/server_base.py
143 143 self.webcam_encodings = [] 144 144 self.webcam_forwarding_device = None 145 145 self.virtual_video_devices = 0 146 self.input_devices = "auto" 147 self.input_devices_data = None 146 148 self.mem_bytes = 0 147 149 148 150 #sound: … … 256 258 self.notifications = opts.notifications 257 259 self.scaling_control = parse_bool_or_int("video-scaling", opts.video_scaling) 258 260 self.webcam_forwarding = opts.webcam.lower() not in FALSE_OPTIONS 261 self.input_devices = opts.input_devices or "auto" 259 262 260 263 #sound: 261 264 self.pulseaudio = opts.pulseaudio … … 752 755 "info-request": self._process_info_request, 753 756 "start-command": self._process_start_command, 754 757 "print": self._process_print, 758 "input-devices": self._process_input_devices, 755 759 # Note: "clipboard-*" packets are handled via a special case.. 756 760 }) 757 761 … … 1376 1380 #newer flags: 1377 1381 "av-sync", 1378 1382 "auto-video-encoding", 1379 "window-filters")) 1383 "window-filters", 1384 )) 1380 1385 f["sound"] = { 1381 1386 "ogg-latency-fix" : True, 1382 1387 "eos-sequence" : True, … … 1411 1416 "webcam" : self.webcam_forwarding, 1412 1417 "webcam.encodings" : self.webcam_encodings, 1413 1418 "virtual-video-devices" : self.virtual_video_devices, 1419 "input-devices" : self.input_devices, 1414 1420 }) 1415 1421 capabilities.update(self.file_transfer.get_file_transfer_features()) 1416 1422 capabilities.update(flatten_dict(self.get_server_features())) … … 2857 2863 ss.user_event() 2858 2864 2859 2865 2860 def _move_pointer(self, wid, pos ):2866 def _move_pointer(self, wid, pos, *args): 2861 2867 raise NotImplementedError() 2862 2868 2863 2869 def _adjust_pointer(self, proto, wid, pointer): … … 2878 2884 return px+(wx-cx), py+(wy-cy) 2879 2885 return pointer 2880 2886 2881 def _process_mouse_common(self, proto, wid, pointer ):2887 def _process_mouse_common(self, proto, wid, pointer, *args): 2882 2888 pointer = self._adjust_pointer(proto, wid, pointer) 2883 self.do_process_mouse_common(proto, wid, pointer) 2889 #TODO: adjust args too 2890 self.do_process_mouse_common(proto, wid, pointer, *args) 2884 2891 return pointer 2885 2892 2886 def do_process_mouse_common(self, proto, wid, pointer ):2893 def do_process_mouse_common(self, proto, wid, pointer, *args): 2887 2894 pass 2888 2895 2889 2896 … … 2915 2922 if self.ui_driver and self.ui_driver!=ss.uuid: 2916 2923 return 2917 2924 self._update_modifiers(proto, wid, modifiers) 2918 self._process_mouse_common(proto, wid, pointer )2925 self._process_mouse_common(proto, wid, pointer, *packet[5:]) 2919 2926 2920 2927 2921 2928 def _process_damage_sequence(self, proto, packet): … … 3176 3183 ss.send_webcam_stop(device, str(e)) 3177 3184 self.stop_virtual_webcam() 3178 3185 3186 def _process_input_devices(self, ss, packet): 3187 self.input_devices_data = packet[1] 3188 from xpra.util import print_nested_dict 3189 log.info("client input devices:") 3190 print_nested_dict(self.input_devices_data, print_fn=log.info) 3191 self.setup_input_devices() 3179 3192 3193 def setup_input_devices(self): 3194 pass 3195 3180 3196 def process_clipboard_packet(self, ss, packet): 3181 3197 if self.readonly: 3182 3198 return -
xpra/x11/bindings/core_bindings.pxd
15 15 cdef Display * display 16 16 cdef char * display_name 17 17 cdef Atom xatom(self, str_or_int) 18 19 cdef munge_packed_ints_to_longs(self, data) 20 cdef munge_packed_longs_to_ints(self, data) 18 21 # def get_error_text(self, code) -
xpra/x11/bindings/core_bindings.pyx
1 1 # This file is part of Xpra. 2 2 # Copyright (C) 2008, 2009 Nathaniel Smith <njs@pobox.com> 3 # Copyright (C) 2010-201 6Antoine Martin <antoine@devloop.org.uk>3 # Copyright (C) 2010-2017 Antoine Martin <antoine@devloop.org.uk> 4 4 # Xpra is released under the terms of the GNU GPL v2, or, at your option, any 5 5 # later version. See the file COPYING for details. 6 6 7 7 import os 8 8 import time 9 import struct 9 10 10 11 from xpra.util import dump_exc, envbool 11 12 from xpra.os_util import strtobytes … … 33 34 ctypedef int Bool 34 35 35 36 Atom XInternAtom(Display * display, char * atom_name, Bool only_if_exists) 37 char *XGetAtomName(Display *display, Atom atom) 36 38 37 39 int XFree(void * data) 38 40 … … 47 49 from display_source cimport get_display 48 50 from display_source import get_display_name 49 51 52 53 cdef _munge_packed_ints_to_longs(data): 54 assert len(data) % sizeof(int) == 0 55 n = len(data) / sizeof(int) 56 format_from = "@" + "i" * n 57 format_to = "@" + "l" * n 58 return struct.pack(format_to, *struct.unpack(format_from, data)) 59 60 cdef _munge_packed_longs_to_ints(data): 61 assert len(data) % sizeof(long) == 0 62 n = len(data) / sizeof(long) 63 format_from = "@" + "l" * n 64 format_to = "@" + "i" * n 65 #import binascii 66 #log.info("_munge_packed_longs_to_ints(%s) hex data=%s", data, binascii.hexlify(data)) 67 return struct.pack(format_to, *struct.unpack(format_from, data)) 68 69 50 70 cdef _X11CoreBindings singleton = None 51 71 def X11CoreBindings(): 52 72 global singleton … … 83 103 def get_xatom(self, str_or_int): 84 104 return self.xatom(str_or_int) 85 105 106 def XGetAtomName(self, Atom atom): 107 v = XGetAtomName(self.display, atom) 108 return v[:] 109 110 cdef munge_packed_ints_to_longs(self, data): 111 return _munge_packed_ints_to_longs(data) 112 113 cdef munge_packed_longs_to_ints(self, data): 114 return _munge_packed_longs_to_ints(data) 115 116 86 117 def get_error_text(self, code): 87 118 assert self.display!=NULL, "display is closed" 88 119 if type(code)!=int: -
xpra/x11/bindings/window_bindings.pyx
81 81 pass 82 82 83 83 Atom XInternAtom(Display * display, char * atom_name, Bool only_if_exists) 84 char *XGetAtomName(Display *display, Atom atom)85 84 86 85 Window XDefaultRootWindow(Display * display) 87 86 … … 357 356 void XDamageSubtract(Display *, Damage, XserverRegion repair, XserverRegion parts) 358 357 359 358 360 361 362 363 cpdef _munge_packed_ints_to_longs(data):364 assert len(data) % sizeof(int) == 0365 n = len(data) / sizeof(int)366 format_from = "@" + "i" * n367 format_to = "@" + "l" * n368 return struct.pack(format_to, *struct.unpack(format_from, data))369 370 cpdef _munge_packed_longs_to_ints(data):371 assert len(data) % sizeof(long) == 0372 n = len(data) / sizeof(long)373 format_from = "@" + "l" * n374 format_to = "@" + "i" * n375 return struct.pack(format_to, *struct.unpack(format_from, data))376 377 378 379 380 381 382 359 cdef long cast_to_long(i): 383 360 if i < 0: 384 361 return <long>i … … 461 438 return XDefaultRootWindow(self.display) 462 439 463 440 464 cpdef XGetAtomName(self, Atom atom):465 v = XGetAtomName(self.display, atom)466 return v[:]467 468 441 def MapWindow(self, Window xwindow): 469 442 XMapWindow(self.display, xwindow) 470 443 … … 905 878 0, 906 879 # This argument has to be divided by 4. Thus 907 880 # speaks the spec. 908 buffer_size / 4,881 buffer_size // 4, 909 882 False, 910 883 xreq_type, &xactual_type, 911 884 &actual_format, &nitems, &bytes_after, &prop) … … 937 910 data = (<char *> prop)[:nbytes] 938 911 XFree(prop) 939 912 if actual_format == 32: 940 return _munge_packed_longs_to_ints(data)913 return self.munge_packed_longs_to_ints(data) 941 914 else: 942 915 return data 943 916 … … 985 958 assert (len(data) % (format / 8)) == 0, "size of data is not a multiple of %s" % (format/8) 986 959 cdef int nitems = len(data) / (format / 8) 987 960 if format == 32: 988 data = _munge_packed_ints_to_longs(data)961 data = self.munge_packed_ints_to_longs(data) 989 962 cdef char * data_str 990 963 data_str = data 991 964 #print("XChangeProperty(%#x, %s, %s) data=%s" % (xwindow, property, value, str([hex(x) for x in data_str]))) -
xpra/x11/bindings/xi2_bindings.pyx
1 # This file is part of Xpra. 2 # Copyright (C) 2017 Antoine Martin <antoine@devloop.org.uk> 3 # Xpra is released under the terms of the GNU GPL v2, or, at your option, any 4 # later version. See the file COPYING for details. 5 6 import os 7 import time 8 import struct 9 import collections 10 11 from xpra.log import Logger 12 log = Logger("x11", "bindings", "xinput") 13 14 from xpra.x11.gtk2.common import X11Event 15 16 from libc.stdint cimport uintptr_t 17 18 19 ################################### 20 # Headers, python magic 21 ################################### 22 cdef extern from "string.h": 23 void* memset(void * ptr, int value, size_t num) 24 25 cdef extern from "X11/Xutil.h": 26 pass 27 28 ###### 29 # Xlib primitives and constants 30 ###### 31 32 include "constants.pxi" 33 ctypedef unsigned long CARD32 34 35 cdef extern from "X11/Xlib.h": 36 ctypedef struct Display: 37 pass 38 39 ctypedef CARD32 XID 40 ctypedef int Bool 41 ctypedef int Status 42 ctypedef CARD32 Atom 43 ctypedef XID Window 44 ctypedef CARD32 Time 45 46 ctypedef struct XGenericEventCookie: 47 int type # of event. Always GenericEvent 48 unsigned long serial 49 Bool send_event 50 Display *display 51 int extension #major opcode of extension that caused the event 52 int evtype #actual event type 53 unsigned int cookie 54 void *data 55 56 int XIAnyPropertyType 57 58 Atom XInternAtom(Display * display, char * atom_name, Bool only_if_exists) 59 int XFree(void * data) 60 61 Bool XQueryExtension(Display * display, char *name, 62 int *major_opcode_return, int *first_event_return, int *first_error_return) 63 64 Bool XGetEventData(Display *display, XGenericEventCookie *cookie) 65 void XFreeEventData(Display *display, XGenericEventCookie *cookie) 66 67 Window XDefaultRootWindow(Display * display) 68 69 Bool XQueryPointer(Display *display, Window w, Window *root_return, Window *child_return, int *root_x_return, int *root_y_return, 70 int *win_x_return, int *win_y_return, unsigned int *mask_return) 71 int XFlush(Display *dpy) 72 73 cdef extern from "X11/extensions/XInput2.h": 74 int XI_LASTEVENT 75 int XI_DeviceChanged 76 int XI_KeyPress 77 int XI_KeyRelease 78 int XI_ButtonPress 79 int XI_ButtonRelease 80 int XI_Motion 81 int XI_Enter 82 int XI_Leave 83 int XI_FocusIn 84 int XI_FocusOut 85 int XI_HierarchyChanged 86 int XI_PropertyEvent 87 int XI_RawKeyPress 88 int XI_RawKeyRelease 89 int XI_RawButtonPress 90 int XI_RawButtonRelease 91 int XI_RawMotion 92 int XI_TouchBegin 93 int XI_TouchUpdate 94 int XI_TouchEnd 95 int XI_TouchOwnership 96 int XI_RawTouchBegin 97 int XI_RawTouchUpdate 98 int XI_RawTouchEnd 99 100 int XIMasterPointer 101 int XIMasterKeyboard 102 int XISlavePointer 103 int XISlaveKeyboard 104 int XIFloatingSlave 105 106 int XIButtonClass 107 int XIKeyClass 108 int XIValuatorClass 109 int XIScrollClass 110 int XITouchClass 111 112 int XIAllDevices 113 int XIAllMasterDevices 114 115 ctypedef struct XIValuatorState: 116 int mask_len 117 unsigned char *mask 118 double *values 119 120 ctypedef struct XIEvent: 121 int type 122 unsigned long serial 123 Bool send_event 124 Display *display 125 int extension 126 int evtype 127 Time time 128 129 ctypedef struct XIRawEvent: 130 int type #GenericEvent 131 unsigned long serial 132 Bool send_event 133 Display *display 134 int extension #XI extension offset 135 int evtype #XI_RawKeyPress, XI_RawKeyRelease, etc 136 Time time 137 int deviceid 138 int sourceid 139 int detail 140 int flags 141 XIValuatorState valuators 142 double *raw_values 143 144 ctypedef struct XIButtonState: 145 int mask_len 146 unsigned char *mask 147 148 ctypedef struct XIModifierState: 149 int base 150 int latched 151 int locked 152 int effective 153 154 ctypedef XIModifierState XIGroupState 155 156 ctypedef struct XIDeviceEvent: 157 int type 158 unsigned long serial 159 Bool send_event 160 Display *display 161 int extension 162 int evtype 163 Time time 164 int deviceid 165 int sourceid 166 int detail 167 Window root 168 Window event 169 Window child 170 double root_x 171 double root_y 172 double event_x 173 double event_y 174 int flags 175 XIButtonState buttons 176 XIValuatorState valuators 177 XIModifierState mods 178 XIGroupState group 179 180 ctypedef struct XIHierarchyInfo: 181 int deviceid 182 int attachment 183 int use 184 Bool enabled 185 int flags 186 187 ctypedef struct XIHierarchyEvent: 188 int type 189 unsigned long serial 190 Bool send_event 191 Display *display 192 int extension 193 int evtype #XI_HierarchyChanged 194 Time time 195 int flags 196 int num_info 197 XIHierarchyInfo *info 198 199 ctypedef struct XIEventMask: 200 int deviceid 201 int mask_len 202 unsigned char* mask 203 204 ctypedef struct XIAnyClassInfo: 205 int type 206 int sourceid 207 208 ctypedef struct XIDeviceInfo: 209 int deviceid 210 char *name 211 int use 212 int attachment 213 Bool enabled 214 int num_classes 215 XIAnyClassInfo **classes 216 217 ctypedef struct XIButtonClassInfo: 218 int type 219 int sourceid 220 int num_buttons 221 Atom *labels 222 XIButtonState state 223 224 ctypedef struct XIKeyClassInfo: 225 int type 226 int sourceid 227 int num_keycodes 228 int *keycodes 229 230 ctypedef struct XIValuatorClassInfo: 231 int type 232 int sourceid 233 int number 234 Atom label 235 double min 236 double max 237 double value 238 int resolution 239 int mode 240 241 ctypedef struct XIScrollClassInfo: 242 int type 243 int sourceid 244 int number 245 int scroll_type 246 double increment 247 int flags 248 249 ctypedef struct XITouchClassInfo: 250 int type 251 int sourceid 252 int mode 253 int num_touches 254 255 Status XIQueryVersion(Display *display, int *major_version_inout, int *minor_version_inout) 256 Status XISelectEvents(Display *display, Window win, XIEventMask *masks, int num_masks) 257 XIDeviceInfo* XIQueryDevice(Display *display, int deviceid, int *ndevices_return) 258 void XIFreeDeviceInfo(XIDeviceInfo *info) 259 Atom *XIListProperties(Display *display, int deviceid, int *num_props_return) 260 Status XIGetProperty(Display *display, int deviceid, Atom property, long offset, long length, 261 Bool delete_property, Atom type, Atom *type_return, 262 int *format_return, unsigned long *num_items_return, 263 unsigned long *bytes_after_return, unsigned char **data) 264 265 266 DEF MAX_XI_EVENTS = 64 267 DEF XI_EVENT_MASK_SIZE = (MAX_XI_EVENTS+7)//8 268 269 XI_EVENT_NAMES = { 270 XI_DeviceChanged : "XI_DeviceChanged", 271 XI_KeyPress : "XI_KeyPress", 272 XI_KeyRelease : "XI_KeyRelease", 273 XI_ButtonPress : "XI_ButtonPress", 274 XI_ButtonRelease : "XI_ButtonRelease", 275 XI_Motion : "XI_Motion", 276 XI_Enter : "XI_Enter", 277 XI_Leave : "XI_Leave", 278 XI_FocusIn : "XI_FocusIn", 279 XI_FocusOut : "XI_FocusOut", 280 XI_HierarchyChanged : "XI_HierarchyChanged", 281 XI_PropertyEvent : "XI_PropertyEvent", 282 XI_RawKeyPress : "XI_RawKeyPress", 283 XI_RawKeyRelease : "XI_RawKeyRelease", 284 XI_RawButtonPress : "XI_RawButtonPress", 285 XI_RawButtonRelease : "XI_RawButtonRelease", 286 XI_RawMotion : "XI_RawMotion", 287 XI_TouchBegin : "XI_TouchBegin", 288 XI_TouchUpdate : "XI_TouchUpdate", 289 XI_TouchEnd : "XI_TouchEnd", 290 XI_TouchOwnership : "XI_TouchOwnership", 291 XI_RawTouchBegin : "XI_RawTouchBegin", 292 XI_RawTouchUpdate : "XI_RawTouchUpdate", 293 XI_RawTouchEnd : "XI_RawTouchEnd", 294 } 295 296 XI_USE = { 297 XIMasterPointer : "master pointer", 298 XIMasterKeyboard : "master keyboard", 299 XISlavePointer : "slave pointer", 300 XISlaveKeyboard : "slave keyboard", 301 XIFloatingSlave : "floating slave", 302 } 303 304 CLASS_INFO = { 305 XIButtonClass : "button", 306 XIKeyClass : "key", 307 XIValuatorClass : "valuator", 308 XIScrollClass : "scroll", 309 XITouchClass : "touch", 310 } 311 312 313 from core_bindings cimport _X11CoreBindings 314 315 cdef _X11XI2Bindings singleton = None 316 def X11XI2Bindings(): 317 global singleton 318 if singleton is None: 319 singleton = _X11XI2Bindings() 320 return singleton 321 322 cdef class _X11XI2Bindings(_X11CoreBindings): 323 324 cdef int opcode 325 cdef object events 326 cdef object event_handlers 327 328 def __init__(self): 329 self.opcode = -1 330 self.event_handlers = {} 331 self.reset_events() 332 333 def __repr__(self): 334 return "X11XI2Bindings(%s)" % self.display_name 335 336 def connect(self, window, event, handler): 337 self.event_handlers.setdefault(window, {})[event] = handler 338 339 def disconnect(self, window): 340 try: 341 del self.event_handlers[window] 342 except: 343 pass 344 345 346 def reset_events(self): 347 self.events = collections.deque(maxlen=100) 348 349 def find_event(self, event_name, serial): 350 for x in reversed(self.events): 351 #log.info("find_event(%s, %#x) checking %s", event_name, serial, x) 352 if x.name==event_name and x.serial==serial: 353 #log.info("matched") 354 return x 355 if x.serial<serial: 356 #log.info("serial too old") 357 return None 358 return None 359 360 def find_events(self, event_name, windows): 361 cdef Window found = 0 362 cdef Window window 363 matches = [] 364 for x in reversed(self.events): 365 window = x.xid 366 if x.name==event_name and ((found>0 and found==window) or (found==0 and window in windows)): 367 matches.append(x) 368 found = window 369 elif found: 370 break 371 return matches 372 373 cdef int get_xi_opcode(self, int major=2, int minor=2): 374 if self.opcode!=-1: 375 return self.opcode 376 cdef int opcode, event, error 377 if not XQueryExtension(self.display, "XInputExtension", &opcode, &event, &error): 378 log.warn("Warning: XI2 events are not supported") 379 self.opcode = 0 380 return 0 381 cdef int rmajor = major, rminor = minor 382 cdef int rc = XIQueryVersion(self.display, &rmajor, &rminor) 383 if rc == BadRequest: 384 log.warn("Warning: no XI2 %i.%i support,", major, minor) 385 log.warn(" server supports version %i.%i only", rmajor, rminor) 386 self.opcode = 0 387 return 0 388 elif rc: 389 log.warn("Warning: Xlib bug querying XI2, code %i", rc) 390 self.opcode = 0 391 return 0 392 self.opcode = opcode 393 log("get_xi_opcode%s=%i", (major, minor), opcode) 394 return opcode 395 396 cdef register_parser(self): 397 log("register_parser()") 398 if self.opcode>0: 399 from xpra.x11.gtk2.gdk_bindings import add_x_event_parser 400 add_x_event_parser(self.opcode, self.parse_xi_event) 401 402 cdef register_gdk_events(self): 403 log("register_gdk_events()") 404 if self.opcode<=0: 405 return 406 global XI_EVENT_NAMES 407 from xpra.x11.gtk2.gdk_bindings import add_x_event_signal, add_x_event_type_name 408 for e, xi_event_name in { 409 XI_DeviceChanged : "device-changed", 410 XI_KeyPress : "key-press", 411 XI_KeyRelease : "key-release", 412 XI_ButtonPress : "button-press", 413 XI_ButtonRelease : "button-release", 414 XI_Motion : "motion", 415 XI_Enter : "enter", 416 XI_Leave : "leave", 417 XI_FocusIn : "focus-in", 418 XI_FocusOut : "focus-out", 419 XI_HierarchyChanged : "focus-changed", 420 XI_PropertyEvent : "property-event", 421 XI_RawKeyPress : "raw-key-press", 422 XI_RawKeyRelease : "raw-key-release", 423 XI_RawButtonPress : "raw-button-press", 424 XI_RawButtonRelease : "raw-button-release", 425 XI_RawMotion : "raw-motion", 426 XI_TouchBegin : "touch-begin", 427 XI_TouchUpdate : "touch-update", 428 XI_TouchEnd : "touch-end", 429 XI_TouchOwnership : "touch-ownership", 430 XI_RawTouchBegin : "raw-touch-begin", 431 XI_RawTouchUpdate : "raw-touch-update", 432 XI_RawTouchEnd : "raw-touch-end", 433 }.items(): 434 event = self.opcode+e 435 add_x_event_signal(event, ("xi-%s" % xi_event_name, None)) 436 name = XI_EVENT_NAMES[e] 437 add_x_event_type_name(event, name) 438 439 def select_xi2_events(self): 440 cdef Window win = XDefaultRootWindow(self.display) 441 log("select_xi2_events() root window=%#x", win) 442 assert XI_LASTEVENT<MAX_XI_EVENTS, "bug: source needs to be updated, XI_LASTEVENT=%i" % XI_LASTEVENT 443 cdef XIEventMask evmasks[1] 444 cdef unsigned char mask1[XI_EVENT_MASK_SIZE] 445 memset(mask1, 0, XI_EVENT_MASK_SIZE) 446 #define XISetMask(ptr, event) (((unsigned char*)(ptr))[(event)>>3] |= (1 << ((event) & 7))) 447 #XISetMask(mask1, XI_RawMotion) 448 for e in ( 449 XI_KeyPress, XI_KeyRelease, 450 XI_Motion, 451 XI_HierarchyChanged, 452 XI_ButtonPress, XI_ButtonRelease, 453 XI_RawButtonPress, XI_RawButtonRelease, 454 XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd, 455 XI_RawTouchBegin, XI_RawTouchUpdate, XI_RawTouchEnd, 456 XI_RawMotion, 457 ): 458 mask1[e>>3] |= (1<< (e & 0x7)) 459 evmasks[0].deviceid = XIAllDevices #XIAllMasterDevices #XIAllDevices 460 evmasks[0].mask_len = XI_EVENT_MASK_SIZE 461 evmasks[0].mask = mask1 462 XISelectEvents(self.display, win, evmasks, 1) 463 XFlush(self.display) 464 465 def parse_xi_event(self, display, uintptr_t _cookie): 466 log("parse_xi_event(%s)", _cookie) 467 cdef XGenericEventCookie *cookie = <XGenericEventCookie*> _cookie 468 cdef XIDeviceEvent *device_e 469 cdef XIHierarchyEvent * hierarchy_e 470 cdef XIEvent *xie 471 cdef XIRawEvent *raw 472 cdef int i = 0, j = 0 473 if not XGetEventData(self.display, cookie): 474 return None 475 xie = <XIEvent*> cookie.data 476 device_e = <XIDeviceEvent*> cookie.data 477 cdef int xi_type = cookie.evtype 478 etype = self.opcode+xi_type 479 global XI_EVENT_NAMES 480 event_name = XI_EVENT_NAMES.get(xi_type) 481 if not event_name: 482 log("unknown XI2 event code: %i", xi_type) 483 return None 484 485 #don't parse the same thing again: 486 if len(self.events)>0: 487 last_event = self.events[-1] 488 if last_event.serial==xie.serial and last_event.type==etype: 489 return last_event 490 491 pyev = X11Event(event_name) 492 pyev.type = etype 493 pyev.display = display 494 pyev.send_event = bool(xie.send_event) 495 pyev.serial = xie.serial 496 pyev.time = int(xie.time) 497 pyev.window = int(XDefaultRootWindow(self.display)) 498 499 if xi_type in (XI_KeyPress, XI_KeyRelease, 500 XI_ButtonPress, XI_ButtonRelease, 501 XI_Motion, 502 XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd): 503 device = <XIDeviceEvent*> cookie.data 504 #pyev.source = device.sourceid #always 0 505 pyev.device = device.deviceid 506 pyev.detail = device.detail 507 pyev.flags = device.flags 508 pyev.window = int(device.child or device.event or device.root) 509 pyev.x_root = device.root_x 510 pyev.y_root = device.root_y 511 pyev.x = device.event_x 512 pyev.y = device.event_y 513 #mask = [] 514 valuators = {} 515 valuator = 0 516 for i in range(device.valuators.mask_len*8): 517 if device.valuators.mask[i>>3] & (1 << (i & 0x7)): 518 valuators[i] = device.valuators.values[valuator] 519 valuator += 1 520 pyev.valuators = valuators 521 buttons = [] 522 for i in range(device.buttons.mask_len): 523 if device.buttons.mask[i>>3] & (1<< (i & 0x7)): 524 buttons.append(i) 525 pyev.buttons = buttons 526 state = [] 527 pyev.state = state 528 pyev.modifiers = { 529 "base" : device.mods.base, 530 "latched" : device.mods.latched, 531 "locked" : device.mods.locked, 532 "effective" : device.mods.effective, 533 } 534 #make it compatible with gdk events: 535 pyev.state = device.mods.effective 536 elif xi_type in (XI_RawKeyPress, XI_RawKeyRelease, 537 XI_RawButtonPress, XI_RawButtonRelease, 538 XI_RawMotion, 539 XI_RawTouchBegin, XI_RawTouchUpdate, XI_RawTouchEnd): 540 raw = <XIRawEvent*> cookie.data 541 valuators = {} 542 raw_valuators = {} 543 valuator = 0 544 for i in range(raw.valuators.mask_len*8): 545 if raw.valuators.mask[i>>3] & (1 << (i & 0x7)): 546 valuators[i] = raw.valuators.values[valuator] 547 raw_valuators[i] = raw.raw_values[valuator] 548 valuator += 1 549 pyev.valuators = valuators 550 pyev.raw_valuators = raw_valuators 551 elif xi_type == XI_HierarchyChanged: 552 pass 553 #hierarchy_e = <XIHierarchyEvent*> cookie.data 554 #pyev.flags = hierarchy_e.flags 555 #info = {} 556 #for i in range(hierarchy_e.num_info): 557 # 558 XFreeEventData(self.display, cookie) 559 pyev.xid = pyev.window 560 self.events.append(pyev) 561 562 handler = self.event_handlers.get(pyev.window, {}).get(event_name) 563 log("parse_xi_event: %s, handler=%s", pyev, handler) 564 if handler: 565 handler(pyev) 566 return None 567 568 def get_devices(self, show_all=True, show_disabled=False): 569 global XI_USE 570 cdef int ndevices, i, j 571 cdef XIDeviceInfo *devices 572 cdef XIDeviceInfo *device 573 cdef XIAnyClassInfo *clazz 574 if show_all: 575 device_types = XIAllDevices 576 else: 577 device_types = XIAllMasterDevices 578 devices = XIQueryDevice(self.display, device_types, &ndevices) 579 dinfo = {} 580 for i in range(ndevices): 581 device = &devices[i] 582 if not device.enabled and not show_disabled: 583 continue 584 info = { 585 "name" : device.name, 586 "use" : XI_USE.get(device.use, "unknown use: %i" % device.use), 587 "attachment" : device.attachment, 588 "enabled" : device.enabled, 589 } 590 classes = {} 591 for j in range(device.num_classes): 592 clazz = device.classes[j] 593 classes[j] = self.get_class_info(clazz) 594 info["classes"] = classes 595 properties = self.get_device_properties(device.deviceid) 596 if properties: 597 info["properties"] = properties 598 dinfo[device.deviceid] = info 599 XIFreeDeviceInfo(devices) 600 return dinfo 601 602 def get_device_properties(self, deviceid): 603 cdef Atom *atoms 604 cdef int nprops, i 605 atoms = XIListProperties(self.display, deviceid, &nprops) 606 if atoms==NULL or nprops==0: 607 return None 608 props = {} 609 for i in range(nprops): 610 value = self.get_device_property(deviceid, atoms[i]) 611 if value is not None: 612 prop_name = self.XGetAtomName(atoms[i]) 613 props[prop_name] = value 614 return props 615 616 cdef get_device_property(self, int deviceid, Atom property, req_type=0): 617 #code mostly duplicated from window_bindings XGetWindowProperty: 618 cdef int buffer_size = 64 * 1024 619 cdef Atom xactual_type = <Atom> 0 620 cdef int actual_format = 0 621 cdef long offset = 0 622 cdef unsigned long nitems = 0, bytes_after = 0 623 cdef unsigned char *prop = NULL 624 cdef Status status 625 cdef Atom xreq_type = XIAnyPropertyType 626 if req_type: 627 xreq_type = self.get_xatom(req_type) 628 629 status = XIGetProperty(self.display, 630 deviceid, property, 631 0, 632 buffer_size//4, 633 False, 634 xreq_type, &xactual_type, 635 &actual_format, &nitems, &bytes_after, &prop) 636 if status != Success: 637 raise Exception("failed to retrieve XI property") 638 if xactual_type == XNone: 639 return None 640 if xreq_type and xreq_type != xactual_type: 641 raise Exception("expected %s but got %s" % (req_type, self.XGetAtomName(xactual_type))) 642 # This should only occur for bad property types: 643 assert not (bytes_after and not nitems) 644 if bytes_after: 645 raise Exception("reserved %i bytes for buffer, but data is bigger by %i bytes!" % (buffer_size, bytes_after)) 646 assert actual_format > 0 647 if actual_format == 8: 648 bytes_per_item = 1 649 elif actual_format == 16: 650 bytes_per_item = sizeof(short) 651 elif actual_format == 32: 652 bytes_per_item = 4 #sizeof(long) 653 else: 654 assert False 655 cdef int nbytes = bytes_per_item * nitems 656 data = (<char *> prop)[:nbytes] 657 XFree(prop) 658 prop_type = self.XGetAtomName(xactual_type) 659 import binascii 660 log.info("%s : %s value=%s", self.XGetAtomName(property), prop_type, data) 661 log.info("hex=%s (type=%s, nitems=%i, bytes per item=%i, actual format=%i)", binascii.hexlify(data), prop_type, nitems, bytes_per_item, actual_format) 662 663 #FIXME: 32 here.. breaks 664 if actual_format == 33: 665 data = self.munge_packed_longs_to_ints(data) 666 667 fmt = None 668 if prop_type=="INTEGER": 669 fmt = { 670 8 : "b", 671 16 : "h", 672 32 : "i", 673 }.get(actual_format) 674 elif prop_type=="CARDINAL": 675 fmt = { 676 8 : "B", 677 16 : "H", 678 32 : "I", 679 }.get(actual_format) 680 elif prop_type=="FLOAT": 681 fmt = "f" 682 if fmt: 683 log.info("parsing using %s", fmt*nitems) 684 value = struct.unpack(fmt*nitems, data) 685 if nitems==1: 686 return value[0] 687 return value 688 return data 689 690 cdef get_class_info(self, XIAnyClassInfo *class_info): 691 cdef int i 692 cdef XIButtonClassInfo *button 693 cdef XIKeyClassInfo *key 694 cdef XIValuatorClassInfo *valuator 695 cdef XIScrollClassInfo *scroll 696 cdef XITouchClassInfo *touch 697 info = { 698 "type" : CLASS_INFO.get(class_info.type, "unknown type: %i" % class_info.type), 699 "sourceid" : class_info.sourceid, 700 } 701 if class_info.type==XIButtonClass: 702 button = <XIButtonClassInfo*> class_info 703 buttons = [] 704 for i in range(button.num_buttons): 705 if button.labels[i]>0: 706 buttons.append(self.XGetAtomName(button.labels[i])) 707 info["buttons"] = buttons 708 #XIButtonState state 709 elif class_info.type==XIKeyClass: 710 key = <XIKeyClassInfo*> class_info 711 keys = [] 712 for i in range(key.num_keycodes): 713 keys.append(key.keycodes[i]) 714 elif class_info.type==XIValuatorClass: 715 valuator = <XIValuatorClassInfo*> class_info 716 info.update({ 717 "number" : valuator.number, 718 "min" : valuator.min, 719 "max" : valuator.max, 720 "value" : valuator.value, 721 "resolution": valuator.resolution, 722 "mode" : valuator.mode, 723 }) 724 if valuator.label: 725 info["label"] = self.XGetAtomName(valuator.label) 726 elif class_info.type==XIScrollClass: 727 scroll = <XIScrollClassInfo*> class_info 728 info.update({ 729 "number" : scroll.number, 730 "scroll-type" : scroll.scroll_type, 731 "increment" : scroll.increment, 732 "flags" : scroll.flags, 733 }) 734 elif class_info.type==XITouchClass: 735 touch = <XITouchClassInfo*> class_info 736 info.update({ 737 "mode" : touch.mode, 738 "num-touches" : touch.num_touches, 739 }) 740 return info 741 742 743 def gdk_inject(self): 744 self.get_xi_opcode() 745 #log.info("XInput Devices:") 746 #from xpra.util import print_nested_dict 747 #print_nested_dict(self.get_devices(), print_fn=log.info) 748 self.register_parser() 749 self.register_gdk_events() 750 #self.select_xi2_events() -
xpra/x11/desktop_server.py
352 352 self._damage(window, 0, 0, w, h) 353 353 354 354 355 def _move_pointer(self, wid, pos ):355 def _move_pointer(self, wid, pos, *args): 356 356 if wid>=0: 357 357 window = self._id_to_window.get(wid) 358 358 if not window: … … 360 360 else: 361 361 #TODO: just like shadow server, adjust for window position 362 362 pass 363 X11ServerBase._move_pointer(self, wid, pos )363 X11ServerBase._move_pointer(self, wid, pos, *args) 364 364 365 365 366 366 def _process_close_window(self, proto, packet): -
xpra/x11/gtk2/gdk_bindings.pyx
1047 1047 #log("calling %s%s", parser, (d, <uintptr_t> &e.xcookie)) 1048 1048 pyev = parser(d, <uintptr_t> &e.xcookie) 1049 1049 #log("pyev=%s (window=%#x)", pyev, e.xany.window) 1050 pyev.window = _gw(d, pyev.window) 1051 pyev.delivered_to = pyev.window 1050 if pyev and type(pyev.window)==int: 1051 pyev.window = _gw(d, pyev.window) 1052 pyev.delivered_to = pyev.window 1052 1053 return pyev 1053 1054 return None 1054 1055 -
xpra/x11/server.py
948 948 949 949 """ override so we can raise the window under the cursor 950 950 (gtk raise does not change window stacking, just focus) """ 951 def _move_pointer(self, wid, pos ):951 def _move_pointer(self, wid, pos, *args): 952 952 if wid>=0: 953 953 window = self._id_to_window.get(wid) 954 954 if not window: … … 956 956 else: 957 957 mouselog("raising %s", window) 958 958 window.raise_window() 959 X11ServerBase._move_pointer(self, wid, pos )959 X11ServerBase._move_pointer(self, wid, pos, *args) 960 960 961 961 962 962 def _process_close_window(self, proto, packet): -
xpra/x11/x11_server_base.py
43 43 grablog = Logger("server", "grab") 44 44 cursorlog = Logger("server", "cursor") 45 45 screenlog = Logger("server", "screen") 46 xinputlog = Logger("xinput") 46 47 gllog = Logger("screen", "opengl") 47 48 48 49 from xpra.util import iround, envbool, envint … … 74 75 log("found window: %s", window_info(window)) 75 76 76 77 78 class XTestPointerDevice(object): 79 80 def __repr__(self): 81 return "XTestPointerDevice" 82 83 def move_pointer(self, screen_no, x, y, *args): 84 mouselog("xtest_fake_motion(%i, %s, %s)", screen_no, x, y) 85 X11Keyboard.xtest_fake_motion(screen_no, x, y) 86 87 def click(self, button, pressed, *args): 88 mouselog("xtest_fake_button(%i, %s)", button, pressed) 89 X11Keyboard.xtest_fake_button(button, pressed) 90 91 def close(self): 92 pass 93 94 class EVDEVPointerDevice(object): 95 96 def __init__(self, name, device): 97 self.name = name 98 self.device = device 99 100 def __repr__(self): 101 return "EVDEVPointerDevice(%s)" % self.name 102 103 def move_pointer(self, screen_no, x, y, ex=None, ey=None, x_root=None, y_root=None, valuators=None, raw_valuators=None): 104 mouselog.warn("move_pointer(%i, %s, %s, ..) valuators=%s, raw_valuators=%s", screen_no, x, y, valuators, raw_valuators) 105 def fv(value): 106 return float(value[0])/float(value[1]) 107 def iv(value): 108 f = fv(value) 109 if f>0: 110 return max(1, int(f)) 111 else: 112 return min(-1, int(f)) 113 from evdev import ecodes 114 events = [] 115 if valuators: 116 x = valuators.get(0) 117 y = valuators.get(1) 118 if x: 119 events.append((ecodes.EV_ABS, ecodes.ABS_X, iv(x))) 120 if y: 121 events.append((ecodes.EV_ABS, ecodes.ABS_Y, iv(y))) 122 elif raw_valuators: 123 x = raw_valuators.get(0) 124 y = raw_valuators.get(1) 125 events = [] 126 if x: 127 events.append((ecodes.EV_REL, ecodes.REL_X, iv(x))) 128 if y: 129 events.append((ecodes.EV_REL, ecodes.REL_Y, iv(y))) 130 self.post_events(events) 131 132 def post_events(self, events): 133 for event in events: 134 self.device.write(*event) 135 self.device.syn() 136 137 def click(self, button, pressed, *args): 138 mouselog.warn("click(%i, %s, %s)", button, pressed, args) 139 #ui.write(e.EV_KEY, e.BTN_LEFT, 1) 140 from evdev import ecodes 141 evdev_button = { 142 1 : ecodes.BTN_LEFT, 143 2 : ecodes.BTN_RIGHT, 144 3 : ecodes.BTN_MIDDLE, 145 4 : ecodes.BTN_SIDE, 146 5 : ecodes.BTN_EXTRA, 147 6 : ecodes.BTN_FORWARD, 148 7 : ecodes.BTN_BACK, 149 8 : ecodes.BTN_TASK, 150 }.get(button, -1) 151 if evdev_button==-1: 152 log.error("Error: unknown button %i", button) 153 return 154 self.device.write(ecodes.EV_KEY, evdev_button, pressed) 155 156 def close(self): 157 d = self.device 158 if d: 159 self.device = None 160 d.close() 161 162 77 163 class X11ServerBase(GTKServerBase): 78 164 """ 79 165 Base class for X11 servers, … … 93 179 self.current_xinerama_config = None 94 180 self.x11_init() 95 181 GTKServerBase.init(self, opts) 182 self.pointer_devices = {} 183 self.keyboard_devices = {} 184 if self.input_devices=="xi": 185 try: 186 import evdev 187 self.evdev = evdev 188 except ImportError as e: 189 xinputlog.warn("Warning: cannot enable xi input devices via python-evdev:") 190 xinputlog.warn(" %s", e) 191 self.input_devices = "auto" 96 192 97 193 def x11_init(self): 98 194 if self.fake_xinerama: … … 618 714 def do_cleanup(self, *args): 619 715 GTKServerBase.do_cleanup(self) 620 716 cleanup_fakeXinerama() 717 self.cleanup_input_devices() 621 718 622 719 623 720 def _process_server_settings(self, proto, packet): … … 710 807 #-1 uses the current screen 711 808 return -1 712 809 713 def _move_pointer(self, wid, pos): 810 811 def cleanup_input_devices(self): 812 def close_devices(devices): 813 for device in devices: 814 try: 815 device.close() 816 except Exception as e: 817 xinputlog.error("Error closing input device %s:", device) 818 xinputlog.error(" %s", device) 819 pointer_devices = self.pointer_devices.values() 820 self.pointer_devices = {} 821 close_devices(pointer_devices) 822 keyboard_devices = self.keyboard_devices.values() 823 self.keyboard_devices = {} 824 close_devices(keyboard_devices) 825 826 827 def setup_input_devices(self): 828 xinputlog("setup_input_devices() input_devices=%s, evdev=%s", self.input_devices, self.evdev) 829 #default is always present: 830 self.pointer_devices[-1] = XTestPointerDevice() 831 if self.input_devices!="xi" or not self.evdev or not self.input_devices_data: 832 return 833 for deviceid, props in self.input_devices_data.items(): 834 if not props.get("enabled"): 835 continue 836 name = props.get("name") 837 use = props.get("use") 838 if use in ("master keyboard", "master pointer"): 839 #should not be needed, we want to use the slave devices 840 log("ignoring %s: %s", use, name) 841 continue 842 if name=="Virtual core XTEST keyboard": 843 #should always be present already - just needs mapping to it 844 #this should use the legacy XTEST code path 845 log("ignoring %s", name) 846 continue 847 if name=="Virtual core XTEST pointer": 848 self.pointer_devices[deviceid] = XTestPointerDevice() 849 continue 850 self.pointer_devices[-1] = XTestPointerDevice() 851 if use=="slave keyboard": 852 #not implemented yet! 853 log("TODO: handle slave keyboard %s", name) 854 continue 855 if use=="slave pointer": 856 self.setup_pointer_device(deviceid) 857 858 def setup_pointer_device(self, deviceid): 859 props = self.input_devices_data[deviceid] 860 xinputlog("setup_pointer_device(%s) properties=%s", deviceid, props) 861 assert props["use"] == "slave pointer" 862 name = props.get("name", "") 863 #is_touchpad = name.lower().find("touchpad")>=0 864 from evdev import UInput, AbsInfo, ecodes as e 865 #caps = {e.EV_KEY : (e.BTN_LEFT, e.BTN_RIGHT), e.EV_REL:(e.REL_X,e.REL_Y)} 866 #caps = {e.EV_KEY : (e.BTN_LEFT, e.BTN_RIGHT), e.EV_ABS:(e.ABS_X,e.ABS_Y)} 867 caps = { 868 e.EV_KEY : [e.BTN_LEFT, e.BTN_RIGHT], 869 e.EV_ABS : [ 870 (e.ABS_X, AbsInfo(value=100, min=0, max=3264, fuzz=0, flat=0, resolution=13)), 871 (e.ABS_Y, AbsInfo(100, 0, 1856, 0, 0, 13))] 872 } 873 device = UInput(caps, name="%s - xpra pointer" % name) 874 log.info("capabilities=%s", device.capabilities(verbose=True, absinfo=True)) 875 self.pointer_devices[deviceid] = EVDEVPointerDevice(name, device) 876 log.info("setup_pointer_device(%s) %s=%s", deviceid, name, device) 877 878 879 def _move_pointer(self, wid, pos, deviceid, *args): 880 #args = 714 881 #(this is called within an xswallow context) 715 882 screen_no = self.get_screen_number(wid) 716 mouselog("move_pointer(%s, %s) screen_no=%i", wid, pos, screen_no) 883 device = self.pointer_devices[deviceid] 884 mouselog.info("move_pointer(%s, %s, %s) screen_no=%i, device=%s", wid, pos, deviceid, screen_no, device) 717 885 x, y = pos 718 X11Keyboard.xtest_fake_motion(screen_no, x, y) 886 try: 887 device.move_pointer(screen_no, x, y, *args) 888 except Exception as e: 889 mouselog.error("Error: failed to move the pointer to %sx%s using %s", x, y, device) 890 mouselog.error(" %s", e) 719 891 720 def do_process_mouse_common(self, proto, wid, pointer): 892 def do_process_mouse_common(self, proto, wid, pointer, deviceid=-1, *args): 893 log.info("do_process_mouse_common%s", tuple([proto, wid, pointer, deviceid]+list(args))) 721 894 if self.readonly: 722 895 return 896 if self.input_devices_data: 897 device_data = self.input_devices_data.get(deviceid) 898 if device_data: 899 log.warn("process_mouse_common from device=%s", device_data.get("name")) 723 900 pos = self.root_window.get_pointer()[:2] 724 if pos!=pointer :901 if pos!=pointer or self.input_devices=="xi": 725 902 ss = self._server_sources.get(proto) 726 903 assert ss, "source not found for %s" % proto 727 904 self.last_mouse_user = ss.uuid 728 905 with xswallow: 729 self._move_pointer(wid, pointer )906 self._move_pointer(wid, pointer, deviceid, *args) 730 907 731 908 def _update_modifiers(self, proto, wid, modifiers): 732 909 if self.readonly: … … 739 916 if wid==self.get_focus(): 740 917 ss.user_event() 741 918 742 def do_process_button_action(self, proto, wid, button, pressed, pointer, modifiers, *args):919 def do_process_button_action(self, proto, wid, button, pressed, pointer, modifiers, buttons=[], deviceid=-1, *args): 743 920 self._update_modifiers(proto, wid, modifiers) 744 self._process_mouse_common(proto, wid, pointer) 745 mouselog("xtest_fake_button(%s, %s) at %s", button, pressed, pointer) 921 #TODO: pass extra args 922 self._process_mouse_common(proto, wid, pointer, deviceid) 923 device = self.pointer_devices.get(deviceid) 924 assert device, "pointer device %s not found" % deviceid 746 925 try: 747 926 with xsync: 748 X11Keyboard.xtest_fake_button(button, pressed)927 device.click(button, pressed, *args) 749 928 except XError: 750 929 err = "Failed to pass on (un)press of mouse button %s" % button 751 930 if button>=4: