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

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

updated patch: export all device properties

  • 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.error("no X11 bindings", exc_info=True)
     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                    assert X11WindowBindings, "no X11 window bindings"
     780                    assert X11XI2Bindings, "no XI2 window bindings"
     781                    X11XI2Bindings().gdk_inject()
     782                    self.init_x11_filter()
     783                    XI2 = X11XI2Bindings()
     784                    XI2.select_xi2_events()
     785            except Exception as e:
     786                log("enable_xi2()", exc_info=True)
     787                log.error("Error: cannot enable XI2 events")
     788                log.error(" %s", e)
     789            else:
     790                #register our enhanced event handlers:
     791                self.add_xi2_method_overrides()
     792        with xsync:
     793            try:
     794                #this would trigger warnings with our temporary opengl windows:
     795                #only enable it after we have connected:
     796                self.client.after_handshake(enable_xi2)
     797            except Exception as e:
     798                log("setup_xi()", exc_info=True)
     799                log.error("Error: failed to load the XI2 bindings")
     800                log.error(" %s", e)
     801
     802    def add_xi2_method_overrides(self):
     803        global WINDOW_ADD_HOOKS
     804        WINDOW_ADD_HOOKS = [XI2_Window]
     805
     806
    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.pxd

     
    1515    cdef Display * display
    1616    cdef char * display_name
    1717    cdef Atom xatom(self, str_or_int)
     18
     19    cdef munge_packed_ints_to_longs(self, data)
     20    cdef munge_packed_longs_to_ints(self, data)
    1821#    def get_error_text(self, code)
  • xpra/x11/bindings/core_bindings.pyx

     
    11# This file is part of Xpra.
    22# Copyright (C) 2008, 2009 Nathaniel Smith <njs@pobox.com>
    3 # Copyright (C) 2010-2016 Antoine Martin <antoine@devloop.org.uk>
     3# Copyright (C) 2010-2017 Antoine Martin <antoine@devloop.org.uk>
    44# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
    55# later version. See the file COPYING for details.
    66
    77import os
    88import time
     9import struct
    910
    1011from xpra.util import dump_exc, envbool
    1112from xpra.os_util import strtobytes
     
    3334    ctypedef int Bool
    3435
    3536    Atom XInternAtom(Display * display, char * atom_name, Bool only_if_exists)
     37    char *XGetAtomName(Display *display, Atom atom)
    3638
    3739    int XFree(void * data)
    3840
     
    4749from display_source cimport get_display
    4850from display_source import get_display_name
    4951
     52
     53cdef _munge_packed_ints_to_longs(data):
     54    assert len(data) % sizeof(int) == 0
     55    n = len(data) / sizeof(int)
     56    format_from = "@" + "i" * n
     57    format_to = "@" + "l" * n
     58    return struct.pack(format_to, *struct.unpack(format_from, data))
     59
     60cdef _munge_packed_longs_to_ints(data):
     61    assert len(data) % sizeof(long) == 0
     62    n = len(data) / sizeof(long)
     63    format_from = "@" + "l" * n
     64    format_to = "@" + "i" * n
     65    #import binascii
     66    #log.info("_munge_packed_longs_to_ints(%s) hex data=%s", data, binascii.hexlify(data))
     67    return struct.pack(format_to, *struct.unpack(format_from, data))
     68
     69
    5070cdef _X11CoreBindings singleton = None
    5171def X11CoreBindings():
    5272    global singleton
     
    83103    def get_xatom(self, str_or_int):
    84104        return self.xatom(str_or_int)
    85105
     106    def XGetAtomName(self, Atom atom):
     107        v = XGetAtomName(self.display, atom)
     108        return v[:]
     109
     110    cdef munge_packed_ints_to_longs(self, data):
     111        return _munge_packed_ints_to_longs(data)
     112
     113    cdef munge_packed_longs_to_ints(self, data):
     114        return _munge_packed_longs_to_ints(data)
     115
     116
    86117    def get_error_text(self, code):
    87118        assert self.display!=NULL, "display is closed"
    88119        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
     
    357356    void XDamageSubtract(Display *, Damage, XserverRegion repair, XserverRegion parts)
    358357
    359358
    360 
    361 
    362 
    363 cpdef _munge_packed_ints_to_longs(data):
    364     assert len(data) % sizeof(int) == 0
    365     n = len(data) / sizeof(int)
    366     format_from = "@" + "i" * n
    367     format_to = "@" + "l" * n
    368     return struct.pack(format_to, *struct.unpack(format_from, data))
    369 
    370 cpdef _munge_packed_longs_to_ints(data):
    371     assert len(data) % sizeof(long) == 0
    372     n = len(data) / sizeof(long)
    373     format_from = "@" + "l" * n
    374     format_to = "@" + "i" * n
    375     return struct.pack(format_to, *struct.unpack(format_from, data))
    376 
    377 
    378 
    379 
    380 
    381 
    382359cdef long cast_to_long(i):
    383360    if i < 0:
    384361        return <long>i
     
    461438        return XDefaultRootWindow(self.display)
    462439
    463440
    464     cpdef XGetAtomName(self, Atom atom):
    465         v = XGetAtomName(self.display, atom)
    466         return v[:]
    467 
    468441    def MapWindow(self, Window xwindow):
    469442        XMapWindow(self.display, xwindow)
    470443
     
    905878                                     0,
    906879                                     # This argument has to be divided by 4.  Thus
    907880                                     # speaks the spec.
    908                                      buffer_size / 4,
     881                                     buffer_size // 4,
    909882                                     False,
    910883                                     xreq_type, &xactual_type,
    911884                                     &actual_format, &nitems, &bytes_after, &prop)
     
    937910        data = (<char *> prop)[:nbytes]
    938911        XFree(prop)
    939912        if actual_format == 32:
    940             return _munge_packed_longs_to_ints(data)
     913            return self.munge_packed_longs_to_ints(data)
    941914        else:
    942915            return data
    943916
     
    985958        assert (len(data) % (format / 8)) == 0, "size of data is not a multiple of %s" % (format/8)
    986959        cdef int nitems = len(data) / (format / 8)
    987960        if format == 32:
    988             data = _munge_packed_ints_to_longs(data)
     961            data = self.munge_packed_ints_to_longs(data)
    989962        cdef char * data_str
    990963        data_str = data
    991964        #print("XChangeProperty(%#x, %s, %s) data=%s" % (xwindow, property, value, str([hex(x) for x in data_str])))
  • xpra/x11/bindings/xi2_bindings.pyx

     
     1# This file is part of Xpra.
     2# Copyright (C) 2017 Antoine Martin <antoine@devloop.org.uk>
     3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
     4# later version. See the file COPYING for details.
     5
     6import os
     7import time
     8import struct
     9import collections
     10
     11from xpra.log import Logger
     12log = Logger("x11", "bindings", "xinput")
     13
     14from xpra.x11.gtk2.common import X11Event
     15
     16from libc.stdint cimport uintptr_t
     17
     18
     19###################################
     20# Headers, python magic
     21###################################
     22cdef extern from "string.h":
     23    void* memset(void * ptr, int value, size_t num)
     24
     25cdef extern from "X11/Xutil.h":
     26    pass
     27
     28######
     29# Xlib primitives and constants
     30######
     31
     32include "constants.pxi"
     33ctypedef unsigned long CARD32
     34
     35cdef extern from "X11/Xlib.h":
     36    ctypedef struct Display:
     37        pass
     38
     39    ctypedef CARD32 XID
     40    ctypedef int Bool
     41    ctypedef int Status
     42    ctypedef CARD32 Atom
     43    ctypedef XID Window
     44    ctypedef CARD32 Time
     45
     46    ctypedef struct XGenericEventCookie:
     47        int            type     # of event. Always GenericEvent
     48        unsigned long  serial
     49        Bool           send_event
     50        Display        *display
     51        int            extension    #major opcode of extension that caused the event
     52        int            evtype       #actual event type
     53        unsigned int   cookie
     54        void           *data
     55
     56    int XIAnyPropertyType
     57
     58    Atom XInternAtom(Display * display, char * atom_name, Bool only_if_exists)
     59    int XFree(void * data)
     60
     61    Bool XQueryExtension(Display * display, char *name,
     62                         int *major_opcode_return, int *first_event_return, int *first_error_return)
     63
     64    Bool XGetEventData(Display *display, XGenericEventCookie *cookie)
     65    void XFreeEventData(Display *display, XGenericEventCookie *cookie)
     66
     67    Window XDefaultRootWindow(Display * display)
     68
     69    Bool XQueryPointer(Display *display, Window w, Window *root_return, Window *child_return, int *root_x_return, int *root_y_return,
     70                       int *win_x_return, int *win_y_return, unsigned int *mask_return)
     71    int XFlush(Display *dpy)
     72
     73cdef extern from "X11/extensions/XInput2.h":
     74    int XI_LASTEVENT
     75    int XI_DeviceChanged
     76    int XI_KeyPress
     77    int XI_KeyRelease
     78    int XI_ButtonPress
     79    int XI_ButtonRelease
     80    int XI_Motion
     81    int XI_Enter
     82    int XI_Leave
     83    int XI_FocusIn
     84    int XI_FocusOut
     85    int XI_HierarchyChanged
     86    int XI_PropertyEvent
     87    int XI_RawKeyPress
     88    int XI_RawKeyRelease
     89    int XI_RawButtonPress
     90    int XI_RawButtonRelease
     91    int XI_RawMotion
     92    int XI_TouchBegin
     93    int XI_TouchUpdate
     94    int XI_TouchEnd
     95    int XI_TouchOwnership
     96    int XI_RawTouchBegin
     97    int XI_RawTouchUpdate
     98    int XI_RawTouchEnd
     99
     100    int XIMasterPointer
     101    int XIMasterKeyboard
     102    int XISlavePointer
     103    int XISlaveKeyboard
     104    int XIFloatingSlave
     105
     106    int XIButtonClass
     107    int XIKeyClass
     108    int XIValuatorClass
     109    int XIScrollClass
     110    int XITouchClass   
     111
     112    int XIAllDevices
     113    int XIAllMasterDevices
     114
     115    ctypedef struct XIValuatorState:
     116        int           mask_len
     117        unsigned char *mask
     118        double        *values
     119
     120    ctypedef struct XIEvent:
     121        int           type
     122        unsigned long serial
     123        Bool          send_event
     124        Display       *display
     125        int           extension
     126        int           evtype
     127        Time          time
     128
     129    ctypedef struct XIRawEvent:
     130        int           type      #GenericEvent
     131        unsigned long serial
     132        Bool          send_event
     133        Display       *display
     134        int           extension #XI extension offset
     135        int           evtype    #XI_RawKeyPress, XI_RawKeyRelease, etc
     136        Time          time
     137        int           deviceid
     138        int           sourceid
     139        int           detail
     140        int           flags
     141        XIValuatorState valuators
     142        double        *raw_values
     143
     144    ctypedef struct XIButtonState:
     145        int           mask_len
     146        unsigned char *mask
     147
     148    ctypedef struct XIModifierState:
     149        int    base
     150        int    latched
     151        int    locked
     152        int    effective
     153
     154    ctypedef XIModifierState XIGroupState
     155
     156    ctypedef struct XIDeviceEvent:
     157        int           type
     158        unsigned long serial
     159        Bool          send_event
     160        Display       *display
     161        int           extension
     162        int           evtype
     163        Time          time
     164        int           deviceid
     165        int           sourceid
     166        int           detail
     167        Window        root
     168        Window        event
     169        Window        child
     170        double        root_x
     171        double        root_y
     172        double        event_x
     173        double        event_y
     174        int           flags
     175        XIButtonState       buttons
     176        XIValuatorState     valuators
     177        XIModifierState     mods
     178        XIGroupState        group
     179
     180    ctypedef struct XIHierarchyInfo:
     181        int           deviceid
     182        int           attachment
     183        int           use
     184        Bool          enabled
     185        int           flags
     186
     187    ctypedef struct XIHierarchyEvent:
     188        int           type
     189        unsigned long serial
     190        Bool          send_event
     191        Display       *display
     192        int           extension
     193        int           evtype            #XI_HierarchyChanged
     194        Time          time
     195        int           flags
     196        int           num_info
     197        XIHierarchyInfo *info
     198
     199    ctypedef struct XIEventMask:
     200        int                 deviceid
     201        int                 mask_len
     202        unsigned char*      mask
     203
     204    ctypedef struct XIAnyClassInfo:
     205        int         type
     206        int         sourceid
     207
     208    ctypedef struct XIDeviceInfo:
     209        int                 deviceid
     210        char                *name
     211        int                 use
     212        int                 attachment
     213        Bool                enabled
     214        int                 num_classes
     215        XIAnyClassInfo      **classes
     216
     217    ctypedef struct XIButtonClassInfo:
     218        int         type
     219        int         sourceid
     220        int         num_buttons
     221        Atom        *labels
     222        XIButtonState state
     223
     224    ctypedef struct XIKeyClassInfo:
     225        int         type
     226        int         sourceid
     227        int         num_keycodes
     228        int         *keycodes
     229
     230    ctypedef struct XIValuatorClassInfo:
     231        int         type
     232        int         sourceid
     233        int         number
     234        Atom        label
     235        double      min
     236        double      max
     237        double      value
     238        int         resolution
     239        int         mode
     240
     241    ctypedef struct XIScrollClassInfo:
     242        int         type
     243        int         sourceid
     244        int         number
     245        int         scroll_type
     246        double      increment
     247        int         flags
     248
     249    ctypedef struct XITouchClassInfo:
     250        int         type
     251        int         sourceid
     252        int         mode
     253        int         num_touches
     254
     255    Status XIQueryVersion(Display *display, int *major_version_inout, int *minor_version_inout)
     256    Status XISelectEvents(Display *display, Window win, XIEventMask *masks, int num_masks)
     257    XIDeviceInfo* XIQueryDevice(Display *display, int deviceid, int *ndevices_return)
     258    void XIFreeDeviceInfo(XIDeviceInfo *info)
     259    Atom *XIListProperties(Display *display, int deviceid, int *num_props_return)
     260    Status XIGetProperty(Display *display, int deviceid, Atom property, long offset, long length,
     261                         Bool delete_property, Atom type, Atom *type_return,
     262                         int *format_return, unsigned long *num_items_return,
     263                         unsigned long *bytes_after_return, unsigned char **data)
     264
     265
     266DEF MAX_XI_EVENTS = 64
     267DEF XI_EVENT_MASK_SIZE = (MAX_XI_EVENTS+7)//8
     268
     269XI_EVENT_NAMES = {
     270    XI_DeviceChanged    : "XI_DeviceChanged",
     271    XI_KeyPress         : "XI_KeyPress",
     272    XI_KeyRelease       : "XI_KeyRelease",
     273    XI_ButtonPress      : "XI_ButtonPress",
     274    XI_ButtonRelease    : "XI_ButtonRelease",
     275    XI_Motion           : "XI_Motion",
     276    XI_Enter            : "XI_Enter",
     277    XI_Leave            : "XI_Leave",
     278    XI_FocusIn          : "XI_FocusIn",
     279    XI_FocusOut         : "XI_FocusOut",
     280    XI_HierarchyChanged : "XI_HierarchyChanged",
     281    XI_PropertyEvent    : "XI_PropertyEvent",
     282    XI_RawKeyPress      : "XI_RawKeyPress",
     283    XI_RawKeyRelease    : "XI_RawKeyRelease",
     284    XI_RawButtonPress   : "XI_RawButtonPress",
     285    XI_RawButtonRelease : "XI_RawButtonRelease",
     286    XI_RawMotion        : "XI_RawMotion",
     287    XI_TouchBegin       : "XI_TouchBegin",
     288    XI_TouchUpdate      : "XI_TouchUpdate",
     289    XI_TouchEnd         : "XI_TouchEnd",
     290    XI_TouchOwnership   : "XI_TouchOwnership",
     291    XI_RawTouchBegin    : "XI_RawTouchBegin",
     292    XI_RawTouchUpdate   : "XI_RawTouchUpdate",
     293    XI_RawTouchEnd      : "XI_RawTouchEnd",
     294    }
     295
     296XI_USE = {
     297    XIMasterPointer     : "master pointer",
     298    XIMasterKeyboard    : "master keyboard",
     299    XISlavePointer      : "slave pointer",
     300    XISlaveKeyboard     : "slave keyboard",
     301    XIFloatingSlave     : "floating slave",
     302    }
     303
     304CLASS_INFO = {
     305    XIButtonClass       : "button",
     306    XIKeyClass          : "key",
     307    XIValuatorClass     : "valuator",
     308    XIScrollClass       : "scroll",
     309    XITouchClass        : "touch",
     310    }
     311
     312
     313from core_bindings cimport _X11CoreBindings
     314
     315cdef _X11XI2Bindings singleton = None
     316def X11XI2Bindings():
     317    global singleton
     318    if singleton is None:
     319        singleton = _X11XI2Bindings()
     320    return singleton
     321
     322cdef class _X11XI2Bindings(_X11CoreBindings):
     323
     324    cdef int opcode
     325    cdef object events
     326    cdef object event_handlers
     327
     328    def __init__(self):
     329        self.opcode = -1
     330        self.event_handlers = {}
     331        self.reset_events()
     332
     333    def __repr__(self):
     334        return "X11XI2Bindings(%s)" % self.display_name
     335
     336    def connect(self, window, event, handler):
     337        self.event_handlers.setdefault(window, {})[event] = handler
     338
     339
     340    def reset_events(self):
     341        self.events = collections.deque(maxlen=100)
     342
     343    def find_event(self, event_name, serial):
     344        for x in reversed(self.events):
     345            #log.info("find_event(%s, %#x) checking %s", event_name, serial, x)
     346            if x.name==event_name and x.serial==serial:
     347                #log.info("matched")
     348                return x
     349            if x.serial<serial:
     350                #log.info("serial too old")
     351                return None
     352        return None
     353
     354    def find_events(self, event_name, windows):
     355        cdef Window found = 0
     356        cdef Window window
     357        matches = []
     358        for x in reversed(self.events):
     359            window = x.xid
     360            if x.name==event_name and ((found>0 and found==window) or (found==0 and window in windows)):
     361                matches.append(x)
     362                found = window
     363            elif found:
     364                break
     365        return matches
     366
     367    cdef int get_xi_opcode(self, int major=2, int minor=2):
     368        if self.opcode!=-1:
     369            return self.opcode
     370        cdef int opcode, event, error
     371        if not XQueryExtension(self.display, "XInputExtension", &opcode, &event, &error):
     372            log.warn("Warning: XI2 events are not supported")
     373            self.opcode = 0
     374            return 0
     375        cdef int rmajor = major, rminor = minor
     376        cdef int rc = XIQueryVersion(self.display, &rmajor, &rminor)
     377        if rc == BadRequest:
     378            log.warn("Warning: no XI2 %i.%i support,", major, minor)
     379            log.warn(" server supports version %i.%i only", rmajor, rminor)
     380            self.opcode = 0
     381            return 0
     382        elif rc:
     383            log.warn("Warning: Xlib bug querying XI2, code %i", rc)
     384            self.opcode = 0
     385            return 0
     386        self.opcode = opcode
     387        log("get_xi_opcode%s=%i", (major, minor), opcode)
     388        return opcode
     389
     390    cdef register_parser(self):
     391        log("register_parser()")
     392        if self.opcode>0:
     393            from xpra.x11.gtk2.gdk_bindings import add_x_event_parser
     394            add_x_event_parser(self.opcode, self.parse_xi_event)
     395
     396    cdef register_gdk_events(self):
     397        log("register_gdk_events()")
     398        if self.opcode<=0:
     399            return
     400        global XI_EVENT_NAMES
     401        from xpra.x11.gtk2.gdk_bindings import add_x_event_signal, add_x_event_type_name
     402        for e, xi_event_name in {
     403            XI_DeviceChanged    : "device-changed",
     404            XI_KeyPress         : "key-press",
     405            XI_KeyRelease       : "key-release",
     406            XI_ButtonPress      : "button-press",
     407            XI_ButtonRelease    : "button-release",
     408            XI_Motion           : "motion",
     409            XI_Enter            : "enter",
     410            XI_Leave            : "leave",
     411            XI_FocusIn          : "focus-in",
     412            XI_FocusOut         : "focus-out",
     413            XI_HierarchyChanged : "focus-changed",
     414            XI_PropertyEvent    : "property-event",
     415            XI_RawKeyPress      : "raw-key-press",
     416            XI_RawKeyRelease    : "raw-key-release",
     417            XI_RawButtonPress   : "raw-button-press",
     418            XI_RawButtonRelease : "raw-button-release",
     419            XI_RawMotion        : "raw-motion",
     420            XI_TouchBegin       : "touch-begin",
     421            XI_TouchUpdate      : "touch-update",
     422            XI_TouchEnd         : "touch-end",
     423            XI_TouchOwnership   : "touch-ownership",
     424            XI_RawTouchBegin    : "raw-touch-begin",
     425            XI_RawTouchUpdate   : "raw-touch-update",
     426            XI_RawTouchEnd      : "raw-touch-end",
     427            }.items():
     428            event = self.opcode+e
     429            add_x_event_signal(event, ("xi-%s" % xi_event_name, None))
     430            name = XI_EVENT_NAMES[e]
     431            add_x_event_type_name(event, name)
     432
     433    def select_xi2_events(self):
     434        cdef Window win = XDefaultRootWindow(self.display)
     435        log("select_xi2_events() root window=%#x", win)
     436        assert XI_LASTEVENT<MAX_XI_EVENTS, "bug: source needs to be updated, XI_LASTEVENT=%i" % XI_LASTEVENT
     437        cdef XIEventMask evmasks[1]
     438        cdef unsigned char mask1[XI_EVENT_MASK_SIZE]
     439        memset(mask1, 0, XI_EVENT_MASK_SIZE)
     440        #define XISetMask(ptr, event)   (((unsigned char*)(ptr))[(event)>>3] |=  (1 << ((event) & 7)))
     441        #XISetMask(mask1, XI_RawMotion)
     442        for e in (
     443            XI_KeyPress, XI_KeyRelease,
     444            XI_Motion,
     445            XI_HierarchyChanged,
     446            XI_ButtonPress, XI_ButtonRelease,
     447            XI_RawButtonPress, XI_RawButtonRelease,
     448            XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd,
     449            XI_RawTouchBegin, XI_RawTouchUpdate, XI_RawTouchEnd,
     450            XI_RawMotion,
     451            ):
     452            mask1[e>>3] |= (1<< (e & 0x7))
     453        evmasks[0].deviceid = XIAllDevices  #XIAllMasterDevices    #XIAllDevices
     454        evmasks[0].mask_len = XI_EVENT_MASK_SIZE
     455        evmasks[0].mask = mask1
     456        XISelectEvents(self.display, win, evmasks, 1)
     457        XFlush(self.display)
     458
     459    def parse_xi_event(self, display, uintptr_t _cookie):
     460        log("parse_xi_event(%s)", _cookie)
     461        cdef XGenericEventCookie *cookie = <XGenericEventCookie*> _cookie
     462        cdef XIDeviceEvent *device_e
     463        cdef XIHierarchyEvent * hierarchy_e
     464        cdef XIEvent *xie
     465        cdef XIRawEvent *raw
     466        cdef int i = 0, j = 0
     467        if not XGetEventData(self.display, cookie):
     468            return None
     469        xie = <XIEvent*> cookie.data
     470        device_e = <XIDeviceEvent*> cookie.data
     471        cdef int xi_type = cookie.evtype
     472        etype = self.opcode+xi_type
     473        global XI_EVENT_NAMES
     474        event_name = XI_EVENT_NAMES.get(xi_type)
     475        if not event_name:
     476            log("unknown XI2 event code: %i", xi_type)
     477            return None
     478
     479        #don't parse the same thing again:
     480        if len(self.events)>0:
     481            last_event = self.events[-1]
     482            if last_event.serial==xie.serial and last_event.type==etype:
     483                return last_event
     484
     485        pyev = X11Event(event_name)
     486        pyev.type = etype
     487        pyev.display = display
     488        pyev.send_event = bool(xie.send_event)
     489        pyev.serial = xie.serial
     490        pyev.time = int(xie.time)
     491        pyev.window = int(XDefaultRootWindow(self.display))
     492
     493        if xi_type in (XI_KeyPress, XI_KeyRelease,
     494                       XI_ButtonPress, XI_ButtonRelease,
     495                       XI_Motion,
     496                       XI_TouchBegin, XI_TouchUpdate, XI_TouchEnd):
     497            device = <XIDeviceEvent*> cookie.data
     498            #pyev.source = device.sourceid    #always 0
     499            pyev.device = device.deviceid
     500            pyev.detail = device.detail
     501            pyev.flags = device.flags
     502            pyev.window = int(device.child or device.event or device.root)
     503            pyev.x_root = device.root_x
     504            pyev.y_root = device.root_y
     505            pyev.x = device.event_x
     506            pyev.y = device.event_y
     507            #mask = []
     508            valuators = {}
     509            valuator = 0
     510            for i in range(device.valuators.mask_len*8):
     511                if device.valuators.mask[i>>3] & (1 << (i & 0x7)):
     512                    valuators[i] = device.valuators.values[valuator]
     513                    valuator += 1
     514            pyev.valuators = valuators
     515            buttons = []
     516            for i in range(device.buttons.mask_len):
     517                if device.buttons.mask[i>>3] & (1<< (i & 0x7)):
     518                    buttons.append(i)
     519            pyev.buttons = buttons
     520            state = []
     521            pyev.state = state
     522            pyev.modifiers = {
     523                "base"      : device.mods.base,
     524                "latched"   : device.mods.latched,
     525                "locked"    : device.mods.locked,
     526                "effective" : device.mods.effective,
     527                }
     528            #make it compatible with gdk events:
     529            pyev.state = device.mods.effective
     530        elif xi_type in (XI_RawKeyPress, XI_RawKeyRelease,
     531                         XI_RawButtonPress, XI_RawButtonRelease,
     532                         XI_RawMotion,
     533                         XI_RawTouchBegin, XI_RawTouchUpdate, XI_RawTouchEnd):
     534            raw = <XIRawEvent*> cookie.data
     535            valuators = {}
     536            raw_valuators = {}
     537            valuator = 0
     538            for i in range(raw.valuators.mask_len*8):
     539                if raw.valuators.mask[i>>3] & (1 << (i & 0x7)):
     540                    valuators[i] = raw.valuators.values[valuator]
     541                    raw_valuators[i] = raw.raw_values[valuator]
     542                    valuator += 1
     543            pyev.valuators = valuators
     544            pyev.raw_valuators = raw_valuators
     545        elif xi_type == XI_HierarchyChanged:
     546            pass
     547            #hierarchy_e = <XIHierarchyEvent*> cookie.data
     548            #pyev.flags = hierarchy_e.flags
     549            #info = {}
     550            #for i in range(hierarchy_e.num_info):
     551            #
     552        XFreeEventData(self.display, cookie)
     553        pyev.xid = pyev.window
     554        self.events.append(pyev)
     555
     556        handler = self.event_handlers.get(pyev.window, {}).get(event_name)
     557        log("parse_xi_event: %s, handler=%s", pyev, handler)
     558        if handler:
     559            handler(pyev)
     560        return None
     561
     562    def get_devices(self, show_all=True, show_disabled=False):
     563        global XI_USE
     564        cdef int ndevices, i, j
     565        cdef XIDeviceInfo *devices
     566        cdef XIDeviceInfo *device
     567        cdef XIAnyClassInfo *clazz
     568        if show_all:
     569            device_types = XIAllDevices
     570        else:
     571            device_types = XIAllMasterDevices
     572        devices = XIQueryDevice(self.display, device_types, &ndevices)
     573        dinfo = {}
     574        for i in range(ndevices):
     575            device = &devices[i]
     576            if not device.enabled and not show_disabled:
     577                continue
     578            info = {
     579                "name"          : device.name,
     580                "use"           : XI_USE.get(device.use, "unknown use: %i" % device.use),
     581                "attachment"    : device.attachment,
     582                "enabled"       : device.enabled,
     583                }
     584            classes = {}
     585            for j in range(device.num_classes):
     586                clazz = device.classes[j]
     587                classes[j] = self.get_class_info(clazz)
     588            info["classes"] = classes
     589            properties = self.get_device_properties(device.deviceid)
     590            if properties:
     591                info["properties"] = properties
     592            dinfo[device.deviceid] = info
     593        XIFreeDeviceInfo(devices)
     594        return dinfo
     595
     596    def get_device_properties(self, deviceid):
     597        cdef Atom *atoms
     598        cdef int nprops, i
     599        atoms = XIListProperties(self.display, deviceid, &nprops)
     600        if atoms==NULL or nprops==0:
     601            return None
     602        props = {}
     603        for i in range(nprops):
     604            value = self.get_device_property(deviceid, atoms[i])
     605            if value is not None:
     606                prop_name = self.XGetAtomName(atoms[i])
     607                props[prop_name] = value
     608        return props
     609
     610    cdef get_device_property(self, int deviceid, Atom property, req_type=0):
     611        #code mostly duplicated from window_bindings XGetWindowProperty:
     612        cdef int buffer_size = 64 * 1024
     613        cdef Atom xactual_type = <Atom> 0
     614        cdef int actual_format = 0
     615        cdef long offset = 0
     616        cdef unsigned long nitems = 0, bytes_after = 0
     617        cdef unsigned char *prop = NULL
     618        cdef Status status
     619        cdef Atom xreq_type = XIAnyPropertyType
     620        if req_type:
     621            xreq_type = self.get_xatom(req_type)
     622
     623        status = XIGetProperty(self.display,
     624                               deviceid, property,
     625                               0,
     626                               buffer_size//4,
     627                               False,
     628                               xreq_type, &xactual_type,
     629                               &actual_format, &nitems, &bytes_after, &prop)
     630        if status != Success:
     631            raise Exception("failed to retrieve XI property")
     632        if xactual_type == XNone:
     633            return None
     634        if xreq_type and xreq_type != xactual_type:
     635            raise Exception("expected %s but got %s" % (req_type, self.XGetAtomName(xactual_type)))
     636        # This should only occur for bad property types:
     637        assert not (bytes_after and not nitems)
     638        if bytes_after:
     639            raise Exception("reserved %i bytes for buffer, but data is bigger by %i bytes!" % (buffer_size, bytes_after))
     640        assert actual_format > 0
     641        if actual_format == 8:
     642            bytes_per_item = 1
     643        elif actual_format == 16:
     644            bytes_per_item = sizeof(short)
     645        elif actual_format == 32:
     646            bytes_per_item = 4  #sizeof(long)
     647        else:
     648            assert False
     649        cdef int nbytes = bytes_per_item * nitems
     650        data = (<char *> prop)[:nbytes]
     651        XFree(prop)
     652        prop_type = self.XGetAtomName(xactual_type)
     653        import binascii
     654        log.info("%s : %s value=%s", self.XGetAtomName(property), prop_type, data)
     655        log.info("hex=%s (type=%s, nitems=%i, bytes per item=%i, actual format=%i)", binascii.hexlify(data), prop_type, nitems, bytes_per_item, actual_format)
     656
     657        #FIXME: 32 here.. breaks
     658        if actual_format == 33:
     659            data = self.munge_packed_longs_to_ints(data)
     660
     661        fmt = None
     662        if prop_type=="INTEGER":
     663            fmt = {
     664                8   : "b",
     665                16  : "h",
     666                32  : "i",
     667                }.get(actual_format)
     668        elif prop_type=="CARDINAL":
     669            fmt = {
     670                8   : "B",
     671                16  : "H",
     672                32  : "I",
     673                }.get(actual_format)
     674        elif prop_type=="FLOAT":
     675            fmt = "f"
     676        if fmt:
     677            log.info("parsing using %s", fmt*nitems)
     678            value = struct.unpack(fmt*nitems, data)
     679            if nitems==1:
     680                return value[0]
     681            return value
     682        return data
     683
     684    cdef get_class_info(self, XIAnyClassInfo *class_info):
     685        cdef int i
     686        cdef XIButtonClassInfo *button
     687        cdef XIKeyClassInfo *key
     688        cdef XIValuatorClassInfo *valuator
     689        cdef XIScrollClassInfo *scroll
     690        cdef XITouchClassInfo *touch
     691        info = {
     692            "type"      : CLASS_INFO.get(class_info.type, "unknown type: %i" % class_info.type),
     693            "sourceid"  : class_info.sourceid,
     694            }
     695        if class_info.type==XIButtonClass:
     696            button = <XIButtonClassInfo*> class_info
     697            buttons = []
     698            for i in range(button.num_buttons):
     699                if button.labels[i]>0:
     700                    buttons.append(self.XGetAtomName(button.labels[i]))
     701            info["buttons"] = buttons
     702            #XIButtonState state
     703        elif class_info.type==XIKeyClass:
     704            key = <XIKeyClassInfo*> class_info
     705            keys = []
     706            for i in range(key.num_keycodes):
     707                keys.append(key.keycodes[i])
     708        elif class_info.type==XIValuatorClass:
     709            valuator = <XIValuatorClassInfo*> class_info
     710            info.update({
     711                "number"    : valuator.number,
     712                "min"       : valuator.min,
     713                "max"       : valuator.max,
     714                "value"     : valuator.value,
     715                "resolution": valuator.resolution,
     716                "mode"      : valuator.mode,
     717                })
     718            if valuator.label:
     719                info["label"] = self.XGetAtomName(valuator.label)
     720        elif class_info.type==XIScrollClass:
     721            scroll = <XIScrollClassInfo*> class_info
     722            info.update({
     723                "number"        : scroll.number,
     724                "scroll-type"   : scroll.scroll_type,
     725                "increment"     : scroll.increment,
     726                "flags"         : scroll.flags,
     727                })
     728        elif class_info.type==XITouchClass:
     729            touch = <XITouchClassInfo*> class_info
     730            info.update({
     731                "mode"          : touch.mode,
     732                "num-touches"   : touch.num_touches,
     733                })
     734        return info
     735
     736
     737    def gdk_inject(self):
     738        self.get_xi_opcode()
     739        log.info("XInput Devices:")
     740        from xpra.util import print_nested_dict
     741        print_nested_dict(self.get_devices(), print_fn=log.info)
     742        self.register_parser()
     743        self.register_gdk_events()
     744        #self.select_xi2_events()
  • xpra/x11/gtk2/gdk_bindings.pyx

     
    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: