Ticket #173: xi-events-v8.patch
File xi-events-v8.patch, 63.9 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 … … 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
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: … … 1201 1202 def scale_pointer(self, pointer): 1202 1203 return int(pointer[0]/self.xscale), int(pointer[1]/self.yscale) 1203 1204 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]) 1205 def send_button(self, wid, button, pressed, pointer, modifiers, buttons, *args): 1209 1206 pressed_state = self._button_state.get(button, False) 1210 1207 if PYTHON3 and WIN32 and pressed_state==pressed: 1211 1208 mouselog("button action: unchanged state, ignoring event") 1212 1209 return 1213 1210 self._button_state[button] = pressed 1214 send_button(pressed) 1211 packet = ["button-action", wid, 1212 button, pressed, 1213 pointer, modifiers, buttons] + list(args) 1214 log.info("button packet: %s", packet) 1215 self.send_positional(packet) 1215 1216 1216 1217 1217 1218 def window_keyboard_layout_changed(self, window): -
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 559 def noop(self, *args): 560 pass 561 562 def configured(self, *args): 563 self.windows = self.get_parent_windows(self.xid) 564 for window in self.windows: 565 self.XI2.connect(window, "XI_Motion", self.do_xi_motion) 566 self.XI2.connect(window, "XI_ButtonPress", self.do_xi_button) 567 self.XI2.connect(window, "XI_ButtonRelease", self.do_xi_button) 568 569 def get_parent_windows(self, oxid): 570 windows = [oxid] 571 root = self.X11Window.getDefaultRootWindow() 572 xid = oxid 573 while True: 574 xid = self.X11Window.getParent(xid) 575 if xid==0 or xid==root: 576 break 577 windows.append(xid) 578 log("get_parent_windows(%#x)=%s", oxid, csv(hex(x) for x in windows)) 579 return windows 580 581 582 def do_xi_button(self, event): 583 window = self.window 584 if self.window._client.readonly: 585 return 586 #TODO: server needs to tell us if it handles xinput: 587 server_input_devices = "auto" 588 if server_input_devices=="xinput": 589 #skip synthetic scroll events for two-finger scroll, 590 #as the server should synthesize them from the motion events 591 #those have the same serial: 592 matching_motion = self.XI2.find_event("XI_Motion", event.serial) 593 #maybe we need more to distinguish? 594 server_input_devices = "auto" 595 if matching_motion: 596 return 597 button = event.detail 598 depressed = (event.name == "XI_ButtonPress") 599 args = self.get_pointer_extra_args(event) 600 window._button_action(button, event, depressed, *args) 601 602 def do_xi_motion(self, event): 603 window = self.window 604 if window.moveresize_event: 605 window.motion_moveresize(event) 606 self.do_motion_notify_event(event) 607 return 608 if window._client.readonly: 609 return 610 log("do_motion_notify_event(%s)", event) 611 #find the motion events in the xi2 event list: 612 pointer, modifiers, buttons = window._pointer_modifiers(event) 613 wid = self.window.get_mouse_event_wid(*pointer) 614 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) 615 packet = ["pointer-position", wid, pointer, modifiers, buttons, 616 event.device] + self.get_pointer_extra_args(event) 617 mouselog.info("motion packet: %s", packet) 618 window._client.send_mouse_position(packet) 619 620 def get_pointer_extra_args(self, event): 621 args = [] 622 def intscaled(f): 623 return int(f*1000000), 1000000 624 def dictscaled(v): 625 return dict((k,intscaled(v)) for k,v in v.items()) 626 raw_valuators = {} 627 raw_event_name = event.name.replace("XI_", "XI_Raw") #ie: XI_Motion -> XI_RawMotion 628 raw = self.XI2.find_event(raw_event_name, event.serial) 629 #log.info("raw(%s)=%s", raw_event_name, raw) 630 if raw: 631 raw_valuators = raw.raw_valuators 632 for x in ("x", "y", "x_root", "y_root"): 633 args.append(intscaled(getattr(event, x))) 634 for v in [event.valuators, raw_valuators]: 635 args.append(dictscaled(v)) 636 return args 637 638 519 639 class ClientExtras(object): 520 640 def __init__(self, client, opts): 521 641 self.client = client … … 528 648 self.x11_filter = None 529 649 if client.xsettings_enabled: 530 650 self.setup_xprops() 651 if client.input_devices=="xi": 652 self.setup_xi() 531 653 self.setup_dbus_signals() 532 654 533 655 def ready(self): … … 565 687 bus._clean_up_signal_match(self.upower_sleeping_match) 566 688 if self.login1_match: 567 689 bus._clean_up_signal_match(self.login1_match) 690 global WINDOW_METHOD_OVERRIDES 691 WINDOW_METHOD_OVERRIDES = {} 568 692 569 693 def resuming_callback(self, *args): 570 694 eventlog("resuming_callback%s", args) … … 647 771 except ImportError as e: 648 772 log.error("failed to load X11 properties/settings bindings: %s - root window properties will not be propagated", e) 649 773 774 def setup_xi(self): 775 from xpra.gtk_common.error import xsync 776 def enable_xi2(): 777 try: 778 with xsync: 779 assert X11WindowBindings, "no X11 window bindings" 780 assert X11XI2Bindings, "no XI2 window bindings" 781 X11XI2Bindings().gdk_inject() 782 self.init_x11_filter() 783 XI2 = X11XI2Bindings() 784 XI2.select_xi2_events() 785 except Exception as e: 786 log("enable_xi2()", exc_info=True) 787 log.error("Error: cannot enable XI2 events") 788 log.error(" %s", e) 789 else: 790 #register our enhanced event handlers: 791 self.add_xi2_method_overrides() 792 with xsync: 793 try: 794 #this would trigger warnings with our temporary opengl windows: 795 #only enable it after we have connected: 796 self.client.after_handshake(enable_xi2) 797 except Exception as e: 798 log("setup_xi()", exc_info=True) 799 log.error("Error: failed to load the XI2 bindings") 800 log.error(" %s", e) 801 802 def add_xi2_method_overrides(self): 803 global WINDOW_ADD_HOOKS 804 WINDOW_ADD_HOOKS = [XI2_Window] 805 806 650 807 def _get_xsettings(self): 651 808 try: 652 809 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.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 340 def reset_events(self): 341 self.events = collections.deque(maxlen=100) 342 343 def find_event(self, event_name, serial): 344 for x in reversed(self.events): 345 #log.info("find_event(%s, %#x) checking %s", event_name, serial, x) 346 if x.name==event_name and x.serial==serial: 347 #log.info("matched") 348 return x 349 if x.serial<serial: 350 #log.info("serial too old") 351 return None 352 return None 353 354 def find_events(self, event_name, windows): 355 cdef Window found = 0 356 cdef Window window 357 matches = [] 358 for x in reversed(self.events): 359 window = x.xid 360 if x.name==event_name and ((found>0 and found==window) or (found==0 and window in windows)): 361 matches.append(x) 362 found = window 363 elif found: 364 break 365 return matches 366 367 cdef int get_xi_opcode(self, int major=2, int minor=2): 368 if self.opcode!=-1: 369 return self.opcode 370 cdef int opcode, event, error 371 if not XQueryExtension(self.display, "XInputExtension", &opcode, &event, &error): 372 log.warn("Warning: XI2 events are not supported") 373 self.opcode = 0 374 return 0 375 cdef int rmajor = major, rminor = minor 376 cdef int rc = XIQueryVersion(self.display, &rmajor, &rminor) 377 if rc == BadRequest: 378 log.warn("Warning: no XI2 %i.%i support,", major, minor) 379 log.warn(" server supports version %i.%i only", rmajor, rminor) 380 self.opcode = 0 381 return 0 382 elif rc: 383 log.warn("Warning: Xlib bug querying XI2, code %i", rc) 384 self.opcode = 0 385 return 0 386 self.opcode = opcode 387 log("get_xi_opcode%s=%i", (major, minor), opcode) 388 return opcode 389 390 cdef register_parser(self): 391 log("register_parser()") 392 if self.opcode>0: 393 from xpra.x11.gtk2.gdk_bindings import add_x_event_parser 394 add_x_event_parser(self.opcode, self.parse_xi_event) 395 396 cdef register_gdk_events(self): 397 log("register_gdk_events()") 398 if self.opcode<=0: 399 return 400 global XI_EVENT_NAMES 401 from xpra.x11.gtk2.gdk_bindings import add_x_event_signal, add_x_event_type_name 402 for e, xi_event_name in { 403 XI_DeviceChanged : "device-changed", 404 XI_KeyPress : "key-press", 405 XI_KeyRelease : "key-release", 406 XI_ButtonPress : "button-press", 407 XI_ButtonRelease : "button-release", 408 XI_Motion : "motion", 409 XI_Enter : "enter", 410 XI_Leave : "leave", 411 XI_FocusIn : "focus-in", 412 XI_FocusOut : "focus-out", 413 XI_HierarchyChanged : "focus-changed", 414 XI_PropertyEvent : "property-event", 415 XI_RawKeyPress : "raw-key-press", 416 XI_RawKeyRelease : "raw-key-release", 417 XI_RawButtonPress : "raw-button-press", 418 XI_RawButtonRelease : "raw-button-release", 419 XI_RawMotion : "raw-motion", 420 XI_TouchBegin : "touch-begin", 421 XI_TouchUpdate : "touch-update", 422 XI_TouchEnd : "touch-end", 423 XI_TouchOwnership : "touch-ownership", 424 XI_RawTouchBegin : "raw-touch-begin", 425 XI_RawTouchUpdate : "raw-touch-update", 426 XI_RawTouchEnd : "raw-touch-end", 427 }.items(): 428 event = self.opcode+e 429 add_x_event_signal(event, ("xi-%s" % xi_event_name, None)) 430 name = XI_EVENT_NAMES[e] 431 add_x_event_type_name(event, name) 432 433 def select_xi2_events(self): 434 cdef Window win = XDefaultRootWindow(self.display) 435 log("select_xi2_events() root window=%#x", win) 436 assert XI_LASTEVENT<MAX_XI_EVENTS, "bug: source needs to be updated, XI_LASTEVENT=%i" % XI_LASTEVENT 437 cdef XIEventMask evmasks[1] 438 cdef unsigned char mask1[XI_EVENT_MASK_SIZE] 439 memset(mask1, 0, XI_EVENT_MASK_SIZE) 440 #define XISetMask(ptr, event) (((unsigned char*)(ptr))[(event)>>3] |= (1 << ((event) & 7))) 441 #XISetMask(mask1, XI_RawMotion) 442 for e in ( 443 XI_KeyPress, XI_KeyRelease, 444 XI_Motion, 445 XI_HierarchyChanged, 446 XI_ButtonPress, XI_ButtonRelease, 447 XI_RawButtonPress, XI_RawButtonRelease, 448 XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd, 449 XI_RawTouchBegin, XI_RawTouchUpdate, XI_RawTouchEnd, 450 XI_RawMotion, 451 ): 452 mask1[e>>3] |= (1<< (e & 0x7)) 453 evmasks[0].deviceid = XIAllDevices #XIAllMasterDevices #XIAllDevices 454 evmasks[0].mask_len = XI_EVENT_MASK_SIZE 455 evmasks[0].mask = mask1 456 XISelectEvents(self.display, win, evmasks, 1) 457 XFlush(self.display) 458 459 def parse_xi_event(self, display, uintptr_t _cookie): 460 log("parse_xi_event(%s)", _cookie) 461 cdef XGenericEventCookie *cookie = <XGenericEventCookie*> _cookie 462 cdef XIDeviceEvent *device_e 463 cdef XIHierarchyEvent * hierarchy_e 464 cdef XIEvent *xie 465 cdef XIRawEvent *raw 466 cdef int i = 0, j = 0 467 if not XGetEventData(self.display, cookie): 468 return None 469 xie = <XIEvent*> cookie.data 470 device_e = <XIDeviceEvent*> cookie.data 471 cdef int xi_type = cookie.evtype 472 etype = self.opcode+xi_type 473 global XI_EVENT_NAMES 474 event_name = XI_EVENT_NAMES.get(xi_type) 475 if not event_name: 476 log("unknown XI2 event code: %i", xi_type) 477 return None 478 479 #don't parse the same thing again: 480 if len(self.events)>0: 481 last_event = self.events[-1] 482 if last_event.serial==xie.serial and last_event.type==etype: 483 return last_event 484 485 pyev = X11Event(event_name) 486 pyev.type = etype 487 pyev.display = display 488 pyev.send_event = bool(xie.send_event) 489 pyev.serial = xie.serial 490 pyev.time = int(xie.time) 491 pyev.window = int(XDefaultRootWindow(self.display)) 492 493 if xi_type in (XI_KeyPress, XI_KeyRelease, 494 XI_ButtonPress, XI_ButtonRelease, 495 XI_Motion, 496 XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd): 497 device = <XIDeviceEvent*> cookie.data 498 #pyev.source = device.sourceid #always 0 499 pyev.device = device.deviceid 500 pyev.detail = device.detail 501 pyev.flags = device.flags 502 pyev.window = int(device.child or device.event or device.root) 503 pyev.x_root = device.root_x 504 pyev.y_root = device.root_y 505 pyev.x = device.event_x 506 pyev.y = device.event_y 507 #mask = [] 508 valuators = {} 509 valuator = 0 510 for i in range(device.valuators.mask_len*8): 511 if device.valuators.mask[i>>3] & (1 << (i & 0x7)): 512 valuators[i] = device.valuators.values[valuator] 513 valuator += 1 514 pyev.valuators = valuators 515 buttons = [] 516 for i in range(device.buttons.mask_len): 517 if device.buttons.mask[i>>3] & (1<< (i & 0x7)): 518 buttons.append(i) 519 pyev.buttons = buttons 520 state = [] 521 pyev.state = state 522 pyev.modifiers = { 523 "base" : device.mods.base, 524 "latched" : device.mods.latched, 525 "locked" : device.mods.locked, 526 "effective" : device.mods.effective, 527 } 528 #make it compatible with gdk events: 529 pyev.state = device.mods.effective 530 elif xi_type in (XI_RawKeyPress, XI_RawKeyRelease, 531 XI_RawButtonPress, XI_RawButtonRelease, 532 XI_RawMotion, 533 XI_RawTouchBegin, XI_RawTouchUpdate, XI_RawTouchEnd): 534 raw = <XIRawEvent*> cookie.data 535 valuators = {} 536 raw_valuators = {} 537 valuator = 0 538 for i in range(raw.valuators.mask_len*8): 539 if raw.valuators.mask[i>>3] & (1 << (i & 0x7)): 540 valuators[i] = raw.valuators.values[valuator] 541 raw_valuators[i] = raw.raw_values[valuator] 542 valuator += 1 543 pyev.valuators = valuators 544 pyev.raw_valuators = raw_valuators 545 elif xi_type == XI_HierarchyChanged: 546 pass 547 #hierarchy_e = <XIHierarchyEvent*> cookie.data 548 #pyev.flags = hierarchy_e.flags 549 #info = {} 550 #for i in range(hierarchy_e.num_info): 551 # 552 XFreeEventData(self.display, cookie) 553 pyev.xid = pyev.window 554 self.events.append(pyev) 555 556 handler = self.event_handlers.get(pyev.window, {}).get(event_name) 557 log("parse_xi_event: %s, handler=%s", pyev, handler) 558 if handler: 559 handler(pyev) 560 return None 561 562 def get_devices(self, show_all=True, show_disabled=False): 563 global XI_USE 564 cdef int ndevices, i, j 565 cdef XIDeviceInfo *devices 566 cdef XIDeviceInfo *device 567 cdef XIAnyClassInfo *clazz 568 if show_all: 569 device_types = XIAllDevices 570 else: 571 device_types = XIAllMasterDevices 572 devices = XIQueryDevice(self.display, device_types, &ndevices) 573 dinfo = {} 574 for i in range(ndevices): 575 device = &devices[i] 576 if not device.enabled and not show_disabled: 577 continue 578 info = { 579 "name" : device.name, 580 "use" : XI_USE.get(device.use, "unknown use: %i" % device.use), 581 "attachment" : device.attachment, 582 "enabled" : device.enabled, 583 } 584 classes = {} 585 for j in range(device.num_classes): 586 clazz = device.classes[j] 587 classes[j] = self.get_class_info(clazz) 588 info["classes"] = classes 589 properties = self.get_device_properties(device.deviceid) 590 if properties: 591 info["properties"] = properties 592 dinfo[device.deviceid] = info 593 XIFreeDeviceInfo(devices) 594 return dinfo 595 596 def get_device_properties(self, deviceid): 597 cdef Atom *atoms 598 cdef int nprops, i 599 atoms = XIListProperties(self.display, deviceid, &nprops) 600 if atoms==NULL or nprops==0: 601 return None 602 props = {} 603 for i in range(nprops): 604 value = self.get_device_property(deviceid, atoms[i]) 605 if value is not None: 606 prop_name = self.XGetAtomName(atoms[i]) 607 props[prop_name] = value 608 return props 609 610 cdef get_device_property(self, int deviceid, Atom property, req_type=0): 611 #code mostly duplicated from window_bindings XGetWindowProperty: 612 cdef int buffer_size = 64 * 1024 613 cdef Atom xactual_type = <Atom> 0 614 cdef int actual_format = 0 615 cdef long offset = 0 616 cdef unsigned long nitems = 0, bytes_after = 0 617 cdef unsigned char *prop = NULL 618 cdef Status status 619 cdef Atom xreq_type = XIAnyPropertyType 620 if req_type: 621 xreq_type = self.get_xatom(req_type) 622 623 status = XIGetProperty(self.display, 624 deviceid, property, 625 0, 626 buffer_size//4, 627 False, 628 xreq_type, &xactual_type, 629 &actual_format, &nitems, &bytes_after, &prop) 630 if status != Success: 631 raise Exception("failed to retrieve XI property") 632 if xactual_type == XNone: 633 return None 634 if xreq_type and xreq_type != xactual_type: 635 raise Exception("expected %s but got %s" % (req_type, self.XGetAtomName(xactual_type))) 636 # This should only occur for bad property types: 637 assert not (bytes_after and not nitems) 638 if bytes_after: 639 raise Exception("reserved %i bytes for buffer, but data is bigger by %i bytes!" % (buffer_size, bytes_after)) 640 assert actual_format > 0 641 if actual_format == 8: 642 bytes_per_item = 1 643 elif actual_format == 16: 644 bytes_per_item = sizeof(short) 645 elif actual_format == 32: 646 bytes_per_item = 4 #sizeof(long) 647 else: 648 assert False 649 cdef int nbytes = bytes_per_item * nitems 650 data = (<char *> prop)[:nbytes] 651 XFree(prop) 652 prop_type = self.XGetAtomName(xactual_type) 653 import binascii 654 log.info("%s : %s value=%s", self.XGetAtomName(property), prop_type, data) 655 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) 656 657 #FIXME: 32 here.. breaks 658 if actual_format == 33: 659 data = self.munge_packed_longs_to_ints(data) 660 661 fmt = None 662 if prop_type=="INTEGER": 663 fmt = { 664 8 : "b", 665 16 : "h", 666 32 : "i", 667 }.get(actual_format) 668 elif prop_type=="CARDINAL": 669 fmt = { 670 8 : "B", 671 16 : "H", 672 32 : "I", 673 }.get(actual_format) 674 elif prop_type=="FLOAT": 675 fmt = "f" 676 if fmt: 677 log.info("parsing using %s", fmt*nitems) 678 value = struct.unpack(fmt*nitems, data) 679 if nitems==1: 680 return value[0] 681 return value 682 return data 683 684 cdef get_class_info(self, XIAnyClassInfo *class_info): 685 cdef int i 686 cdef XIButtonClassInfo *button 687 cdef XIKeyClassInfo *key 688 cdef XIValuatorClassInfo *valuator 689 cdef XIScrollClassInfo *scroll 690 cdef XITouchClassInfo *touch 691 info = { 692 "type" : CLASS_INFO.get(class_info.type, "unknown type: %i" % class_info.type), 693 "sourceid" : class_info.sourceid, 694 } 695 if class_info.type==XIButtonClass: 696 button = <XIButtonClassInfo*> class_info 697 buttons = [] 698 for i in range(button.num_buttons): 699 if button.labels[i]>0: 700 buttons.append(self.XGetAtomName(button.labels[i])) 701 info["buttons"] = buttons 702 #XIButtonState state 703 elif class_info.type==XIKeyClass: 704 key = <XIKeyClassInfo*> class_info 705 keys = [] 706 for i in range(key.num_keycodes): 707 keys.append(key.keycodes[i]) 708 elif class_info.type==XIValuatorClass: 709 valuator = <XIValuatorClassInfo*> class_info 710 info.update({ 711 "number" : valuator.number, 712 "min" : valuator.min, 713 "max" : valuator.max, 714 "value" : valuator.value, 715 "resolution": valuator.resolution, 716 "mode" : valuator.mode, 717 }) 718 if valuator.label: 719 info["label"] = self.XGetAtomName(valuator.label) 720 elif class_info.type==XIScrollClass: 721 scroll = <XIScrollClassInfo*> class_info 722 info.update({ 723 "number" : scroll.number, 724 "scroll-type" : scroll.scroll_type, 725 "increment" : scroll.increment, 726 "flags" : scroll.flags, 727 }) 728 elif class_info.type==XITouchClass: 729 touch = <XITouchClassInfo*> class_info 730 info.update({ 731 "mode" : touch.mode, 732 "num-touches" : touch.num_touches, 733 }) 734 return info 735 736 737 def gdk_inject(self): 738 self.get_xi_opcode() 739 log.info("XInput Devices:") 740 from xpra.util import print_nested_dict 741 print_nested_dict(self.get_devices(), print_fn=log.info) 742 self.register_parser() 743 self.register_gdk_events() 744 #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: