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-v5.patch

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

updated patch

  • 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
  • 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:
     
    934935
    935936    def do_motion_notify_event(self, event):
    936937        if self.moveresize_event:
    937             x_root, y_root, direction, button, start_buttons, wx, wy, ww, wh = self.moveresize_event
    938             dirstr = MOVERESIZE_DIRECTION_STRING.get(direction, direction)
    939             buttons = self._event_buttons(event)
    940             if start_buttons is None:
    941                 #first time around, store the buttons
    942                 start_buttons = buttons
    943                 self.moveresize_event[4] = buttons
    944             if (button>0 and button not in buttons) or (button==0 and start_buttons!=buttons):
    945                 geomlog("%s for window button %i is no longer pressed (buttons=%s) cancelling moveresize", dirstr, button, buttons)
    946                 self.moveresize_event = None
    947             else:
    948                 x = event.x_root
    949                 y = event.y_root
    950                 dx = x-x_root
    951                 dy = y-y_root
    952                 #clamp resizing using size hints,
    953                 #or sane defaults: minimum of (1x1) and maximum of (2*15x2*25)
    954                 minw = self.geometry_hints.get("min_width", 1)
    955                 minh = self.geometry_hints.get("min_height", 1)
    956                 maxw = self.geometry_hints.get("max_width", 2**15)
    957                 maxh = self.geometry_hints.get("max_height", 2**15)
    958                 geomlog("%s: min=%ix%i, max=%ix%i, window=%ix%i, delta=%ix%i", dirstr, minw, minh, maxw, maxh, ww, wh, dx, dy)
    959                 if direction in (MOVERESIZE_SIZE_BOTTOMRIGHT, MOVERESIZE_SIZE_BOTTOM, MOVERESIZE_SIZE_BOTTOMLEFT):
    960                     #height will be set to: wh+dy
    961                     dy = max(minh-wh, dy)
    962                     dy = min(maxh-wh, dy)
    963                 elif direction in (MOVERESIZE_SIZE_TOPRIGHT, MOVERESIZE_SIZE_TOP, MOVERESIZE_SIZE_TOPLEFT):
    964                     #height will be set to: wh-dy
    965                     dy = min(wh-minh, dy)
    966                     dy = max(wh-maxh, dy)
    967                 if direction in (MOVERESIZE_SIZE_BOTTOMRIGHT, MOVERESIZE_SIZE_RIGHT, MOVERESIZE_SIZE_TOPRIGHT):
    968                     #width will be set to: ww+dx
    969                     dx = max(minw-ww, dx)
    970                     dx = min(maxw-ww, dx)
    971                 elif direction in (MOVERESIZE_SIZE_BOTTOMLEFT, MOVERESIZE_SIZE_LEFT, MOVERESIZE_SIZE_TOPLEFT):
    972                     #width will be set to: ww-dx
    973                     dx = min(ww-minw, dx)
    974                     dx = max(ww-maxw, dx)
    975                 #calculate move + resize:
    976                 if direction==MOVERESIZE_MOVE:
    977                     data = (wx+dx, wy+dy), None
    978                 elif direction==MOVERESIZE_SIZE_BOTTOMRIGHT:
    979                     data = None, (ww+dx, wh+dy)
    980                 elif direction==MOVERESIZE_SIZE_BOTTOM:
    981                     data = None, (ww, wh+dy)
    982                 elif direction==MOVERESIZE_SIZE_BOTTOMLEFT:
    983                     data = (wx+dx, wy), (ww-dx, wh+dy)
    984                 elif direction==MOVERESIZE_SIZE_RIGHT:
    985                     data = None, (ww+dx, wh)
    986                 elif direction==MOVERESIZE_SIZE_LEFT:
    987                     data = (wx+dx, wy), (ww-dx, wh)
    988                 elif direction==MOVERESIZE_SIZE_TOPRIGHT:
    989                     data = (wx, wy+dy), (ww+dx, wh-dy)
    990                 elif direction==MOVERESIZE_SIZE_TOP:
    991                     data = (wx, wy+dy), (ww, wh-dy)
    992                 elif direction==MOVERESIZE_SIZE_TOPLEFT:
    993                     data = (wx+dx, wy+dy), (ww-dx, wh-dy)
    994                 else:
    995                     #not handled yet!
    996                     data = None
    997                 geomlog("%s for window %ix%i: started at %s, now at %s, delta=%s, button=%s, buttons=%s, data=%s", dirstr, ww, wh, (x_root, y_root), (x, y), (dx, dy), button, buttons, data)
    998                 if data:
    999                     #modifying the window is slower than moving the pointer,
    1000                     #do it via a timer to batch things together
    1001                     self.moveresize_data = data
    1002                     if self.moveresize_timer is None:
    1003                         self.moveresize_timer = self.timeout_add(20, self.do_moveresize)
     938            self.motion_moveresize(event)
    1004939        ClientWindowBase.do_motion_notify_event(self, event)
    1005940
     941    def motion_moveresize(self, event):
     942        x_root, y_root, direction, button, start_buttons, wx, wy, ww, wh = self.moveresize_event
     943        dirstr = MOVERESIZE_DIRECTION_STRING.get(direction, direction)
     944        buttons = self._event_buttons(event)
     945        if start_buttons is None:
     946            #first time around, store the buttons
     947            start_buttons = buttons
     948            self.moveresize_event[4] = buttons
     949        if (button>0 and button not in buttons) or (button==0 and start_buttons!=buttons):
     950            geomlog("%s for window button %i is no longer pressed (buttons=%s) cancelling moveresize", dirstr, button, buttons)
     951            self.moveresize_event = None
     952        else:
     953            x = event.x_root
     954            y = event.y_root
     955            dx = x-x_root
     956            dy = y-y_root
     957            #clamp resizing using size hints,
     958            #or sane defaults: minimum of (1x1) and maximum of (2*15x2*25)
     959            minw = self.geometry_hints.get("min_width", 1)
     960            minh = self.geometry_hints.get("min_height", 1)
     961            maxw = self.geometry_hints.get("max_width", 2**15)
     962            maxh = self.geometry_hints.get("max_height", 2**15)
     963            geomlog("%s: min=%ix%i, max=%ix%i, window=%ix%i, delta=%ix%i", dirstr, minw, minh, maxw, maxh, ww, wh, dx, dy)
     964            if direction in (MOVERESIZE_SIZE_BOTTOMRIGHT, MOVERESIZE_SIZE_BOTTOM, MOVERESIZE_SIZE_BOTTOMLEFT):
     965                #height will be set to: wh+dy
     966                dy = max(minh-wh, dy)
     967                dy = min(maxh-wh, dy)
     968            elif direction in (MOVERESIZE_SIZE_TOPRIGHT, MOVERESIZE_SIZE_TOP, MOVERESIZE_SIZE_TOPLEFT):
     969                #height will be set to: wh-dy
     970                dy = min(wh-minh, dy)
     971                dy = max(wh-maxh, dy)
     972            if direction in (MOVERESIZE_SIZE_BOTTOMRIGHT, MOVERESIZE_SIZE_RIGHT, MOVERESIZE_SIZE_TOPRIGHT):
     973                #width will be set to: ww+dx
     974                dx = max(minw-ww, dx)
     975                dx = min(maxw-ww, dx)
     976            elif direction in (MOVERESIZE_SIZE_BOTTOMLEFT, MOVERESIZE_SIZE_LEFT, MOVERESIZE_SIZE_TOPLEFT):
     977                #width will be set to: ww-dx
     978                dx = min(ww-minw, dx)
     979                dx = max(ww-maxw, dx)
     980            #calculate move + resize:
     981            if direction==MOVERESIZE_MOVE:
     982                data = (wx+dx, wy+dy), None
     983            elif direction==MOVERESIZE_SIZE_BOTTOMRIGHT:
     984                data = None, (ww+dx, wh+dy)
     985            elif direction==MOVERESIZE_SIZE_BOTTOM:
     986                data = None, (ww, wh+dy)
     987            elif direction==MOVERESIZE_SIZE_BOTTOMLEFT:
     988                data = (wx+dx, wy), (ww-dx, wh+dy)
     989            elif direction==MOVERESIZE_SIZE_RIGHT:
     990                data = None, (ww+dx, wh)
     991            elif direction==MOVERESIZE_SIZE_LEFT:
     992                data = (wx+dx, wy), (ww-dx, wh)
     993            elif direction==MOVERESIZE_SIZE_TOPRIGHT:
     994                data = (wx, wy+dy), (ww+dx, wh-dy)
     995            elif direction==MOVERESIZE_SIZE_TOP:
     996                data = (wx, wy+dy), (ww, wh-dy)
     997            elif direction==MOVERESIZE_SIZE_TOPLEFT:
     998                data = (wx+dx, wy+dy), (ww-dx, wh-dy)
     999            else:
     1000                #not handled yet!
     1001                data = None
     1002            geomlog("%s for window %ix%i: started at %s, now at %s, delta=%s, button=%s, buttons=%s, data=%s", dirstr, ww, wh, (x_root, y_root), (x, y), (dx, dy), button, buttons, data)
     1003            if data:
     1004                #modifying the window is slower than moving the pointer,
     1005                #do it via a timer to batch things together
     1006                self.moveresize_data = data
     1007                if self.moveresize_timer is None:
     1008                    self.moveresize_timer = self.timeout_add(20, self.do_moveresize)
     1009
    10061010    def do_moveresize(self):
    10071011        self.moveresize_timer = None
    10081012        mrd = self.moveresize_data
  • 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:
  • 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_ENHANCERS = []
     511def add_window_hooks(window):
     512    global WINDOW_ENHANCERS
     513    for x in WINDOW_ENHANCERS:
     514        x(window)
     515    log.warn("add_window_hooks(%s) added %s", window, WINDOW_ENHANCERS)
     516
     517
    502518def get_info():
    503519    from xpra.platform.gui import get_info_base
    504520    i = get_info_base()
     
    516532    return i
    517533
    518534
     535class XI2_Window(object):
     536    def __init__(self, window):
     537        log.warn("XI2_Window(%s)", window)
     538        self.window = window
     539        self.do_motion_notify_event = window.do_motion_notify_event
     540        window.do_motion_notify_event = self._do_motion_notify_event
     541
     542    def get_parent_windows(self, oxid):
     543        windows = [oxid]
     544        X11Window = X11WindowBindings()
     545        root = X11Window.getDefaultRootWindow()
     546        xid = oxid
     547        while True:
     548            xid = X11Window.getParent(xid)
     549            if xid==0 or xid==root:
     550                break
     551            windows.append(xid)
     552        log("get_parent_windows(%#x)=%s", oxid, csv(hex(x) for x in windows))
     553        return windows
     554
     555    def _do_motion_notify_event(self, event):
     556        if self.window.moveresize_event:
     557            self.window.motion_moveresize(event)
     558            self.do_motion_notify_event(event)
     559            return
     560        if self.window._client.readonly:
     561            return
     562        log("do_motion_notify_event(%s)", event)
     563        #TODO: cache hierarchy and query again on structure notify:
     564        parents = self.get_parent_windows(self.window.get_window().xid)
     565        #find the motion events in the xi2 event list:
     566        XI2 = X11XI2Bindings()
     567        events = XI2.find_events("XI_Motion", parents)
     568        log.warn("events=%s", events)
     569        XI2.reset_events()
     570        if not events:
     571            self.do_motion_notify_event(event)
     572            return
     573        def intscaled(f):
     574            return int(f*1000000), 1000000
     575        oevent = event
     576        for event in events:
     577            #TODO: populate state from XI bindings
     578            event.state = oevent.state
     579            pointer, modifiers, buttons = self.window._pointer_modifiers(event)
     580            wid = self.window.get_mouse_event_wid(*pointer)
     581            mouselog("do_motion_notify_event(%s) wid=%s / focus=%s / window wid=%i, device=%s, pointer=%s, modifiers=%s, buttons=%s", event, wid, self.window._client._focused, self.window._id, event.device, pointer, modifiers, buttons)
     582            packet = ["pointer-position", wid, pointer, modifiers, buttons,
     583                      event.device]
     584            for x in ("x", "y", "x_root", "y_root"):
     585                v = intscaled(getattr(event, x))
     586                packet.append(v)
     587            self.window._client.send_mouse_position(packet)
     588
     589
    519590class ClientExtras(object):
    520591    def __init__(self, client, opts):
    521592        self.client = client
     
    528599        self.x11_filter = None
    529600        if client.xsettings_enabled:
    530601            self.setup_xprops()
     602        if client.input_devices=="xi":
     603            self.setup_xi()
    531604        self.setup_dbus_signals()
    532605
    533606    def ready(self):
     
    565638                bus._clean_up_signal_match(self.upower_sleeping_match)
    566639            if self.login1_match:
    567640                bus._clean_up_signal_match(self.login1_match)
     641        global WINDOW_METHOD_OVERRIDES
     642        WINDOW_METHOD_OVERRIDES = {}
    568643
    569644    def resuming_callback(self, *args):
    570645        eventlog("resuming_callback%s", args)
     
    647722        except ImportError as e:
    648723            log.error("failed to load X11 properties/settings bindings: %s - root window properties will not be propagated", e)
    649724
     725    def setup_xi(self):
     726        from xpra.gtk_common.error import xsync
     727        def enable_xi2():
     728            try:
     729                with xsync:
     730                    self.init_x11_filter()
     731                    XI2 = X11XI2Bindings()
     732                    XI2.select_xi2_events()
     733            except Exception as e:
     734                log("enable_xi2()", exc_info=True)
     735                log.error("Error: cannot enable XI2 events")
     736                log.error(" %s", e)
     737            else:
     738                #register our enhanced event handlers:
     739                self.add_xi2_method_overrides()
     740        with xsync:
     741            try:
     742                assert X11WindowBindings, "no X11 window bindings"
     743                assert X11XI2Bindings, "no XI2 window bindings"
     744                X11XI2Bindings().gdk_inject()
     745                #this would trigger warnings with our temporary opengl windows:
     746                #only enable it after we have connected:
     747                self.client.after_handshake(enable_xi2)
     748            except Exception as e:
     749                log("setup_xi()", exc_info=True)
     750                log.error("Error: failed to load the XI2 bindings")
     751                log.error(" %s", e)
     752
     753    def add_xi2_method_overrides(self):
     754        global WINDOW_ENHANCERS
     755        WINDOW_ENHANCERS = [XI2_Window]
     756
     757
    650758    def _get_xsettings(self):
    651759        try:
    652760            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
     324    def __init__(self):
     325        self.opcode = -1
     326        self.reset_events()
     327
     328    def __repr__(self):
     329        return "X11XI2Bindings(%s)" % self.display_name
     330
     331    def reset_events(self):
     332        self.events = collections.deque(maxlen=100)
     333
     334    def find_events(self, event_name, windows):
     335        cdef Window found = 0
     336        cdef Window window
     337        matches = []
     338        for x in reversed(self.events):
     339            window = x.xid
     340            if x.name==event_name and ((found>0 and found==window) or (found==0 and window in windows)):
     341                matches.append(x)
     342                found = window
     343            elif found:
     344                break
     345        return matches
     346
     347    cdef int get_xi_opcode(self, int major=2, int minor=2):
     348        if self.opcode!=-1:
     349            return self.opcode
     350        cdef int opcode, event, error
     351        if not XQueryExtension(self.display, "XInputExtension", &opcode, &event, &error):
     352            log.warn("Warning: XI2 events are not supported")
     353            self.opcode = 0
     354            return 0
     355        cdef int rmajor = major, rminor = minor
     356        cdef int rc = XIQueryVersion(self.display, &rmajor, &rminor)
     357        if rc == BadRequest:
     358            log.warn("Warning: no XI2 %i.%i support,", major, minor)
     359            log.warn(" server supports version %i.%i only", rmajor, rminor)
     360            self.opcode = 0
     361            return 0
     362        elif rc:
     363            log.warn("Warning: Xlib bug querying XI2, code %i", rc)
     364            self.opcode = 0
     365            return 0
     366        self.opcode = opcode
     367        log("get_xi_opcode%s=%i", (major, minor), opcode)
     368        return opcode
     369
     370    cdef register_parser(self):
     371        log("register_parser()")
     372        if self.opcode>0:
     373            from xpra.x11.gtk2.gdk_bindings import add_x_event_parser
     374            add_x_event_parser(self.opcode, self.parse_xi_event)
     375
     376    cdef register_gdk_events(self):
     377        log("register_gdk_events()")
     378        if self.opcode<=0:
     379            return
     380        global XI_EVENT_NAMES
     381        from xpra.x11.gtk2.gdk_bindings import add_x_event_signal, add_x_event_type_name
     382        for e, xi_event_name in {
     383            XI_DeviceChanged    : "device-changed",
     384            XI_KeyPress         : "key-press",
     385            XI_KeyRelease       : "key-release",
     386            XI_ButtonPress      : "button-press",
     387            XI_ButtonRelease    : "button-release",
     388            XI_Motion           : "motion",
     389            XI_Enter            : "enter",
     390            XI_Leave            : "leave",
     391            XI_FocusIn          : "focus-in",
     392            XI_FocusOut         : "focus-out",
     393            XI_HierarchyChanged : "focus-changed",
     394            XI_PropertyEvent    : "property-event",
     395            XI_RawKeyPress      : "raw-key-press",
     396            XI_RawKeyRelease    : "raw-key-release",
     397            XI_RawButtonPress   : "raw-button-press",
     398            XI_RawButtonRelease : "raw-button-release",
     399            XI_RawMotion        : "raw-motion",
     400            XI_TouchBegin       : "touch-begin",
     401            XI_TouchUpdate      : "touch-update",
     402            XI_TouchEnd         : "touch-end",
     403            XI_TouchOwnership   : "touch-ownership",
     404            XI_RawTouchBegin    : "raw-touch-begin",
     405            XI_RawTouchUpdate   : "raw-touch-update",
     406            XI_RawTouchEnd      : "raw-touch-end",
     407            }.items():
     408            event = self.opcode+e
     409            add_x_event_signal(event, ("xi-%s" % xi_event_name, None))
     410            name = XI_EVENT_NAMES[e]
     411            add_x_event_type_name(event, name)
     412
     413    def select_xi2_events(self):
     414        cdef Window win = XDefaultRootWindow(self.display)
     415        log("select_xi2_events() root window=%#x", win)
     416        assert XI_LASTEVENT<MAX_XI_EVENTS, "bug: source needs to be updated, XI_LASTEVENT=%i" % XI_LASTEVENT
     417        cdef XIEventMask evmasks[1]
     418        cdef unsigned char mask1[XI_EVENT_MASK_SIZE]
     419        memset(mask1, 0, XI_EVENT_MASK_SIZE)
     420        #define XISetMask(ptr, event)   (((unsigned char*)(ptr))[(event)>>3] |=  (1 << ((event) & 7)))
     421        #XISetMask(mask1, XI_RawMotion)
     422        for e in (
     423            XI_KeyPress, XI_KeyRelease,
     424            XI_Motion,
     425            XI_HierarchyChanged,
     426            XI_ButtonPress, XI_ButtonRelease,
     427            XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd,
     428            ):
     429            mask1[e>>3] |= (1<< (e & 0x7))
     430        evmasks[0].deviceid = XIAllDevices  #XIAllMasterDevices    #XIAllDevices
     431        evmasks[0].mask_len = XI_EVENT_MASK_SIZE
     432        evmasks[0].mask = mask1
     433        XISelectEvents(self.display, win, evmasks, 1)
     434        XFlush(self.display)
     435
     436    def parse_xi_event(self, display, uintptr_t _cookie):
     437        log("parse_xi_event(%s)", _cookie)
     438        cdef XGenericEventCookie *cookie = <XGenericEventCookie*> _cookie
     439        cdef XIDeviceEvent *device_e
     440        cdef XIHierarchyEvent * hierarchy_e
     441        cdef XIEvent *xie
     442        cdef XIRawEvent *raw
     443        cdef int i = 0, j = 0
     444        if not XGetEventData(self.display, cookie):
     445            return None
     446        xie = <XIEvent*> cookie.data
     447        device_e = <XIDeviceEvent*> cookie.data
     448        cdef int xi_type = cookie.evtype
     449        etype = self.opcode+xi_type
     450        global XI_EVENT_NAMES
     451        event_name = XI_EVENT_NAMES.get(xi_type)
     452        if not event_name:
     453            log("unknown XI2 event code: %i", xi_type)
     454            return None
     455
     456        pyev = X11Event(event_name)
     457        pyev.type = etype
     458        pyev.display = display
     459        pyev.send_event = bool(xie.send_event)
     460        pyev.serial = xie.serial
     461        pyev.time = int(xie.time)
     462        pyev.window = XDefaultRootWindow(self.display)
     463
     464        if xi_type in (XI_KeyPress, XI_KeyRelease,
     465                       XI_ButtonPress, XI_ButtonRelease,
     466                       XI_Motion,
     467                       XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd):
     468            device = <XIDeviceEvent*> cookie.data
     469            #pyev.source = device.sourceid    #always 0
     470            pyev.device = device.deviceid
     471            pyev.detail = device.detail
     472            pyev.flags = device.flags
     473            pyev.window = int(device.child or device.event or device.root)
     474            pyev.x_root = device.root_x
     475            pyev.y_root = device.root_y
     476            pyev.x = device.event_x
     477            pyev.y = device.event_y
     478            #mask = []
     479            #values = []
     480            #raw_values = []
     481            #if False:
     482            #    for i in range(device.valuators.mask_len):
     483            #        if device.valuators.mask[i//8] & (1 << (i & 0x7)):
     484            #            mask.append(i)
     485            #            values.append(j)
     486            #            raw_values.append(device.valuators.values[j])
     487            #            j += 1
     488            #pyev.valuators = {
     489            #    "mask"      : mask,
     490            #    "values"    : values,
     491            #    }
     492        elif xi_type == XI_HierarchyChanged:
     493            pass
     494            #hierarchy_e = <XIHierarchyEvent*> cookie.data
     495            #pyev.flags = hierarchy_e.flags
     496            #info = {}
     497            #for i in range(hierarchy_e.num_info):
     498            #
     499        XFreeEventData(self.display, cookie)
     500        pyev.xid = pyev.window
     501        self.events.append(pyev)
     502        log("parse_xi_event: %s", pyev)
     503        return pyev
     504
     505    def get_devices(self, show_all=True, show_disabled=False):
     506        global XI_USE
     507        cdef int ndevices, i, j
     508        cdef XIDeviceInfo *devices
     509        cdef XIDeviceInfo *device
     510        cdef XIAnyClassInfo *clazz
     511        if show_all:
     512            device_types = XIAllDevices
     513        else:
     514            device_types = XIAllMasterDevices
     515        devices = XIQueryDevice(self.display, device_types, &ndevices)
     516        dinfo = {}
     517        for i in range(ndevices):
     518            device = &devices[i]
     519            if not device.enabled and not show_disabled:
     520                continue
     521            info = {
     522                "name"          : device.name,
     523                "use"           : XI_USE.get(device.use, "unknown use: %i" % device.use),
     524                "attachment"    : device.attachment,
     525                "enabled"       : device.enabled,
     526                }
     527            classes = {}
     528            for j in range(device.num_classes):
     529                clazz = device.classes[j]
     530                classes[j] = self.get_class_info(clazz)
     531            info["classes"] = classes
     532            dinfo[device.deviceid] = info
     533        XIFreeDeviceInfo(devices)
     534        return dinfo
     535
     536    cdef get_class_info(self, XIAnyClassInfo *class_info):
     537        cdef int i
     538        cdef XIButtonClassInfo *button
     539        cdef XIKeyClassInfo *key
     540        cdef XIValuatorClassInfo *valuator
     541        cdef XIScrollClassInfo *scroll
     542        cdef XITouchClassInfo *touch
     543        info = {
     544            "type"      : CLASS_INFO.get(class_info.type, "unknown type: %i" % class_info.type),
     545            "sourceid"  : class_info.sourceid,
     546            }
     547        if class_info.type==XIButtonClass:
     548            button = <XIButtonClassInfo*> class_info
     549            buttons = []
     550            for i in range(button.num_buttons):
     551                if button.labels[i]>0:
     552                    buttons.append(self.XGetAtomName(button.labels[i]))
     553            info["buttons"] = buttons
     554            #XIButtonState state
     555        elif class_info.type==XIKeyClass:
     556            key = <XIKeyClassInfo*> class_info
     557            keys = []
     558            for i in range(key.num_keycodes):
     559                keys.append(key.keycodes[i])
     560        elif class_info.type==XIValuatorClass:
     561            valuator = <XIValuatorClassInfo*> class_info
     562            info.update({
     563                "number"    : valuator.number,
     564                "min"       : valuator.min,
     565                "max"       : valuator.max,
     566                "value"     : valuator.value,
     567                "resolution": valuator.resolution,
     568                "mode"      : valuator.mode,
     569                })
     570            if valuator.label:
     571                info["label"] = self.XGetAtomName(valuator.label)
     572        elif class_info.type==XIScrollClass:
     573            scroll = <XIScrollClassInfo*> class_info
     574            info.update({
     575                "number"        : scroll.number,
     576                "scroll-type"   : scroll.scroll_type,
     577                "increment"     : scroll.increment,
     578                "flags"         : scroll.flags,
     579                })
     580        elif class_info.type==XITouchClass:
     581            touch = <XITouchClassInfo*> class_info
     582            info.update({
     583                "mode"          : touch.mode,
     584                "num-touches"   : touch.num_touches,
     585                })
     586        return info
     587
     588
     589    def gdk_inject(self):
     590        self.get_xi_opcode()
     591        log.info("XInput Devices:")
     592        from xpra.util import print_nested_dict
     593        print_nested_dict(self.get_devices(), print_fn=log.info)
     594        self.register_parser()
     595        self.register_gdk_events()
     596        #self.select_xi2_events()
  • xpra/x11/x11_server_base.py

     
    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: