xpra icon
Bug tracker and wiki

Ticket #1631: uinput-upgrades.patch

File uinput-upgrades.patch, 15.8 KB (added by Antoine Martin, 3 years ago)

work in progress patch

  • xpra/os_util.py

     
    182182    return rel(sys_platform)
    183183
    184184
     185def get_rand_chars(l=16, chars=b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"):
     186    import random
     187    return b"".join(chars[random.randint(0, len(chars)-1)] for _ in range(l))
     188
    185189def get_hex_uuid():
    186190    return uuid.uuid4().hex
    187191
  • xpra/scripts/server.py

     
    9191def get_dbus_pid():
    9292    return _get_int("_XPRA_DBUS_PID")
    9393
     94def save_uinput_id(uuid):
     95    _save_str("_XPRA_UINPUT_ID", uuid)
     96
     97#def get_uinput_id():
     98#    return _get_str("_XPRA_UINPUT_ID")
     99
    94100def get_dbus_env():
    95101    env = {}
    96102    for n,load in (
     
    157163    except:
    158164        pass
    159165
     166def close_gtk_display():
     167    # Close our display(s) first, so the server dying won't kill us.
     168    # (if gtk has been loaded)
     169    gtk_mod = sys.modules.get("gtk")
     170    if gtk_mod:
     171        for d in gtk_mod.gdk.display_manager_get().list_displays():
     172            d.close()
    160173
     174def kill_xvfb(xvfb_pid):
     175    if xvfb_pid:
     176        from xpra.log import Logger
     177        log = Logger("server")
     178        log.info("killing xvfb with pid %s", xvfb_pid)
     179        try:
     180            os.kill(xvfb_pid, signal.SIGTERM)
     181        except OSError as e:
     182            log.info("failed to kill xvfb process with pid %s:", xvfb_pid)
     183            log.info(" %s", e)
     184
     185
    161186def print_DE_warnings(desktop_display, pulseaudio, notifications, dbus_launch):
    162187    de = os.environ.get("XDG_SESSION_DESKTOP") or os.environ.get("SESSION_DESKTOP")
    163188    if not de:
     
    348373
    349374
    350375def run_server(error_cb, opts, mode, xpra_file, extra_args, desktop_display=None):
     376    from xpra.os_util import strtobytes
    351377    try:
    352378        cwd = os.getcwd()
    353379    except:
     
    675701    os.environ.update(protected_env)
    676702    log("env=%s", os.environ)
    677703
    678     #create devices for vfb if needed:
    679     devices = {}
    680     if start_vfb and opts.input_devices.lower()=="uinput":
    681         devices = create_input_devices(uid)
    682 
    683704    # Start the Xvfb server first to get the display_name if needed
    684705    odisplay_name = display_name
    685706    xvfb = None
    686707    xvfb_pid = None
     708    uinput_uuid = None
    687709    if start_vfb:
    688710        assert not proxying and xauth_data
    689711        pixel_depth = validate_pixel_depth(opts.pixel_depth)
    690712        from xpra.x11.vfb_util import start_Xvfb
    691         xvfb, display_name, cleanups = start_Xvfb(opts.xvfb, pixel_depth, display_name, cwd, uid, gid, username, xauth_data, devices)
     713        from xpra.server.server_util import has_uinput
     714        uinput_uuid = None
     715        if has_uinput() and opts.input_devices.lower() in ("uinput", "auto") and not shadowing:
     716            from xpra.os_util import get_rand_chars
     717            uinput_uuid = get_rand_chars(8)
     718        xvfb, display_name, cleanups = start_Xvfb(opts.xvfb, pixel_depth, display_name, cwd, uid, gid, username, xauth_data, uinput_uuid)
    692719        for f in cleanups:
    693720            add_cleanup(f)
    694721        xvfb_pid = xvfb.pid
     
    698725        os.environ.update(protected_env)
    699726        if display_name!=odisplay_name and pam:
    700727            pam.set_items({"XDISPLAY" : display_name})
     728
    701729    if POSIX and not OSX and displayfd>0:
    702730        from xpra.platform.displayfd import write_displayfd
    703731        try:
     
    713741        except:
    714742            pass
    715743
    716     close_display = None
    717744    if not proxying:
    718745        def close_display():
    719             # Close our display(s) first, so the server dying won't kill us.
    720             # (if gtk has been loaded)
    721             gtk_mod = sys.modules.get("gtk")
    722             if gtk_mod:
    723                 for d in gtk_mod.gdk.display_manager_get().list_displays():
    724                     d.close()
    725             if xvfb_pid:
    726                 log.info("killing xvfb with pid %s", xvfb_pid)
    727                 try:
    728                     os.kill(xvfb_pid, signal.SIGTERM)
    729                 except OSError as e:
    730                     log.info("failed to kill xvfb process with pid %s:", xvfb_pid)
    731                     log.info(" %s", e)
     746            close_gtk_display()
     747            kill_xvfb(xvfb_pid)
    732748        add_cleanup(close_display)
     749    else:
     750        close_display = None
    733751
    734752    if opts.daemon:
    735753        def noerr(fn, *args):
     
    758776            #xvfb problem: exit now
    759777            return  1
    760778
     779    #create devices for vfb if needed:
     780    devices = {}
     781    if not start_vfb and not proxying and not shadowing:
     782        #try to find the existing uinput uuid:
     783        #use a subprocess to avoid polluting our current process
     784        #with X11 connections before we get a chance to change uid
     785        from xpra.os_util import get_status_output
     786        cmd = ["xprop", "-display", display_name, "-root", "_XPRA_UINPUT_ID"]
     787        try:
     788            code, out, err = get_status_output(cmd)
     789        except Exception as e:
     790            log("failed to get existing uinput id: %s", e)
     791        else:
     792            log("Popen(%s)=%s", cmd, (code, out, err))
     793            if code==0 and out.find("=")>0:
     794                uinput_uuid = strtobytes(out.split("=", 1)[1])
     795    if uinput_uuid:
     796        devices = create_input_devices(uinput_uuid, uid)
     797
    761798    if ROOT and (uid!=0 or gid!=0):
    762799        log("root: switching to uid=%i, gid=%i", uid, gid)
    763800        setuidgid(uid, gid)
     
    784821            from xpra.x11.vfb_util import verify_display_ready
    785822            if not verify_display_ready(xvfb, display_name, shadowing):
    786823                return 1
    787             from xpra.x11.gtk2.gdk_display_util import verify_gdk_display
     824            from xpra.x11.gtk2.gdk_display_util import verify_gdk_display       #@Reimport
    788825            display = verify_gdk_display(display_name)
    789826            if not display:
    790827                return 1
     
    833870            save_xvfb_pid(xvfb_pid)
    834871
    835872        if POSIX:
     873            save_uinput_id(uinput_uuid or "")
    836874            dbus_pid = -1
    837875            dbus_env = {}
    838876            if clobber:
     
    904942
    905943    #publish mdns records:
    906944    if opts.mdns:
    907         from xpra.os_util import strtobytes
    908945        from xpra.platform.info import get_username
    909946        from xpra.server.socket_util import mdns_publish
    910947        mdns_info = {
  • xpra/server/server_util.py

     
    295295        log.error(" %", e)
    296296    return None
    297297
    298 def create_uinput_pointer_device(uid, gid):
     298def has_uinput():
    299299    from xpra.log import Logger
    300300    log = Logger("server")
    301301    try:
    302302        import uinput
     303        assert uinput
    303304    except (ImportError, NameError) as e:
    304         log.error("Error: cannot create uinput devices:")
     305        log.info("cannot access python uinput module:")
     306        log.info(" %s", e)
     307        return False
     308    else:
     309        return True
     310
     311def create_uinput_pointer_device(uuid, uid, gid):
     312    from xpra.log import Logger
     313    log = Logger("server")
     314    try:
     315        import uinput
     316    except (ImportError, NameError) as e:
     317        log.error("Error: cannot access python uinput module:")
    305318        log.error(" %s", e)
    306319        return None
    307320    events = (
     
    320333        )
    321334    BUS_USB = 0x03
    322335    #BUS_VIRTUAL = 0x06
     336    VENDOR = 0xffff
     337    PRODUCT = 0xffff
     338    VERSION = 0
     339    #name = "Xpra Virtual Pointer %s" % uuid
    323340    name = "Xpra Virtual Pointer"
    324341    try:
    325         uinput_pointer = uinput.Device(events, name=name, bustype=BUS_USB, vendor=0, product=0, version=0)
     342        uinput_pointer = uinput.Device(events, name=name, bustype=BUS_USB, vendor=VENDOR, product=PRODUCT, version=VERSION)
    326343    except OSError as e:
    327         log.error("Error: cannot open uinput,")
    328         log.error(" make sure that the kernel module is loaded")
    329         log.error(" and that the /dev/uinput device exists:")
    330         log.error(" %s", e)
     344        log("uinput.Device creation failed", exc_info=True)
     345        if os.getuid()==0:
     346            #running as root, this should work!
     347            log.error("Error: cannot open uinput,")
     348            log.error(" make sure that the kernel module is loaded")
     349            log.error(" and that the /dev/uinput device exists:")
     350            log.error(" %s", e)
     351        else:
     352            log.info("cannot access uinput: %s", e)
    331353        return None
    332354    dev_path = get_uinput_device_path(uinput_pointer)
    333355    if not dev_path:
    334356        uinput_pointer.destroy()
    335357        return None
    336     try:
    337         #log("chown%s", (dev_path, uid, gid))
    338         os.lchown(dev_path, uid, gid)
    339         #udev rules change the device ownership
    340         #FIXME: fix udev or use inotify? (racy)
    341         import time
    342         time.sleep(1)
    343         log("chown%s", (dev_path, uid, gid))
    344         os.lchown(dev_path, uid, gid)
    345         #os.lchown("/dev/input/mouse2", uid, gid)
    346     except OSError as e:
    347         log.error("Error: failed to change ownership of '%s':", name)
    348         log.error(" at %s:", dev_path)
    349         log.error(" %s", e)
    350         return None
     358    if False:
     359        try:
     360            #log("chown%s", (dev_path, uid, gid))
     361            os.lchown(dev_path, uid, gid)
     362            #udev rules change the device ownership
     363            #FIXME: fix udev or use inotify? (racy)
     364            import time
     365            time.sleep(1)
     366            log("chown%s", (dev_path, uid, gid))
     367            os.lchown(dev_path, uid, gid)
     368            #os.lchown("/dev/input/mouse2", uid, gid)
     369        except OSError as e:
     370            log.error("Error: failed to change ownership of '%s':", name)
     371            log.error(" at %s:", dev_path)
     372            log.error(" %s", e)
     373            return None
    351374    return name, uinput_pointer, dev_path
    352375
    353 def create_uinput_devices(uid, gid):
    354     d = create_uinput_pointer_device(uid, gid)
     376def create_uinput_devices(uinput_uuid, uid, gid):
     377    d = create_uinput_pointer_device(uinput_uuid, uid, gid)
    355378    if not d:
    356379        return {}
    357380    name, uinput_pointer, dev_path = d
     
    363386            }
    364387        }
    365388
    366 def create_input_devices(uid, gid=-1):
    367     return create_uinput_devices(uid, gid)
     389def create_input_devices(uinput_uuid, uid, gid=-1):
     390    return create_uinput_devices(uinput_uuid, uid, gid)
  • xpra/x11/vfb_util.py

     
    2323assert len(DEFAULT_DESKTOP_VFB_RESOLUTION)==2
    2424
    2525
    26 XORG_DEVICE_OPTIONS = {
    27     "pointer"   : """
    28     MatchIsPointer "True"
    29     Driver "libinput"
    30     Option "AccelProfile" "flat"
    31 """,
    32     "keyboard"  : 'MatchIsKeyboard "True"',
    33     }
    34 
    35 
    36 def create_xorg_device_configs(xorg_conf_dir, devices, uid, gid):
     26def create_xorg_device_configs(xorg_conf_dir, device_uuid, uid, gid):
    3727    from xpra.log import Logger
    3828    log = Logger("server", "x11")
    3929    cleanups = []
    40     if not devices:
     30    if not device_uuid:
    4131        return cleanups
    4232
    4333    def makedir(dirname):
     
    6252    for d in dirs:
    6353        makedir(d)
    6454
    65     #create individual device files:
     55    #create individual device files,
     56    #only pointer for now:
    6657    i = 0
    67     for dev_type, devdef in devices.items():
    68         #ie:
    69         #name = "pointer"
    70         #devdef = {"uinput" : uninput.Device, "device" : "/dev/input20" }
    71         match_type = XORG_DEVICE_OPTIONS.get(dev_type)
    72         uinput = devdef.get("uinput")
    73         device = devdef.get("device")
    74         name = devdef.get("name")
    75         if match_type and uinput and device and name:
    76             conf_file = os.path.join(xorg_conf_dir, "%02i-%s.conf" % (i, dev_type))
    77             with open(conf_file, "wb") as f:
    78                 f.write("""
    79 Section "InputClass"
     58    dev_type = "pointer"
     59    #name = "Xpra Virtual Pointer %s" % device_uuid
     60    name = "Xpra Virtual Pointer"
     61    conf_file = os.path.join(xorg_conf_dir, "%02i-%s.conf" % (i, dev_type))
     62    with open(conf_file, "wb") as f:
     63        f.write(b"""Section "InputClass"
    8064    Identifier "xpra-virtual-%s"
    8165    MatchProduct "%s"
    82     MatchDevicePath "%s"
     66    MatchUSBID "ffff:ffff"
     67    MatchIsPointer "True"
     68    Driver "libinput"
     69    Option "AccelProfile" "flat"
    8370    Option "Ignore" "False"
    84 %s
    8571EndSection
    86 """ % (dev_type, name, device, match_type))
    87                 os.fchown(f.fileno(), uid, gid)
    88                 #Option "AccelerationProfile" "-1"
    89                 #Option "AccelerationScheme" "none"
    90                 #Option "AccelSpeed" "-1"
    91             def cleanup_conf_file():
    92                 log("cleanup_conf_file: %s", conf_file)
    93                 os.unlink(conf_file)
    94             cleanups.insert(0, cleanup_conf_file)
     72""" % (dev_type, name))
     73        os.fchown(f.fileno(), uid, gid)
     74        #Option "AccelerationProfile" "-1"
     75        #Option "AccelerationScheme" "none"
     76        #Option "AccelSpeed" "-1"
     77    def cleanup_conf_file():
     78        log("cleanup_conf_file: %s", conf_file)
     79        os.unlink(conf_file)
     80    cleanups.insert(0, cleanup_conf_file)
    9581    return cleanups
    9682
    97 
    98 def start_Xvfb(xvfb_str, pixel_depth, display_name, cwd, uid, gid, username, xauth_data, devices={}):
     83def start_Xvfb(xvfb_str, pixel_depth, display_name, cwd, uid, gid, username, xauth_data, uinput_uuid=None):
    9984    if not POSIX:
    10085        raise InitException("starting an Xvfb is not supported on %s" % os.name)
    10186    if not xvfb_str:
     
    134119    def pathexpand(s):
    135120        return shellsub(s, subs)
    136121
    137     #create uinput device definition files:
    138     #(we are assuming that Xorg is configured to use this path..)
    139     xorg_conf_dir = pathexpand(get_Xdummy_confdir())
    140     cleanups = create_xorg_device_configs(xorg_conf_dir, devices, uid, gid)
    141 
    142122    #identify logfile argument if it exists,
    143123    #as we may have to rename it, or create the directory for it:
    144124    import shlex
     
    172152    if not xvfb_cmd:
    173153        raise InitException("cannot start Xvfb, the command definition is missing!")
    174154
    175     if devices:
     155    if uinput_uuid:
     156        #use uinput:
    176157        #identify -config xorg.conf argument and replace it with the uinput one:
    177158        try:
    178159            config_argindex = xvfb_cmd.index("-config")
     
    184165            xorg_conf = xorg_conf.replace("xorg.conf", "xorg-uinput.conf")
    185166            if os.path.exists(xorg_conf):
    186167                xvfb_cmd[config_argindex+1] = xorg_conf
     168        #create uinput device definition files:
     169        #(we have to assume that Xorg is configured to use this path..)
     170        xorg_conf_dir = pathexpand(get_Xdummy_confdir())
     171        cleanups = create_xorg_device_configs(xorg_conf_dir, uinput_uuid, uid, gid)
    187172
    188173    xvfb_executable = xvfb_cmd[0]
    189174    if (xvfb_executable.endswith("Xorg") or xvfb_executable.endswith("Xdummy")) and pixel_depth>0:
  • xpra/x11/x11_server_base.py

     
    178178        X11ServerCore.last_client_exited(self)
    179179
    180180    def init_virtual_devices(self, devices):
     181        #(this runs in the main thread - before the main loop starts)
    181182        #for the time being, we only use the pointer if there is one:
    182183        pointer = devices.get("pointer")
    183184        if pointer:
  • udev/rules.d/71-xpra-virtual-pointer.rules

     
    11# allow xpra to use fine grained scrolling
    22
    3 ACTION=="add|change", ATTRS{name}=="Xpra Virtual Pointer", ENV{MOUSE_WHEEL_CLICK_ANGLE}="1", ENV{MOUSE_WHEEL_CLICK_COUNT}="360"
     3ACTION=="add|change", ATTRS{name}=="Xpra Virtual Pointer", ENV{MOUSE_WHEEL_CLICK_ANGLE}="1", ENV{MOUSE_WHEEL_CLICK_COUNT}="360", MODE="0666"