xpra icon
Bug tracker and wiki

This bug tracker and wiki are being discontinued
please use https://github.com/Xpra-org/xpra instead.


Ticket #173: xi-events-v7.patch

File xi-events-v7.patch, 56.4 KB (added by Antoine Martin, 4 years ago)

better approach: register event handlers directly with the XI2 helper class

  • setup.py

     
    914914                   "xpra/x11/bindings/core_bindings.c",
    915915                   "xpra/x11/bindings/posix_display_source.c",
    916916                   "xpra/x11/bindings/ximage.c",
     917                   "xpra/x11/bindings/xi2_bindings.c",
    917918                   "xpra/platform/win32/propsys.cpp",
    918919                   "xpra/platform/darwin/gdk_bindings.c",
    919920                   "xpra/net/bencode/cython_bencode.c",
     
    17341735                ["xpra/x11/bindings/ximage.pyx"],
    17351736                **pkgconfig("x11", "xcomposite", "xdamage", "xext")
    17361737                ))
     1738    cython_add(Extension("xpra.x11.bindings.xi2_bindings",
     1739                ["xpra/x11/bindings/xi2_bindings.pyx"],
     1740                **pkgconfig("x11", "xi")
     1741                ))
    17371742
    17381743toggle_packages(gtk_x11_ENABLED, "xpra.x11.gtk_x11")
    17391744if gtk_x11_ENABLED:
  • xpra/client/client_window_base.py

     
    644644        #overriden in GTKClientWindowBase
    645645        return self._id
    646646
     647    def do_xi_motion(self, *args):
     648        log.warn("do_xi_motion%s", args)
     649
     650
    647651    def do_motion_notify_event(self, event):
    648652        if self._client.readonly:
    649653            return
     
    659663        except:
    660664            return ""
    661665
    662     def _button_action(self, button, event, depressed):
     666    def _button_action(self, button, event, depressed, *args):
    663667        if self._client.readonly:
    664668            return
    665669        pointer, modifiers, buttons = self._pointer_modifiers(event)
     
    680684                b = sb
    681685            server_buttons.append(b)
    682686        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)
    684688        pressed_state = self.button_state.get(button, False)
    685689        if SIMULATE_MOUSE_DOWN and pressed_state is False and depressed is False:
    686690            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

     
    3636                       MOVERESIZE_SIZE_LEFT, MOVERESIZE_MOVE)
    3737
    3838from 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
     39from xpra.gtk_common.gobject_util import no_arg_signal, one_arg_signal
    4040from xpra.gtk_common.gtk_util import (get_pixbuf_from_data, get_default_root_window, is_realized,
    4141    WINDOW_POPUP, WINDOW_TOPLEVEL, GRAB_STATUS_STRING, GRAB_SUCCESS, SCROLL_UP, SCROLL_DOWN, SCROLL_LEFT, SCROLL_RIGHT)
    4242from xpra.gtk_common.keymap import KEY_TRANSLATIONS
     
    145145
    146146    __common_gsignals__ = {
    147147        "state-updated"         : no_arg_signal,
     148        "xi-motion"             : one_arg_signal,
    148149        }
    149150
    150151    #maximum size of the actual window:
     
    933934
    934935
    935936    def do_motion_notify_event(self, event):
     937        log.info("do_motion_notify_event")
    936938        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)
    1004940        ClientWindowBase.do_motion_notify_event(self, event)
    1005941
     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
    10061011    def do_moveresize(self):
    10071012        self.moveresize_timer = None
    10081013        mrd = self.moveresize_data
     
    13231328    def _pointer_modifiers(self, event):
    13241329        x, y = self._get_pointer(event)
    13251330        pointer = self._pointer(x, y)
     1331        #FIXME: state is used for both mods and buttons??
    13261332        modifiers = self._client.mask_to_names(event.state)
    13271333        buttons = self._event_buttons(event)
    13281334        v = pointer, modifiers, buttons
  • xpra/client/ui_client_base.py

     
    424424        self.client_supports_sharing = opts.sharing
    425425        self.log_both = (opts.remote_logging or "").lower()=="both"
    426426        self.client_supports_remote_logging = self.log_both or parse_bool("remote-logging", opts.remote_logging)
     427        self.input_devices = opts.input_devices
    427428        #mouse wheel:
    428429        mw = (opts.mousewheel or "").lower().replace("-", "")
    429430        if mw not in FALSE_OPTIONS:
     
    12011202    def scale_pointer(self, pointer):
    12021203        return int(pointer[0]/self.xscale), int(pointer[1]/self.yscale)
    12031204
    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):
    12091206        pressed_state = self._button_state.get(button, False)
    12101207        if PYTHON3 and WIN32 and pressed_state==pressed:
    12111208            mouselog("button action: unchanged state, ignoring event")
    12121209            return
    12131210        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)
    12151216
    12161217
    12171218    def window_keyboard_layout_changed(self, window):
  • xpra/log.py

     
    264264                ])),
    265265    ("X11", OrderedDict([
    266266                ("x11"          , "All X11 code"),
     267                ("xinput"       , "XInput bindings"),
    267268                ("bindings"     , "X11 Cython bindings"),
    268269                ("core"         , "X11 core bindings"),
    269270                ("randr"        , "X11 RandR bindings"),
  • xpra/platform/darwin/shadow_server.py

     
    139139        GTKShadowServerBase.stop_refresh(self)
    140140
    141141
    142     def do_process_mouse_common(self, proto, wid, pointer):
     142    def do_process_mouse_common(self, proto, wid, pointer, *args):
    143143        CG.CGWarpMouseCursorPosition(pointer)
    144144
    145145    def fake_key(self, keycode, press):
  • xpra/platform/features.py

     
    1515SYSTEM_TRAY_SUPPORTED = False
    1616REINIT_WINDOWS = False
    1717
     18INPUT_DEVICES = ["auto"]
     19
    1820CLIPBOARDS = []
    1921CLIPBOARD_WANT_TARGETS = envbool("XPRA_CLIPBOARD_WANT_TARGETS")
    2022CLIPBOARD_GREEDY = envbool("XPRA_CLIPBOARD_GREEDY")
     
    6264                   "CLIPBOARD_NATIVE_CLASS",
    6365                   "UI_THREAD_POLLING",
    6466                   "CLIENT_MODULES",
     67                   "INPUT_DEVICES",
    6568                   ]
    6669from xpra.platform import platform_import
    6770platform_import(globals(), "features", False,
  • xpra/platform/win32/shadow_server.py

     
    341341        log("refresh()=%s", v)
    342342        return v
    343343
    344     def do_process_mouse_common(self, proto, wid, pointer):
     344    def do_process_mouse_common(self, proto, wid, pointer, *args):
    345345        #adjust pointer position for offset in client:
    346346        try:
    347347            SetCursorPos(*pointer)
  • xpra/platform/xposix/features.py

     
    2121
    2222DEFAULT_SSH_CMD = "ssh"
    2323CLIPBOARDS=["CLIPBOARD", "PRIMARY", "SECONDARY"]
     24
     25INPUT_DEVICES = ["auto", "xi"]
  • xpra/platform/xposix/gui.py

     
    1616dbuslog = Logger("posix", "dbus")
    1717traylog = Logger("posix", "menu")
    1818menulog = Logger("posix", "menu")
     19mouselog = Logger("posix", "mouse")
    1920
    2021from xpra.os_util import strtobytes, bytestostr
    21 from xpra.util import iround, envbool
     22from xpra.util import iround, envbool, csv
    2223from xpra.gtk_common.gobject_compat import get_xid, is_gtk3
    2324
     25try:
     26    from xpra.x11.bindings.window_bindings import X11WindowBindings
     27    from xpra.x11.bindings.xi2_bindings import X11XI2Bindings   #@UnresolvedImport
     28except Exception as e:
     29    log("no ")
     30    X11WindowBindings = None
     31    X11XI2Bindings = None
     32
    2433device_bell = None
    2534GTK_MENUS = envbool("XPRA_GTK_MENUS", False)
    2635RANDR_DPI = envbool("XPRA_RANDR_DPI", True)
     
    93102def _get_X11_window_property(xid, name, req_type):
    94103    try:
    95104        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
    98106        try:
     107            X11Window = X11WindowBindings()
    99108            with xsync:
    100                 prop = window_bindings.XGetWindowProperty(xid, name, req_type)
     109                prop = X11Window.XGetWindowProperty(xid, name, req_type)
    101110            log("_get_X11_window_property(%#x, %s, %s)=%s, len=%s", xid, name, req_type, type(prop), len(prop or []))
    102111            return prop
    103112        except PropertyError as e:
     
    108117    return None
    109118def _get_X11_root_property(name, req_type):
    110119    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()
    114122        return _get_X11_window_property(root_xid, name, req_type)
    115123    except Exception as e:
    116124        log.warn("Warning: failed to get X11 root property '%s'", name)
     
    170178
    171179def _get_xsettings():
    172180    try:
    173         from xpra.x11.bindings.window_bindings import X11WindowBindings #@UnresolvedImport
    174         window_bindings = X11WindowBindings()
     181        X11Window = X11WindowBindings()
    175182        selection = "_XSETTINGS_S0"
    176         owner = window_bindings.XGetSelectionOwner(selection)
     183        owner = X11Window.XGetSelectionOwner(selection)
    177184        if not owner:
    178185            return None
    179186        XSETTINGS = "_XSETTINGS_SETTINGS"
    180         data = window_bindings.XGetWindowProperty(owner, XSETTINGS, XSETTINGS)
     187        data = X11Window.XGetWindowProperty(owner, XSETTINGS, XSETTINGS)
    181188        if not data:
    182189            return None
    183190        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)
    185192    except Exception as e:
    186193        log("_get_xsettings error: %s", e)
    187194    return None
     
    461468    try:
    462469        from xpra.x11.gtk2 import gdk_display_source
    463470        assert gdk_display_source
    464         from xpra.x11.bindings.window_bindings import constants, X11WindowBindings #@UnresolvedImport
     471        from xpra.x11.bindings.window_bindings import constants #@UnresolvedImport
    465472        X11Window = X11WindowBindings()
    466473        root_xid = X11Window.getDefaultRootWindow()
    467474        if window:
     
    499506    _toggle_wm_state(window, "_NET_WM_STATE_SHADED", shaded)
    500507
    501508
     509
     510WINDOW_ADD_HOOKS = []
     511def 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
     517WINDOW_REMOVE_HOOKS = []
     518def 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
    502525def get_info():
    503526    from xpra.platform.gui import get_info_base
    504527    i = get_info_base()
     
    516539    return i
    517540
    518541
     542class 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
    519639class ClientExtras(object):
    520640    def __init__(self, client, opts):
    521641        self.client = client
     
    528648        self.x11_filter = None
    529649        if client.xsettings_enabled:
    530650            self.setup_xprops()
     651        if client.input_devices=="xi":
     652            self.setup_xi()
    531653        self.setup_dbus_signals()
    532654
    533655    def ready(self):
     
    565687                bus._clean_up_signal_match(self.upower_sleeping_match)
    566688            if self.login1_match:
    567689                bus._clean_up_signal_match(self.login1_match)
     690        global WINDOW_METHOD_OVERRIDES
     691        WINDOW_METHOD_OVERRIDES = {}
    568692
    569693    def resuming_callback(self, *args):
    570694        eventlog("resuming_callback%s", args)
     
    647771        except ImportError as e:
    648772            log.error("failed to load X11 properties/settings bindings: %s - root window properties will not be propagated", e)
    649773
     774    def setup_xi(self):
     775        from xpra.gtk_common.error import xsync
     776        def enable_xi2():
     777            try:
     778                with xsync:
     779                    self.init_x11_filter()
     780                    XI2 = X11XI2Bindings()
     781                    XI2.select_xi2_events()
     782            except Exception as e:
     783                log("enable_xi2()", exc_info=True)
     784                log.error("Error: cannot enable XI2 events")
     785                log.error(" %s", e)
     786            else:
     787                #register our enhanced event handlers:
     788                self.add_xi2_method_overrides()
     789        with xsync:
     790            try:
     791                assert X11WindowBindings, "no X11 window bindings"
     792                assert X11XI2Bindings, "no XI2 window bindings"
     793                X11XI2Bindings().gdk_inject()
     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
    650807    def _get_xsettings(self):
    651808        try:
    652809            return self._xsettings_watcher.get_settings()
  • xpra/scripts/config.py

     
    489489                    "dbus-launch"       : str,
    490490                    "webcam"            : str,
    491491                    "mousewheel"        : str,
     492                    "input-devices"     : str,
    492493                    #ssl options:
    493494                    "ssl"               : str,
    494495                    "ssl-key"           : str,
     
    619620                  "quality", "min-quality", "speed", "min-speed",
    620621                  "compression_level",
    621622                  "dpi", "video-scaling", "auto-refresh-delay",
    622                   "webcam", "mousewheel", "pings",
     623                  "webcam", "mousewheel", "input-devices", "pings",
    623624                  "tray", "keyboard-sync", "cursors", "bell", "notifications",
    624625                  "xsettings", "system-tray", "sharing",
    625626                  "delay-tray", "windows", "readonly",
     
    811812                    "dbus-launch"       : "dbus-launch --close-stderr",
    812813                    "webcam"            : ["auto", "no"][OSX],
    813814                    "mousewheel"        : "on",
     815                    "input-devices"     : "auto",
    814816                    #ssl options:
    815817                    "ssl"               : "auto",
    816818                    "ssl-key"           : "",
  • xpra/scripts/main.py

     
    562562    group.add_option("--mousewheel", action="store",
    563563                      dest="mousewheel", default=defaults.mousewheel,
    564564                      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]})
    565572    legacy_bool_parse("global-menus")
    566573    group.add_option("--global-menus", action="store",
    567574                      dest="global_menus", default=defaults.global_menus, metavar="yes|no",
  • xpra/server/server_base.py

     
    28782878                        return px+(wx-cx), py+(wy-cy)
    28792879        return pointer
    28802880
    2881     def _process_mouse_common(self, proto, wid, pointer):
     2881    def _process_mouse_common(self, proto, wid, pointer, device=-1, *args):
    28822882        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)
    28842885        return pointer
    28852886
    2886     def do_process_mouse_common(self, proto, wid, pointer):
     2887    def do_process_mouse_common(self, proto, wid, pointer, device=-1, *args):
    28872888        pass
    28882889
    28892890
     
    29092910        if self.readonly:
    29102911            return
    29112912        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)
    29122918        ss = self._server_sources.get(proto)
    29132919        if ss is not None:
    29142920            ss.mouse_last_position = pointer
     
    29152921        if self.ui_driver and self.ui_driver!=ss.uuid:
    29162922            return
    29172923        self._update_modifiers(proto, wid, modifiers)
    2918         self._process_mouse_common(proto, wid, pointer)
     2924        self._process_mouse_common(proto, wid, pointer, device)
    29192925
    29202926
    29212927    def _process_damage_sequence(self, proto, packet):
  • xpra/x11/bindings/core_bindings.pyx

     
    3333    ctypedef int Bool
    3434
    3535    Atom XInternAtom(Display * display, char * atom_name, Bool only_if_exists)
     36    char *XGetAtomName(Display *display, Atom atom)
    3637
    3738    int XFree(void * data)
    3839
     
    8384    def get_xatom(self, str_or_int):
    8485        return self.xatom(str_or_int)
    8586
     87    def XGetAtomName(self, Atom atom):
     88        v = XGetAtomName(self.display, atom)
     89        return v[:]
     90
     91
    8692    def get_error_text(self, code):
    8793        assert self.display!=NULL, "display is closed"
    8894        if type(code)!=int:
  • xpra/x11/bindings/window_bindings.pyx

     
    8181        pass
    8282
    8383    Atom XInternAtom(Display * display, char * atom_name, Bool only_if_exists)
    84     char *XGetAtomName(Display *display, Atom atom)
    8584
    8685    Window XDefaultRootWindow(Display * display)
    8786
     
    461460        return XDefaultRootWindow(self.display)
    462461
    463462
    464     cpdef XGetAtomName(self, Atom atom):
    465         v = XGetAtomName(self.display, atom)
    466         return v[:]
    467 
    468463    def MapWindow(self, Window xwindow):
    469464        XMapWindow(self.display, xwindow)
    470465
  • 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
     6import os
     7import time
     8import collections
     9
     10from xpra.log import Logger
     11log = Logger("x11", "bindings", "xinput")
     12
     13from xpra.x11.gtk2.common import X11Event
     14
     15from libc.stdint cimport uintptr_t
     16
     17
     18###################################
     19# Headers, python magic
     20###################################
     21cdef extern from "string.h":
     22    void* memset(void * ptr, int value, size_t num)
     23
     24cdef extern from "X11/Xutil.h":
     25    pass
     26
     27######
     28# Xlib primitives and constants
     29######
     30
     31include "constants.pxi"
     32ctypedef unsigned long CARD32
     33
     34cdef 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
     70cdef 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
     263DEF MAX_XI_EVENTS = 64
     264DEF XI_EVENT_MASK_SIZE = (MAX_XI_EVENTS+7)//8
     265
     266XI_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
     293XI_USE = {
     294    XIMasterPointer     : "master pointer",
     295    XIMasterKeyboard    : "master keyboard",
     296    XISlavePointer      : "slave pointer",
     297    XISlaveKeyboard     : "slave keyboard",
     298    XIFloatingSlave     : "floating slave",
     299    }
     300
     301CLASS_INFO = {
     302    XIButtonClass       : "button",
     303    XIKeyClass          : "key",
     304    XIValuatorClass     : "valuator",
     305    XIScrollClass       : "scroll",
     306    XITouchClass        : "touch",
     307    }
     308
     309
     310from core_bindings cimport _X11CoreBindings
     311
     312cdef _X11XI2Bindings singleton = None
     313def X11XI2Bindings():
     314    global singleton
     315    if singleton is None:
     316        singleton = _X11XI2Bindings()
     317    return singleton
     318
     319cdef class _X11XI2Bindings(_X11CoreBindings):
     320
     321    cdef int opcode
     322    cdef object events
     323    cdef object event_handlers
     324
     325    def __init__(self):
     326        self.opcode = -1
     327        self.event_handlers = {}
     328        self.reset_events()
     329
     330    def __repr__(self):
     331        return "X11XI2Bindings(%s)" % self.display_name
     332
     333    def connect(self, window, event, handler):
     334        self.event_handlers.setdefault(window, {})[event] = handler
     335
     336
     337    def reset_events(self):
     338        self.events = collections.deque(maxlen=100)
     339
     340    def find_event(self, event_name, serial):
     341        for x in reversed(self.events):
     342            #log.info("find_event(%s, %#x) checking %s", event_name, serial, x)
     343            if x.name==event_name and x.serial==serial:
     344                #log.info("matched")
     345                return x
     346            if x.serial<serial:
     347                #log.info("serial too old")
     348                return None
     349        return None
     350
     351    def find_events(self, event_name, windows):
     352        cdef Window found = 0
     353        cdef Window window
     354        matches = []
     355        for x in reversed(self.events):
     356            window = x.xid
     357            if x.name==event_name and ((found>0 and found==window) or (found==0 and window in windows)):
     358                matches.append(x)
     359                found = window
     360            elif found:
     361                break
     362        return matches
     363
     364    cdef int get_xi_opcode(self, int major=2, int minor=2):
     365        if self.opcode!=-1:
     366            return self.opcode
     367        cdef int opcode, event, error
     368        if not XQueryExtension(self.display, "XInputExtension", &opcode, &event, &error):
     369            log.warn("Warning: XI2 events are not supported")
     370            self.opcode = 0
     371            return 0
     372        cdef int rmajor = major, rminor = minor
     373        cdef int rc = XIQueryVersion(self.display, &rmajor, &rminor)
     374        if rc == BadRequest:
     375            log.warn("Warning: no XI2 %i.%i support,", major, minor)
     376            log.warn(" server supports version %i.%i only", rmajor, rminor)
     377            self.opcode = 0
     378            return 0
     379        elif rc:
     380            log.warn("Warning: Xlib bug querying XI2, code %i", rc)
     381            self.opcode = 0
     382            return 0
     383        self.opcode = opcode
     384        log("get_xi_opcode%s=%i", (major, minor), opcode)
     385        return opcode
     386
     387    cdef register_parser(self):
     388        log("register_parser()")
     389        if self.opcode>0:
     390            from xpra.x11.gtk2.gdk_bindings import add_x_event_parser
     391            add_x_event_parser(self.opcode, self.parse_xi_event)
     392
     393    cdef register_gdk_events(self):
     394        log("register_gdk_events()")
     395        if self.opcode<=0:
     396            return
     397        global XI_EVENT_NAMES
     398        from xpra.x11.gtk2.gdk_bindings import add_x_event_signal, add_x_event_type_name
     399        for e, xi_event_name in {
     400            XI_DeviceChanged    : "device-changed",
     401            XI_KeyPress         : "key-press",
     402            XI_KeyRelease       : "key-release",
     403            XI_ButtonPress      : "button-press",
     404            XI_ButtonRelease    : "button-release",
     405            XI_Motion           : "motion",
     406            XI_Enter            : "enter",
     407            XI_Leave            : "leave",
     408            XI_FocusIn          : "focus-in",
     409            XI_FocusOut         : "focus-out",
     410            XI_HierarchyChanged : "focus-changed",
     411            XI_PropertyEvent    : "property-event",
     412            XI_RawKeyPress      : "raw-key-press",
     413            XI_RawKeyRelease    : "raw-key-release",
     414            XI_RawButtonPress   : "raw-button-press",
     415            XI_RawButtonRelease : "raw-button-release",
     416            XI_RawMotion        : "raw-motion",
     417            XI_TouchBegin       : "touch-begin",
     418            XI_TouchUpdate      : "touch-update",
     419            XI_TouchEnd         : "touch-end",
     420            XI_TouchOwnership   : "touch-ownership",
     421            XI_RawTouchBegin    : "raw-touch-begin",
     422            XI_RawTouchUpdate   : "raw-touch-update",
     423            XI_RawTouchEnd      : "raw-touch-end",
     424            }.items():
     425            event = self.opcode+e
     426            add_x_event_signal(event, ("xi-%s" % xi_event_name, None))
     427            name = XI_EVENT_NAMES[e]
     428            add_x_event_type_name(event, name)
     429
     430    def select_xi2_events(self):
     431        cdef Window win = XDefaultRootWindow(self.display)
     432        log("select_xi2_events() root window=%#x", win)
     433        assert XI_LASTEVENT<MAX_XI_EVENTS, "bug: source needs to be updated, XI_LASTEVENT=%i" % XI_LASTEVENT
     434        cdef XIEventMask evmasks[1]
     435        cdef unsigned char mask1[XI_EVENT_MASK_SIZE]
     436        memset(mask1, 0, XI_EVENT_MASK_SIZE)
     437        #define XISetMask(ptr, event)   (((unsigned char*)(ptr))[(event)>>3] |=  (1 << ((event) & 7)))
     438        #XISetMask(mask1, XI_RawMotion)
     439        for e in (
     440            XI_KeyPress, XI_KeyRelease,
     441            XI_Motion,
     442            XI_HierarchyChanged,
     443            XI_ButtonPress, XI_ButtonRelease,
     444            XI_RawButtonPress, XI_RawButtonRelease,
     445            XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd,
     446            XI_RawTouchBegin, XI_RawTouchUpdate, XI_RawTouchEnd,
     447            XI_RawMotion,
     448            ):
     449            mask1[e>>3] |= (1<< (e & 0x7))
     450        evmasks[0].deviceid = XIAllDevices  #XIAllMasterDevices    #XIAllDevices
     451        evmasks[0].mask_len = XI_EVENT_MASK_SIZE
     452        evmasks[0].mask = mask1
     453        XISelectEvents(self.display, win, evmasks, 1)
     454        XFlush(self.display)
     455
     456    def parse_xi_event(self, display, uintptr_t _cookie):
     457        log("parse_xi_event(%s)", _cookie)
     458        cdef XGenericEventCookie *cookie = <XGenericEventCookie*> _cookie
     459        cdef XIDeviceEvent *device_e
     460        cdef XIHierarchyEvent * hierarchy_e
     461        cdef XIEvent *xie
     462        cdef XIRawEvent *raw
     463        cdef int i = 0, j = 0
     464        if not XGetEventData(self.display, cookie):
     465            return None
     466        xie = <XIEvent*> cookie.data
     467        device_e = <XIDeviceEvent*> cookie.data
     468        cdef int xi_type = cookie.evtype
     469        etype = self.opcode+xi_type
     470        global XI_EVENT_NAMES
     471        event_name = XI_EVENT_NAMES.get(xi_type)
     472        if not event_name:
     473            log("unknown XI2 event code: %i", xi_type)
     474            return None
     475
     476        #don't parse the same thing again:
     477        if len(self.events)>0:
     478            last_event = self.events[-1]
     479            if last_event.serial==xie.serial and last_event.type==etype:
     480                return last_event
     481
     482        pyev = X11Event(event_name)
     483        pyev.type = etype
     484        pyev.display = display
     485        pyev.send_event = bool(xie.send_event)
     486        pyev.serial = xie.serial
     487        pyev.time = int(xie.time)
     488        pyev.window = int(XDefaultRootWindow(self.display))
     489
     490        if xi_type in (XI_KeyPress, XI_KeyRelease,
     491                       XI_ButtonPress, XI_ButtonRelease,
     492                       XI_Motion,
     493                       XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd):
     494            device = <XIDeviceEvent*> cookie.data
     495            #pyev.source = device.sourceid    #always 0
     496            pyev.device = device.deviceid
     497            pyev.detail = device.detail
     498            pyev.flags = device.flags
     499            pyev.window = int(device.child or device.event or device.root)
     500            pyev.x_root = device.root_x
     501            pyev.y_root = device.root_y
     502            pyev.x = device.event_x
     503            pyev.y = device.event_y
     504            #mask = []
     505            valuators = {}
     506            valuator = 0
     507            for i in range(device.valuators.mask_len*8):
     508                if device.valuators.mask[i>>3] & (1 << (i & 0x7)):
     509                    valuators[i] = device.valuators.values[valuator]
     510                    valuator += 1
     511            pyev.valuators = valuators
     512            buttons = []
     513            for i in range(device.buttons.mask_len):
     514                if device.buttons.mask[i>>3] & (1<< (i & 0x7)):
     515                    buttons.append(i)
     516            pyev.buttons = buttons
     517            state = []
     518            pyev.state = state
     519            pyev.modifiers = {
     520                "base"      : device.mods.base,
     521                "latched"   : device.mods.latched,
     522                "locked"    : device.mods.locked,
     523                "effective" : device.mods.effective,
     524                }
     525            #make it compatible with gdk events:
     526            pyev.state = device.mods.effective
     527        elif xi_type in (XI_RawKeyPress, XI_RawKeyRelease,
     528                         XI_RawButtonPress, XI_RawButtonRelease,
     529                         XI_RawMotion,
     530                         XI_RawTouchBegin, XI_RawTouchUpdate, XI_RawTouchEnd):
     531            raw = <XIRawEvent*> cookie.data
     532            valuators = {}
     533            raw_valuators = {}
     534            valuator = 0
     535            for i in range(raw.valuators.mask_len*8):
     536                if raw.valuators.mask[i>>3] & (1 << (i & 0x7)):
     537                    valuators[i] = raw.valuators.values[valuator]
     538                    raw_valuators[i] = raw.raw_values[valuator]
     539                    valuator += 1
     540            pyev.valuators = valuators
     541            pyev.raw_valuators = raw_valuators
     542        elif xi_type == XI_HierarchyChanged:
     543            pass
     544            #hierarchy_e = <XIHierarchyEvent*> cookie.data
     545            #pyev.flags = hierarchy_e.flags
     546            #info = {}
     547            #for i in range(hierarchy_e.num_info):
     548            #
     549        XFreeEventData(self.display, cookie)
     550        pyev.xid = pyev.window
     551        self.events.append(pyev)
     552
     553        handler = self.event_handlers.get(pyev.window, {}).get(event_name)
     554        log("parse_xi_event: %s, handler=%s", pyev, handler)
     555        if handler:
     556            handler(pyev)
     557        return None
     558
     559    def get_devices(self, show_all=True, show_disabled=False):
     560        global XI_USE
     561        cdef int ndevices, i, j
     562        cdef XIDeviceInfo *devices
     563        cdef XIDeviceInfo *device
     564        cdef XIAnyClassInfo *clazz
     565        if show_all:
     566            device_types = XIAllDevices
     567        else:
     568            device_types = XIAllMasterDevices
     569        devices = XIQueryDevice(self.display, device_types, &ndevices)
     570        dinfo = {}
     571        for i in range(ndevices):
     572            device = &devices[i]
     573            if not device.enabled and not show_disabled:
     574                continue
     575            info = {
     576                "name"          : device.name,
     577                "use"           : XI_USE.get(device.use, "unknown use: %i" % device.use),
     578                "attachment"    : device.attachment,
     579                "enabled"       : device.enabled,
     580                }
     581            classes = {}
     582            for j in range(device.num_classes):
     583                clazz = device.classes[j]
     584                classes[j] = self.get_class_info(clazz)
     585            info["classes"] = classes
     586            dinfo[device.deviceid] = info
     587        XIFreeDeviceInfo(devices)
     588        return dinfo
     589
     590    cdef get_class_info(self, XIAnyClassInfo *class_info):
     591        cdef int i
     592        cdef XIButtonClassInfo *button
     593        cdef XIKeyClassInfo *key
     594        cdef XIValuatorClassInfo *valuator
     595        cdef XIScrollClassInfo *scroll
     596        cdef XITouchClassInfo *touch
     597        info = {
     598            "type"      : CLASS_INFO.get(class_info.type, "unknown type: %i" % class_info.type),
     599            "sourceid"  : class_info.sourceid,
     600            }
     601        if class_info.type==XIButtonClass:
     602            button = <XIButtonClassInfo*> class_info
     603            buttons = []
     604            for i in range(button.num_buttons):
     605                if button.labels[i]>0:
     606                    buttons.append(self.XGetAtomName(button.labels[i]))
     607            info["buttons"] = buttons
     608            #XIButtonState state
     609        elif class_info.type==XIKeyClass:
     610            key = <XIKeyClassInfo*> class_info
     611            keys = []
     612            for i in range(key.num_keycodes):
     613                keys.append(key.keycodes[i])
     614        elif class_info.type==XIValuatorClass:
     615            valuator = <XIValuatorClassInfo*> class_info
     616            info.update({
     617                "number"    : valuator.number,
     618                "min"       : valuator.min,
     619                "max"       : valuator.max,
     620                "value"     : valuator.value,
     621                "resolution": valuator.resolution,
     622                "mode"      : valuator.mode,
     623                })
     624            if valuator.label:
     625                info["label"] = self.XGetAtomName(valuator.label)
     626        elif class_info.type==XIScrollClass:
     627            scroll = <XIScrollClassInfo*> class_info
     628            info.update({
     629                "number"        : scroll.number,
     630                "scroll-type"   : scroll.scroll_type,
     631                "increment"     : scroll.increment,
     632                "flags"         : scroll.flags,
     633                })
     634        elif class_info.type==XITouchClass:
     635            touch = <XITouchClassInfo*> class_info
     636            info.update({
     637                "mode"          : touch.mode,
     638                "num-touches"   : touch.num_touches,
     639                })
     640        return info
     641
     642
     643    def gdk_inject(self):
     644        self.get_xi_opcode()
     645        log.info("XInput Devices:")
     646        from xpra.util import print_nested_dict
     647        print_nested_dict(self.get_devices(), print_fn=log.info)
     648        self.register_parser()
     649        self.register_gdk_events()
     650        #self.select_xi2_events()
  • xpra/x11/gtk2/gdk_bindings.pyx

     
    10471047            #log("calling %s%s", parser, (d, <uintptr_t> &e.xcookie))
    10481048            pyev = parser(d, <uintptr_t> &e.xcookie)
    10491049            #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
    10521053            return pyev
    10531054        return None
    10541055
  • xpra/x11/x11_server_base.py

     
    717717        x, y = pos
    718718        X11Keyboard.xtest_fake_motion(screen_no, x, y)
    719719
    720     def do_process_mouse_common(self, proto, wid, pointer):
     720    def do_process_mouse_common(self, proto, wid, pointer, device, *args):
    721721        if self.readonly:
    722722            return
    723723        pos = self.root_window.get_pointer()[:2]
     
    741741
    742742    def do_process_button_action(self, proto, wid, button, pressed, pointer, modifiers, *args):
    743743        self._update_modifiers(proto, wid, modifiers)
     744        #TODO: pass extra args
    744745        self._process_mouse_common(proto, wid, pointer)
    745746        mouselog("xtest_fake_button(%s, %s) at %s", button, pressed, pointer)
    746747        try: