Ticket #173: xi-events-v5.patch
File xi-events-v5.patch, 49.2 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_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 -
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: … … 934 935 935 936 def do_motion_notify_event(self, 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 -
xpra/client/ui_client_base.py
424 424 self.client_supports_sharing = opts.sharing 425 425 self.log_both = (opts.remote_logging or "").lower()=="both" 426 426 self.client_supports_remote_logging = self.log_both or parse_bool("remote-logging", opts.remote_logging) 427 self.input_devices = opts.input_devices 427 428 #mouse wheel: 428 429 mw = (opts.mousewheel or "").lower().replace("-", "") 429 430 if mw not in FALSE_OPTIONS: -
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("no ") 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_ENHANCERS = [] 511 def add_window_hooks(window): 512 global WINDOW_ENHANCERS 513 for x in WINDOW_ENHANCERS: 514 x(window) 515 log.warn("add_window_hooks(%s) added %s", window, WINDOW_ENHANCERS) 516 517 502 518 def get_info(): 503 519 from xpra.platform.gui import get_info_base 504 520 i = get_info_base() … … 516 532 return i 517 533 518 534 535 class XI2_Window(object): 536 def __init__(self, window): 537 log.warn("XI2_Window(%s)", window) 538 self.window = window 539 self.do_motion_notify_event = window.do_motion_notify_event 540 window.do_motion_notify_event = self._do_motion_notify_event 541 542 def get_parent_windows(self, oxid): 543 windows = [oxid] 544 X11Window = X11WindowBindings() 545 root = X11Window.getDefaultRootWindow() 546 xid = oxid 547 while True: 548 xid = X11Window.getParent(xid) 549 if xid==0 or xid==root: 550 break 551 windows.append(xid) 552 log("get_parent_windows(%#x)=%s", oxid, csv(hex(x) for x in windows)) 553 return windows 554 555 def _do_motion_notify_event(self, event): 556 if self.window.moveresize_event: 557 self.window.motion_moveresize(event) 558 self.do_motion_notify_event(event) 559 return 560 if self.window._client.readonly: 561 return 562 log("do_motion_notify_event(%s)", event) 563 #TODO: cache hierarchy and query again on structure notify: 564 parents = self.get_parent_windows(self.window.get_window().xid) 565 #find the motion events in the xi2 event list: 566 XI2 = X11XI2Bindings() 567 events = XI2.find_events("XI_Motion", parents) 568 log.warn("events=%s", events) 569 XI2.reset_events() 570 if not events: 571 self.do_motion_notify_event(event) 572 return 573 def intscaled(f): 574 return int(f*1000000), 1000000 575 oevent = event 576 for event in events: 577 #TODO: populate state from XI bindings 578 event.state = oevent.state 579 pointer, modifiers, buttons = self.window._pointer_modifiers(event) 580 wid = self.window.get_mouse_event_wid(*pointer) 581 mouselog("do_motion_notify_event(%s) wid=%s / focus=%s / window wid=%i, device=%s, pointer=%s, modifiers=%s, buttons=%s", event, wid, self.window._client._focused, self.window._id, event.device, pointer, modifiers, buttons) 582 packet = ["pointer-position", wid, pointer, modifiers, buttons, 583 event.device] 584 for x in ("x", "y", "x_root", "y_root"): 585 v = intscaled(getattr(event, x)) 586 packet.append(v) 587 self.window._client.send_mouse_position(packet) 588 589 519 590 class ClientExtras(object): 520 591 def __init__(self, client, opts): 521 592 self.client = client … … 528 599 self.x11_filter = None 529 600 if client.xsettings_enabled: 530 601 self.setup_xprops() 602 if client.input_devices=="xi": 603 self.setup_xi() 531 604 self.setup_dbus_signals() 532 605 533 606 def ready(self): … … 565 638 bus._clean_up_signal_match(self.upower_sleeping_match) 566 639 if self.login1_match: 567 640 bus._clean_up_signal_match(self.login1_match) 641 global WINDOW_METHOD_OVERRIDES 642 WINDOW_METHOD_OVERRIDES = {} 568 643 569 644 def resuming_callback(self, *args): 570 645 eventlog("resuming_callback%s", args) … … 647 722 except ImportError as e: 648 723 log.error("failed to load X11 properties/settings bindings: %s - root window properties will not be propagated", e) 649 724 725 def setup_xi(self): 726 from xpra.gtk_common.error import xsync 727 def enable_xi2(): 728 try: 729 with xsync: 730 self.init_x11_filter() 731 XI2 = X11XI2Bindings() 732 XI2.select_xi2_events() 733 except Exception as e: 734 log("enable_xi2()", exc_info=True) 735 log.error("Error: cannot enable XI2 events") 736 log.error(" %s", e) 737 else: 738 #register our enhanced event handlers: 739 self.add_xi2_method_overrides() 740 with xsync: 741 try: 742 assert X11WindowBindings, "no X11 window bindings" 743 assert X11XI2Bindings, "no XI2 window bindings" 744 X11XI2Bindings().gdk_inject() 745 #this would trigger warnings with our temporary opengl windows: 746 #only enable it after we have connected: 747 self.client.after_handshake(enable_xi2) 748 except Exception as e: 749 log("setup_xi()", exc_info=True) 750 log.error("Error: failed to load the XI2 bindings") 751 log.error(" %s", e) 752 753 def add_xi2_method_overrides(self): 754 global WINDOW_ENHANCERS 755 WINDOW_ENHANCERS = [XI2_Window] 756 757 650 758 def _get_xsettings(self): 651 759 try: 652 760 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
2878 2878 return px+(wx-cx), py+(wy-cy) 2879 2879 return pointer 2880 2880 2881 def _process_mouse_common(self, proto, wid, pointer ):2881 def _process_mouse_common(self, proto, wid, pointer, device=-1, *args): 2882 2882 pointer = self._adjust_pointer(proto, wid, pointer) 2883 self.do_process_mouse_common(proto, wid, pointer) 2883 #TODO: adjust args too 2884 self.do_process_mouse_common(proto, wid, pointer, device, *args) 2884 2885 return pointer 2885 2886 2886 def do_process_mouse_common(self, proto, wid, pointer ):2887 def do_process_mouse_common(self, proto, wid, pointer, device=-1, *args): 2887 2888 pass 2888 2889 2889 2890 … … 2909 2910 if self.readonly: 2910 2911 return 2911 2912 wid, pointer, modifiers = packet[1:4] 2913 device = -1 2914 if len(packet)>=6: 2915 #buttons = packet[4] 2916 device = packet[5] 2917 log.warn("pointer position with device=%s", device) 2912 2918 ss = self._server_sources.get(proto) 2913 2919 if ss is not None: 2914 2920 ss.mouse_last_position = pointer … … 2915 2921 if self.ui_driver and self.ui_driver!=ss.uuid: 2916 2922 return 2917 2923 self._update_modifiers(proto, wid, modifiers) 2918 self._process_mouse_common(proto, wid, pointer )2924 self._process_mouse_common(proto, wid, pointer, device) 2919 2925 2920 2926 2921 2927 def _process_damage_sequence(self, proto, packet): -
xpra/x11/bindings/core_bindings.pyx
33 33 ctypedef int Bool 34 34 35 35 Atom XInternAtom(Display * display, char * atom_name, Bool only_if_exists) 36 char *XGetAtomName(Display *display, Atom atom) 36 37 37 38 int XFree(void * data) 38 39 … … 83 84 def get_xatom(self, str_or_int): 84 85 return self.xatom(str_or_int) 85 86 87 def XGetAtomName(self, Atom atom): 88 v = XGetAtomName(self.display, atom) 89 return v[:] 90 91 86 92 def get_error_text(self, code): 87 93 assert self.display!=NULL, "display is closed" 88 94 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 … … 461 460 return XDefaultRootWindow(self.display) 462 461 463 462 464 cpdef XGetAtomName(self, Atom atom):465 v = XGetAtomName(self.display, atom)466 return v[:]467 468 463 def MapWindow(self, Window xwindow): 469 464 XMapWindow(self.display, xwindow) 470 465 -
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 collections 9 10 from xpra.log import Logger 11 log = Logger("x11", "bindings", "xinput") 12 13 from xpra.x11.gtk2.common import X11Event 14 15 from libc.stdint cimport uintptr_t 16 17 18 ################################### 19 # Headers, python magic 20 ################################### 21 cdef extern from "string.h": 22 void* memset(void * ptr, int value, size_t num) 23 24 cdef extern from "X11/Xutil.h": 25 pass 26 27 ###### 28 # Xlib primitives and constants 29 ###### 30 31 include "constants.pxi" 32 ctypedef unsigned long CARD32 33 34 cdef extern from "X11/Xlib.h": 35 ctypedef struct Display: 36 pass 37 38 ctypedef CARD32 XID 39 ctypedef int Bool 40 ctypedef int Status 41 ctypedef CARD32 Atom 42 ctypedef XID Window 43 ctypedef CARD32 Time 44 45 ctypedef struct XGenericEventCookie: 46 int type # of event. Always GenericEvent 47 unsigned long serial 48 Bool send_event 49 Display *display 50 int extension #major opcode of extension that caused the event 51 int evtype #actual event type 52 unsigned int cookie 53 void *data 54 55 Atom XInternAtom(Display * display, char * atom_name, Bool only_if_exists) 56 int XFree(void * data) 57 58 Bool XQueryExtension(Display * display, char *name, 59 int *major_opcode_return, int *first_event_return, int *first_error_return) 60 61 Bool XGetEventData(Display *display, XGenericEventCookie *cookie) 62 void XFreeEventData(Display *display, XGenericEventCookie *cookie) 63 64 Window XDefaultRootWindow(Display * display) 65 66 Bool XQueryPointer(Display *display, Window w, Window *root_return, Window *child_return, int *root_x_return, int *root_y_return, 67 int *win_x_return, int *win_y_return, unsigned int *mask_return) 68 int XFlush(Display *dpy) 69 70 cdef extern from "X11/extensions/XInput2.h": 71 int XI_LASTEVENT 72 int XI_DeviceChanged 73 int XI_KeyPress 74 int XI_KeyRelease 75 int XI_ButtonPress 76 int XI_ButtonRelease 77 int XI_Motion 78 int XI_Enter 79 int XI_Leave 80 int XI_FocusIn 81 int XI_FocusOut 82 int XI_HierarchyChanged 83 int XI_PropertyEvent 84 int XI_RawKeyPress 85 int XI_RawKeyRelease 86 int XI_RawButtonPress 87 int XI_RawButtonRelease 88 int XI_RawMotion 89 int XI_TouchBegin 90 int XI_TouchUpdate 91 int XI_TouchEnd 92 int XI_TouchOwnership 93 int XI_RawTouchBegin 94 int XI_RawTouchUpdate 95 int XI_RawTouchEnd 96 97 int XIMasterPointer 98 int XIMasterKeyboard 99 int XISlavePointer 100 int XISlaveKeyboard 101 int XIFloatingSlave 102 103 int XIButtonClass 104 int XIKeyClass 105 int XIValuatorClass 106 int XIScrollClass 107 int XITouchClass 108 109 int XIAllDevices 110 int XIAllMasterDevices 111 112 ctypedef struct XIValuatorState: 113 int mask_len 114 unsigned char *mask 115 double *values 116 117 ctypedef struct XIEvent: 118 int type 119 unsigned long serial 120 Bool send_event 121 Display *display 122 int extension 123 int evtype 124 Time time 125 126 ctypedef struct XIRawEvent: 127 int type #GenericEvent 128 unsigned long serial 129 Bool send_event 130 Display *display 131 int extension #XI extension offset 132 int evtype #XI_RawKeyPress, XI_RawKeyRelease, etc 133 Time time 134 int deviceid 135 int sourceid 136 int detail 137 int flags 138 XIValuatorState valuators 139 double *raw_values 140 141 ctypedef struct XIButtonState: 142 int mask_len 143 unsigned char *mask 144 145 ctypedef struct XIModifierState: 146 int base 147 int latched 148 int locked 149 int effective 150 151 ctypedef XIModifierState XIGroupState 152 153 ctypedef struct XIDeviceEvent: 154 int type 155 unsigned long serial 156 Bool send_event 157 Display *display 158 int extension 159 int evtype 160 Time time 161 int deviceid 162 int sourceid 163 int detail 164 Window root 165 Window event 166 Window child 167 double root_x 168 double root_y 169 double event_x 170 double event_y 171 int flags 172 XIButtonState buttons 173 XIValuatorState valuators 174 XIModifierState mods 175 XIGroupState group 176 177 ctypedef struct XIHierarchyInfo: 178 int deviceid 179 int attachment 180 int use 181 Bool enabled 182 int flags 183 184 ctypedef struct XIHierarchyEvent: 185 int type 186 unsigned long serial 187 Bool send_event 188 Display *display 189 int extension 190 int evtype #XI_HierarchyChanged 191 Time time 192 int flags 193 int num_info 194 XIHierarchyInfo *info 195 196 ctypedef struct XIEventMask: 197 int deviceid 198 int mask_len 199 unsigned char* mask 200 201 ctypedef struct XIAnyClassInfo: 202 int type 203 int sourceid 204 205 ctypedef struct XIDeviceInfo: 206 int deviceid 207 char *name 208 int use 209 int attachment 210 Bool enabled 211 int num_classes 212 XIAnyClassInfo **classes 213 214 ctypedef struct XIButtonClassInfo: 215 int type 216 int sourceid 217 int num_buttons 218 Atom *labels 219 XIButtonState state 220 221 ctypedef struct XIKeyClassInfo: 222 int type 223 int sourceid 224 int num_keycodes 225 int *keycodes 226 227 ctypedef struct XIValuatorClassInfo: 228 int type 229 int sourceid 230 int number 231 Atom label 232 double min 233 double max 234 double value 235 int resolution 236 int mode 237 238 ctypedef struct XIScrollClassInfo: 239 int type 240 int sourceid 241 int number 242 int scroll_type 243 double increment 244 int flags 245 246 ctypedef struct XITouchClassInfo: 247 int type 248 int sourceid 249 int mode 250 int num_touches 251 252 Status XIQueryVersion(Display *display, int *major_version_inout, int *minor_version_inout) 253 Status XISelectEvents(Display *display, Window win, XIEventMask *masks, int num_masks) 254 XIDeviceInfo* XIQueryDevice(Display *display, int deviceid, int *ndevices_return) 255 void XIFreeDeviceInfo(XIDeviceInfo *info) 256 Atom *XIListProperties(Display *display, int deviceid, int *num_props_return) 257 Status XIGetProperty(Display *display, int deviceid, Atom property, long offset, long length, 258 Bool delete_property, Atom type, Atom *type_return, 259 int *format_return, unsigned long *num_items_return, 260 unsigned long *bytes_after_return, unsigned char **data) 261 262 263 DEF MAX_XI_EVENTS = 64 264 DEF XI_EVENT_MASK_SIZE = (MAX_XI_EVENTS+7)//8 265 266 XI_EVENT_NAMES = { 267 XI_DeviceChanged : "XI_DeviceChanged", 268 XI_KeyPress : "XI_KeyPress", 269 XI_KeyRelease : "XI_KeyRelease", 270 XI_ButtonPress : "XI_ButtonPress", 271 XI_ButtonRelease : "XI_ButtonRelease", 272 XI_Motion : "XI_Motion", 273 XI_Enter : "XI_Enter", 274 XI_Leave : "XI_Leave", 275 XI_FocusIn : "XI_FocusIn", 276 XI_FocusOut : "XI_FocusOut", 277 XI_HierarchyChanged : "XI_HierarchyChanged", 278 XI_PropertyEvent : "XI_PropertyEvent", 279 XI_RawKeyPress : "XI_RawKeyPress", 280 XI_RawKeyRelease : "XI_RawKeyRelease", 281 XI_RawButtonPress : "XI_RawButtonPress", 282 XI_RawButtonRelease : "XI_RawButtonRelease", 283 XI_RawMotion : "XI_RawMotion", 284 XI_TouchBegin : "XI_TouchBegin", 285 XI_TouchUpdate : "XI_TouchUpdate", 286 XI_TouchEnd : "XI_TouchEnd", 287 XI_TouchOwnership : "XI_TouchOwnership", 288 XI_RawTouchBegin : "XI_RawTouchBegin", 289 XI_RawTouchUpdate : "XI_RawTouchUpdate", 290 XI_RawTouchEnd : "XI_RawTouchEnd", 291 } 292 293 XI_USE = { 294 XIMasterPointer : "master pointer", 295 XIMasterKeyboard : "master keyboard", 296 XISlavePointer : "slave pointer", 297 XISlaveKeyboard : "slave keyboard", 298 XIFloatingSlave : "floating slave", 299 } 300 301 CLASS_INFO = { 302 XIButtonClass : "button", 303 XIKeyClass : "key", 304 XIValuatorClass : "valuator", 305 XIScrollClass : "scroll", 306 XITouchClass : "touch", 307 } 308 309 310 from core_bindings cimport _X11CoreBindings 311 312 cdef _X11XI2Bindings singleton = None 313 def X11XI2Bindings(): 314 global singleton 315 if singleton is None: 316 singleton = _X11XI2Bindings() 317 return singleton 318 319 cdef class _X11XI2Bindings(_X11CoreBindings): 320 321 cdef int opcode 322 cdef object events 323 324 def __init__(self): 325 self.opcode = -1 326 self.reset_events() 327 328 def __repr__(self): 329 return "X11XI2Bindings(%s)" % self.display_name 330 331 def reset_events(self): 332 self.events = collections.deque(maxlen=100) 333 334 def find_events(self, event_name, windows): 335 cdef Window found = 0 336 cdef Window window 337 matches = [] 338 for x in reversed(self.events): 339 window = x.xid 340 if x.name==event_name and ((found>0 and found==window) or (found==0 and window in windows)): 341 matches.append(x) 342 found = window 343 elif found: 344 break 345 return matches 346 347 cdef int get_xi_opcode(self, int major=2, int minor=2): 348 if self.opcode!=-1: 349 return self.opcode 350 cdef int opcode, event, error 351 if not XQueryExtension(self.display, "XInputExtension", &opcode, &event, &error): 352 log.warn("Warning: XI2 events are not supported") 353 self.opcode = 0 354 return 0 355 cdef int rmajor = major, rminor = minor 356 cdef int rc = XIQueryVersion(self.display, &rmajor, &rminor) 357 if rc == BadRequest: 358 log.warn("Warning: no XI2 %i.%i support,", major, minor) 359 log.warn(" server supports version %i.%i only", rmajor, rminor) 360 self.opcode = 0 361 return 0 362 elif rc: 363 log.warn("Warning: Xlib bug querying XI2, code %i", rc) 364 self.opcode = 0 365 return 0 366 self.opcode = opcode 367 log("get_xi_opcode%s=%i", (major, minor), opcode) 368 return opcode 369 370 cdef register_parser(self): 371 log("register_parser()") 372 if self.opcode>0: 373 from xpra.x11.gtk2.gdk_bindings import add_x_event_parser 374 add_x_event_parser(self.opcode, self.parse_xi_event) 375 376 cdef register_gdk_events(self): 377 log("register_gdk_events()") 378 if self.opcode<=0: 379 return 380 global XI_EVENT_NAMES 381 from xpra.x11.gtk2.gdk_bindings import add_x_event_signal, add_x_event_type_name 382 for e, xi_event_name in { 383 XI_DeviceChanged : "device-changed", 384 XI_KeyPress : "key-press", 385 XI_KeyRelease : "key-release", 386 XI_ButtonPress : "button-press", 387 XI_ButtonRelease : "button-release", 388 XI_Motion : "motion", 389 XI_Enter : "enter", 390 XI_Leave : "leave", 391 XI_FocusIn : "focus-in", 392 XI_FocusOut : "focus-out", 393 XI_HierarchyChanged : "focus-changed", 394 XI_PropertyEvent : "property-event", 395 XI_RawKeyPress : "raw-key-press", 396 XI_RawKeyRelease : "raw-key-release", 397 XI_RawButtonPress : "raw-button-press", 398 XI_RawButtonRelease : "raw-button-release", 399 XI_RawMotion : "raw-motion", 400 XI_TouchBegin : "touch-begin", 401 XI_TouchUpdate : "touch-update", 402 XI_TouchEnd : "touch-end", 403 XI_TouchOwnership : "touch-ownership", 404 XI_RawTouchBegin : "raw-touch-begin", 405 XI_RawTouchUpdate : "raw-touch-update", 406 XI_RawTouchEnd : "raw-touch-end", 407 }.items(): 408 event = self.opcode+e 409 add_x_event_signal(event, ("xi-%s" % xi_event_name, None)) 410 name = XI_EVENT_NAMES[e] 411 add_x_event_type_name(event, name) 412 413 def select_xi2_events(self): 414 cdef Window win = XDefaultRootWindow(self.display) 415 log("select_xi2_events() root window=%#x", win) 416 assert XI_LASTEVENT<MAX_XI_EVENTS, "bug: source needs to be updated, XI_LASTEVENT=%i" % XI_LASTEVENT 417 cdef XIEventMask evmasks[1] 418 cdef unsigned char mask1[XI_EVENT_MASK_SIZE] 419 memset(mask1, 0, XI_EVENT_MASK_SIZE) 420 #define XISetMask(ptr, event) (((unsigned char*)(ptr))[(event)>>3] |= (1 << ((event) & 7))) 421 #XISetMask(mask1, XI_RawMotion) 422 for e in ( 423 XI_KeyPress, XI_KeyRelease, 424 XI_Motion, 425 XI_HierarchyChanged, 426 XI_ButtonPress, XI_ButtonRelease, 427 XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd, 428 ): 429 mask1[e>>3] |= (1<< (e & 0x7)) 430 evmasks[0].deviceid = XIAllDevices #XIAllMasterDevices #XIAllDevices 431 evmasks[0].mask_len = XI_EVENT_MASK_SIZE 432 evmasks[0].mask = mask1 433 XISelectEvents(self.display, win, evmasks, 1) 434 XFlush(self.display) 435 436 def parse_xi_event(self, display, uintptr_t _cookie): 437 log("parse_xi_event(%s)", _cookie) 438 cdef XGenericEventCookie *cookie = <XGenericEventCookie*> _cookie 439 cdef XIDeviceEvent *device_e 440 cdef XIHierarchyEvent * hierarchy_e 441 cdef XIEvent *xie 442 cdef XIRawEvent *raw 443 cdef int i = 0, j = 0 444 if not XGetEventData(self.display, cookie): 445 return None 446 xie = <XIEvent*> cookie.data 447 device_e = <XIDeviceEvent*> cookie.data 448 cdef int xi_type = cookie.evtype 449 etype = self.opcode+xi_type 450 global XI_EVENT_NAMES 451 event_name = XI_EVENT_NAMES.get(xi_type) 452 if not event_name: 453 log("unknown XI2 event code: %i", xi_type) 454 return None 455 456 pyev = X11Event(event_name) 457 pyev.type = etype 458 pyev.display = display 459 pyev.send_event = bool(xie.send_event) 460 pyev.serial = xie.serial 461 pyev.time = int(xie.time) 462 pyev.window = XDefaultRootWindow(self.display) 463 464 if xi_type in (XI_KeyPress, XI_KeyRelease, 465 XI_ButtonPress, XI_ButtonRelease, 466 XI_Motion, 467 XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd): 468 device = <XIDeviceEvent*> cookie.data 469 #pyev.source = device.sourceid #always 0 470 pyev.device = device.deviceid 471 pyev.detail = device.detail 472 pyev.flags = device.flags 473 pyev.window = int(device.child or device.event or device.root) 474 pyev.x_root = device.root_x 475 pyev.y_root = device.root_y 476 pyev.x = device.event_x 477 pyev.y = device.event_y 478 #mask = [] 479 #values = [] 480 #raw_values = [] 481 #if False: 482 # for i in range(device.valuators.mask_len): 483 # if device.valuators.mask[i//8] & (1 << (i & 0x7)): 484 # mask.append(i) 485 # values.append(j) 486 # raw_values.append(device.valuators.values[j]) 487 # j += 1 488 #pyev.valuators = { 489 # "mask" : mask, 490 # "values" : values, 491 # } 492 elif xi_type == XI_HierarchyChanged: 493 pass 494 #hierarchy_e = <XIHierarchyEvent*> cookie.data 495 #pyev.flags = hierarchy_e.flags 496 #info = {} 497 #for i in range(hierarchy_e.num_info): 498 # 499 XFreeEventData(self.display, cookie) 500 pyev.xid = pyev.window 501 self.events.append(pyev) 502 log("parse_xi_event: %s", pyev) 503 return pyev 504 505 def get_devices(self, show_all=True, show_disabled=False): 506 global XI_USE 507 cdef int ndevices, i, j 508 cdef XIDeviceInfo *devices 509 cdef XIDeviceInfo *device 510 cdef XIAnyClassInfo *clazz 511 if show_all: 512 device_types = XIAllDevices 513 else: 514 device_types = XIAllMasterDevices 515 devices = XIQueryDevice(self.display, device_types, &ndevices) 516 dinfo = {} 517 for i in range(ndevices): 518 device = &devices[i] 519 if not device.enabled and not show_disabled: 520 continue 521 info = { 522 "name" : device.name, 523 "use" : XI_USE.get(device.use, "unknown use: %i" % device.use), 524 "attachment" : device.attachment, 525 "enabled" : device.enabled, 526 } 527 classes = {} 528 for j in range(device.num_classes): 529 clazz = device.classes[j] 530 classes[j] = self.get_class_info(clazz) 531 info["classes"] = classes 532 dinfo[device.deviceid] = info 533 XIFreeDeviceInfo(devices) 534 return dinfo 535 536 cdef get_class_info(self, XIAnyClassInfo *class_info): 537 cdef int i 538 cdef XIButtonClassInfo *button 539 cdef XIKeyClassInfo *key 540 cdef XIValuatorClassInfo *valuator 541 cdef XIScrollClassInfo *scroll 542 cdef XITouchClassInfo *touch 543 info = { 544 "type" : CLASS_INFO.get(class_info.type, "unknown type: %i" % class_info.type), 545 "sourceid" : class_info.sourceid, 546 } 547 if class_info.type==XIButtonClass: 548 button = <XIButtonClassInfo*> class_info 549 buttons = [] 550 for i in range(button.num_buttons): 551 if button.labels[i]>0: 552 buttons.append(self.XGetAtomName(button.labels[i])) 553 info["buttons"] = buttons 554 #XIButtonState state 555 elif class_info.type==XIKeyClass: 556 key = <XIKeyClassInfo*> class_info 557 keys = [] 558 for i in range(key.num_keycodes): 559 keys.append(key.keycodes[i]) 560 elif class_info.type==XIValuatorClass: 561 valuator = <XIValuatorClassInfo*> class_info 562 info.update({ 563 "number" : valuator.number, 564 "min" : valuator.min, 565 "max" : valuator.max, 566 "value" : valuator.value, 567 "resolution": valuator.resolution, 568 "mode" : valuator.mode, 569 }) 570 if valuator.label: 571 info["label"] = self.XGetAtomName(valuator.label) 572 elif class_info.type==XIScrollClass: 573 scroll = <XIScrollClassInfo*> class_info 574 info.update({ 575 "number" : scroll.number, 576 "scroll-type" : scroll.scroll_type, 577 "increment" : scroll.increment, 578 "flags" : scroll.flags, 579 }) 580 elif class_info.type==XITouchClass: 581 touch = <XITouchClassInfo*> class_info 582 info.update({ 583 "mode" : touch.mode, 584 "num-touches" : touch.num_touches, 585 }) 586 return info 587 588 589 def gdk_inject(self): 590 self.get_xi_opcode() 591 log.info("XInput Devices:") 592 from xpra.util import print_nested_dict 593 print_nested_dict(self.get_devices(), print_fn=log.info) 594 self.register_parser() 595 self.register_gdk_events() 596 #self.select_xi2_events() -
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: