Ticket #173: xi-events-v9.patch
File xi-events-v9.patch, 68.7 KB (added by , 4 years ago) |
---|
-
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
644 644 #overriden in GTKClientWindowBase 645 645 return self._id 646 646 647 def do_xi_motion(self, *args): 648 log.warn("do_xi_motion%s", args) 649 650 647 651 def do_motion_notify_event(self, event): 648 652 if self._client.readonly: 649 653 return … … 659 663 except: 660 664 return "" 661 665 662 def _button_action(self, button, event, depressed ):666 def _button_action(self, button, event, depressed, *args): 663 667 if self._client.readonly: 664 668 return 665 669 pointer, modifiers, buttons = self._pointer_modifiers(event) … … 680 684 b = sb 681 685 server_buttons.append(b) 682 686 def send_button(pressed): 683 self._client.send_button(wid, server_button, pressed, pointer, modifiers, server_buttons )687 self._client.send_button(wid, server_button, pressed, pointer, modifiers, server_buttons, *args) 684 688 pressed_state = self.button_state.get(button, False) 685 689 if SIMULATE_MOUSE_DOWN and pressed_state is False and depressed is False: 686 690 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
36 36 MOVERESIZE_SIZE_LEFT, MOVERESIZE_MOVE) 37 37 38 38 from xpra.gtk_common.gobject_compat import import_gtk, import_gdk, import_cairo, import_pixbufloader, get_xid 39 from xpra.gtk_common.gobject_util import no_arg_signal 39 from xpra.gtk_common.gobject_util import no_arg_signal, one_arg_signal 40 40 from xpra.gtk_common.gtk_util import (get_pixbuf_from_data, get_default_root_window, is_realized, 41 41 WINDOW_POPUP, WINDOW_TOPLEVEL, GRAB_STATUS_STRING, GRAB_SUCCESS, SCROLL_UP, SCROLL_DOWN, SCROLL_LEFT, SCROLL_RIGHT) 42 42 from xpra.gtk_common.keymap import KEY_TRANSLATIONS … … 145 145 146 146 __common_gsignals__ = { 147 147 "state-updated" : no_arg_signal, 148 "xi-motion" : one_arg_signal, 148 149 } 149 150 150 151 #maximum size of the actual window: … … 933 934 934 935 935 936 def do_motion_notify_event(self, event): 937 log.info("do_motion_notify_event") 936 938 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) 939 self.motion_moveresize(event) 1004 940 ClientWindowBase.do_motion_notify_event(self, event) 1005 941 942 def motion_moveresize(self, event): 943 x_root, y_root, direction, button, start_buttons, wx, wy, ww, wh = self.moveresize_event 944 dirstr = MOVERESIZE_DIRECTION_STRING.get(direction, direction) 945 buttons = self._event_buttons(event) 946 if start_buttons is None: 947 #first time around, store the buttons 948 start_buttons = buttons 949 self.moveresize_event[4] = buttons 950 if (button>0 and button not in buttons) or (button==0 and start_buttons!=buttons): 951 geomlog("%s for window button %i is no longer pressed (buttons=%s) cancelling moveresize", dirstr, button, buttons) 952 self.moveresize_event = None 953 else: 954 x = event.x_root 955 y = event.y_root 956 dx = x-x_root 957 dy = y-y_root 958 #clamp resizing using size hints, 959 #or sane defaults: minimum of (1x1) and maximum of (2*15x2*25) 960 minw = self.geometry_hints.get("min_width", 1) 961 minh = self.geometry_hints.get("min_height", 1) 962 maxw = self.geometry_hints.get("max_width", 2**15) 963 maxh = self.geometry_hints.get("max_height", 2**15) 964 geomlog("%s: min=%ix%i, max=%ix%i, window=%ix%i, delta=%ix%i", dirstr, minw, minh, maxw, maxh, ww, wh, dx, dy) 965 if direction in (MOVERESIZE_SIZE_BOTTOMRIGHT, MOVERESIZE_SIZE_BOTTOM, MOVERESIZE_SIZE_BOTTOMLEFT): 966 #height will be set to: wh+dy 967 dy = max(minh-wh, dy) 968 dy = min(maxh-wh, dy) 969 elif direction in (MOVERESIZE_SIZE_TOPRIGHT, MOVERESIZE_SIZE_TOP, MOVERESIZE_SIZE_TOPLEFT): 970 #height will be set to: wh-dy 971 dy = min(wh-minh, dy) 972 dy = max(wh-maxh, dy) 973 if direction in (MOVERESIZE_SIZE_BOTTOMRIGHT, MOVERESIZE_SIZE_RIGHT, MOVERESIZE_SIZE_TOPRIGHT): 974 #width will be set to: ww+dx 975 dx = max(minw-ww, dx) 976 dx = min(maxw-ww, dx) 977 elif direction in (MOVERESIZE_SIZE_BOTTOMLEFT, MOVERESIZE_SIZE_LEFT, MOVERESIZE_SIZE_TOPLEFT): 978 #width will be set to: ww-dx 979 dx = min(ww-minw, dx) 980 dx = max(ww-maxw, dx) 981 #calculate move + resize: 982 if direction==MOVERESIZE_MOVE: 983 data = (wx+dx, wy+dy), None 984 elif direction==MOVERESIZE_SIZE_BOTTOMRIGHT: 985 data = None, (ww+dx, wh+dy) 986 elif direction==MOVERESIZE_SIZE_BOTTOM: 987 data = None, (ww, wh+dy) 988 elif direction==MOVERESIZE_SIZE_BOTTOMLEFT: 989 data = (wx+dx, wy), (ww-dx, wh+dy) 990 elif direction==MOVERESIZE_SIZE_RIGHT: 991 data = None, (ww+dx, wh) 992 elif direction==MOVERESIZE_SIZE_LEFT: 993 data = (wx+dx, wy), (ww-dx, wh) 994 elif direction==MOVERESIZE_SIZE_TOPRIGHT: 995 data = (wx, wy+dy), (ww+dx, wh-dy) 996 elif direction==MOVERESIZE_SIZE_TOP: 997 data = (wx, wy+dy), (ww, wh-dy) 998 elif direction==MOVERESIZE_SIZE_TOPLEFT: 999 data = (wx+dx, wy+dy), (ww-dx, wh-dy) 1000 else: 1001 #not handled yet! 1002 data = None 1003 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) 1004 if data: 1005 #modifying the window is slower than moving the pointer, 1006 #do it via a timer to batch things together 1007 self.moveresize_data = data 1008 if self.moveresize_timer is None: 1009 self.moveresize_timer = self.timeout_add(20, self.do_moveresize) 1010 1006 1011 def do_moveresize(self): 1007 1012 self.moveresize_timer = None 1008 1013 mrd = self.moveresize_data … … 1323 1328 def _pointer_modifiers(self, event): 1324 1329 x, y = self._get_pointer(event) 1325 1330 pointer = self._pointer(x, y) 1331 #FIXME: state is used for both mods and buttons?? 1326 1332 modifiers = self._client.mask_to_names(event.state) 1327 1333 buttons = self._event_buttons(event) 1328 1334 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, 621 event.device] + self.get_pointer_extra_args(event) 622 mouselog.info("motion packet: %s", packet) 623 window._client.send_mouse_position(packet) 624 625 def get_pointer_extra_args(self, event): 626 args = [] 627 def intscaled(f): 628 return int(f*1000000), 1000000 629 def dictscaled(v): 630 return dict((k,intscaled(v)) for k,v in v.items()) 631 raw_valuators = {} 632 raw_event_name = event.name.replace("XI_", "XI_Raw") #ie: XI_Motion -> XI_RawMotion 633 raw = self.XI2.find_event(raw_event_name, event.serial) 634 #log.info("raw(%s)=%s", raw_event_name, raw) 635 if raw: 636 raw_valuators = raw.raw_valuators 637 for x in ("x", "y", "x_root", "y_root"): 638 args.append(intscaled(getattr(event, x))) 639 for v in [event.valuators, raw_valuators]: 640 args.append(dictscaled(v)) 641 return args 642 643 519 644 class ClientExtras(object): 520 645 def __init__(self, client, opts): 521 646 self.client = client … … 528 653 self.x11_filter = None 529 654 if client.xsettings_enabled: 530 655 self.setup_xprops() 656 if client.input_devices=="xi": 657 self.setup_xi() 531 658 self.setup_dbus_signals() 532 659 533 660 def ready(self): … … 565 692 bus._clean_up_signal_match(self.upower_sleeping_match) 566 693 if self.login1_match: 567 694 bus._clean_up_signal_match(self.login1_match) 695 global WINDOW_METHOD_OVERRIDES 696 WINDOW_METHOD_OVERRIDES = {} 568 697 569 698 def resuming_callback(self, *args): 570 699 eventlog("resuming_callback%s", args) … … 647 776 except ImportError as e: 648 777 log.error("failed to load X11 properties/settings bindings: %s - root window properties will not be propagated", e) 649 778 779 def setup_xi(self): 780 from xpra.gtk_common.error import xsync 781 def may_enable_xi2(): 782 if self.client.server_input_devices!="xi": 783 log.warn("Warning: server does not support xi input devices") 784 return 785 try: 786 with xsync: 787 assert X11WindowBindings, "no X11 window bindings" 788 assert X11XI2Bindings, "no XI2 window bindings" 789 X11XI2Bindings().gdk_inject() 790 self.init_x11_filter() 791 XI2 = X11XI2Bindings() 792 XI2.select_xi2_events() 793 devices = XI2.get_devices() 794 if devices: 795 self.client.send_input_devices(devices) 796 except Exception as e: 797 log("enable_xi2()", exc_info=True) 798 log.error("Error: cannot enable XI2 events") 799 log.error(" %s", e) 800 else: 801 #register our enhanced event handlers: 802 self.add_xi2_method_overrides() 803 with xsync: 804 try: 805 #this would trigger warnings with our temporary opengl windows: 806 #only enable it after we have connected: 807 self.client.after_handshake(may_enable_xi2) 808 except Exception as e: 809 log("setup_xi()", exc_info=True) 810 log.error("Error: failed to load the XI2 bindings") 811 log.error(" %s", e) 812 813 def add_xi2_method_overrides(self): 814 global WINDOW_ADD_HOOKS 815 WINDOW_ADD_HOOKS = [XI2_Window] 816 817 650 818 def _get_xsettings(self): 651 819 try: 652 820 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/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())) … … 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, device=-1, *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, device, *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, device=-1, *args): 2887 2894 pass 2888 2895 2889 2896 … … 2909 2916 if self.readonly: 2910 2917 return 2911 2918 wid, pointer, modifiers = packet[1:4] 2919 device = -1 2920 if len(packet)>=6: 2921 #buttons = packet[4] 2922 device = packet[5] 2923 if self.input_devices_data: 2924 device_data = self.input_devices_data.get(device) 2925 if device_data: 2926 log.warn("pointer position from device=%s - %s", device_data.get("name"), device_data) 2927 #log.warn("pointer position with device=%s", device_data.get("name")) 2912 2928 ss = self._server_sources.get(proto) 2913 2929 if ss is not None: 2914 2930 ss.mouse_last_position = pointer … … 2915 2931 if self.ui_driver and self.ui_driver!=ss.uuid: 2916 2932 return 2917 2933 self._update_modifiers(proto, wid, modifiers) 2918 self._process_mouse_common(proto, wid, pointer )2934 self._process_mouse_common(proto, wid, pointer, device) 2919 2935 2920 2936 2921 2937 def _process_damage_sequence(self, proto, packet): … … 3176 3192 ss.send_webcam_stop(device, str(e)) 3177 3193 self.stop_virtual_webcam() 3178 3194 3195 def _process_input_devices(self, ss, packet): 3196 self.input_devices_data = packet[1] 3197 from xpra.util import print_nested_dict 3198 log.info("client input devices:") 3199 print_nested_dict(self.input_devices_data, print_fn=log.info) 3179 3200 3180 3201 def process_clipboard_packet(self, ss, packet): 3181 3202 if self.readonly: -
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/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/x11_server_base.py
717 717 x, y = pos 718 718 X11Keyboard.xtest_fake_motion(screen_no, x, y) 719 719 720 def do_process_mouse_common(self, proto, wid, pointer ):720 def do_process_mouse_common(self, proto, wid, pointer, device, *args): 721 721 if self.readonly: 722 722 return 723 723 pos = self.root_window.get_pointer()[:2] … … 741 741 742 742 def do_process_button_action(self, proto, wid, button, pressed, pointer, modifiers, *args): 743 743 self._update_modifiers(proto, wid, modifiers) 744 #TODO: pass extra args 744 745 self._process_mouse_common(proto, wid, pointer) 745 746 mouselog("xtest_fake_button(%s, %s) at %s", button, pressed, pointer) 746 747 try: