3

Introduction: The xinput command allows me to set deceleration parameter for my mouse, what I have to do, because I like small mouse sensitivity and smallest value in Ubuntu is still a little too high for me. So I use:

szczepan@szczepan-550P5C:~$ xinput list
⎡ Virtual core pointer                      id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ A4TECH USB Device                         id=10   [slave  pointer  (2)]
⎜   ↳ A4TECH USB Device                         id=11   [slave  pointer  (2)]
⎜   ↳ SynPS/2 Synaptics TouchPad                id=14   [slave  pointer  (2)]
⎣ Virtual core keyboard                     id=3    [master keyboard (2)]
    ↳ Virtual core XTEST keyboard               id=5    [slave  keyboard (3)]
    ↳ Power Button                              id=6    [slave  keyboard (3)]
    ↳ Video Bus                                 id=7    [slave  keyboard (3)]
    ↳ Video Bus                                 id=8    [slave  keyboard (3)]
    ↳ Power Button                              id=9    [slave  keyboard (3)]
    ↳ WebCam SC-13HDL11939N                     id=12   [slave  keyboard (3)]
    ↳ AT Translated Set 2 keyboard              id=13   [slave  keyboard (3)]
szczepan@szczepan-550P5C:~$ xinput set-prop 11 "Device Accel Constant Deceleration" 5

... and everything is all right. But I have to use these commands every time I plug my device on, log in after turn off/sleep.

My question: is it possible to force Ubuntu, every time this deivce is detected, to set parameter "Device Accel Constant Deceleration" for device A4TECH USB Device as 5? And if yes, how can I do that? Thanks for all effort in advance.

Szczepan
  • 268

1 Answers1

4

Introduction

The script bellow waits for user-defined device to be connected and sets appropriate values , continuously repeating the process , thus allowing the user to connect and disconnect device multiple time during session.

It reads user - defined settings in ~/.xinputmonrc file, which must be created before launching the script

Usage

As shown by -h option:

usage: xinput_monitor.py [-h] [-q] -d DEVICE

Script that waits for presence of user device and sets preferences as defined in
~/.xinputmonrc file

optional arguments:
  -h, --help            show this help message and exit
  -q, --quiet           Blocks on-screen notifications
  -d DEVICE, --device DEVICE
                        device name

NOTE: As stated in the description, the file reads ~/.xinputmonrc where ~ is your home directory. Example of such file (note array usage for properties that require multiple values):

{
 "Device Accel Constant Deceleration":5,
 "Evdev Scrolling Distance":[1,1,1]
}

Suppose we have Logitech USB Receiver device listed in xinput output. The script can be called like so:

python3 xinput_monitor.py -d "Logitech USB Receiver"

The script will issue a notification that monitoring has started:

enter image description here

Once the device is connected, it will issue notification that device has been detected:

enter image description here

If everything is OK , the script proceeds silently and waits till device is disconnected:

enter image description here

The -q option allows silencing all the notification bubbles.

Obtaining the script

The source code is available in this post and as Gist on Github

You can copy the source code from here, save it as xinput_monitor.py , and make executable with chmod +x xinput_monitor.py terminal command in whichever directory you saved it.

Source code

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Author: Sergiy Kolodyazhnyy
Date:  August 2nd, 2016
Written for: https://askubuntu.com/q/806212/295286
Tested on Ubuntu 16.04 LTS

usage: xinput_monitor.py [-h] [-q] -d DEVICE

Script that waits for presence of user device 
and sets preferences as defined
in ~/.xinputmonrc file

optional arguments:
  -h, --help            show this help message and exit
  -q, --quiet           Blocks on-screen notifications
  -d DEVICE, --device DEVICE
                        device name

"""
from __future__ import print_function
import gi
gi.require_version('Notify', '0.7')
from gi.repository import Notify
import subprocess
import argparse
import time
import json
import os

def send_notif(n,title, text):
    try:
        if Notify.init(__file__):
            # n = Notify.Notification.new("Notify")
            n.update(title, text)
            n.set_urgency(2)
            if not n.show():
                raise SyntaxError("sending notification failed!")
        else:
            raise SyntaxError("can't initialize notification!")
    except SyntaxError as error:
        print(error)
        if error == "sending notification failed!":
            Notify.uninit()
    else:
        Notify.uninit()

def run_cmd(cmdlist):
    """ Reusable function for running shell commands"""
    try:
        stdout = subprocess.check_output(cmdlist)
    except subprocess.CalledProcessError as pserror:
        return pserror.output.decode().strip()
        #sys.exit(1)
    else:
        if stdout:
            return stdout.decode().strip()

def list_ids(mouse_name):
    """ Returns list of ids for the same device"""
    #while True:
    mouse_ids = []
    for dev_id in run_cmd(['xinput','list','--id-only']).split('\n'):
        if mouse_name in run_cmd(['xinput','list','--name-only',dev_id]):
           mouse_ids.append(dev_id)
    return mouse_ids

def read_config_file(notif):

    """ reads ~/.xinputmonrc  file """
    rcfile = os.path.join( os.path.expanduser('~'),'.xinputmonrc')
    try:
        with open(rcfile) as config_file:
            config_data = json.load(config_file)
    except IOError as error:
        send_notif(notif, __file__ , error.__repr__()  )
    else:
        if config_data:
            return config_data

def set_props(notif,device):
    """Sets properties per each device is
       given by list_ids() function"""

    props = read_config_file(notif)
    # Thiscan also be set manually as in 
    # commented-out example below

    #props = { 'Device Accel Profile':'-1',
    #          'Device Accel Constant Deceleration':'3.5',
    #          'Device Accel Velocity Scaling':'1.0'   }

    if not props:
        send_notif(notif,'Reading ~/.xinputmonrc failed', 
                   'Please write proper rc file.') 
        return None
    """ set all property-value pair per each device id
        Uncomment the print function if you wish to know
        which ids have been altered for double-checking
        with xinput list-props"""
    for dev_id in list_ids(device):
        # print(dev_id)
        for prop,value in props.items():
            if type(value) is not list: 
                value = [value]
            run_cmd(['xinput','set-prop',dev_id,prop] + 
                    [str(item) for item in value ])  

def parse_args():
    """ Parse command line arguments"""
    arg_parser = argparse.ArgumentParser(
                 description="""Script that waits for """ + 
                             """presence of user device """+ 
                             """and sets preferences as """ + 
                             """defined in ~/.xinputmonrc file""")
    arg_parser.add_argument(
                '-q','--quiet', action='store_true',
                help='Blocks on-screen notifications',
                required=False)

    arg_parser.add_argument(
                '-d','--device', 
                help='device name',
                type=str,
                required=True)
    return arg_parser.parse_args()

def main():
    notif = Notify.Notification.new("Notify")
    args = parse_args()
    while True:
         if not args.quiet:
             send_notif(notif, __file__ , 
                        'Wating for ' + args.device )
         while args.device not in run_cmd(['xinput','list','--name-only']):
             time.sleep(0.25)
             pass
         time.sleep(0.25) # let xinput catch up
         if not args.quiet:
             send_notif(notif, __file__, 
                        args.device + ' connected. Setting values')
         # set props here
         set_props(notif,args.device)
         while args.device in run_cmd(['xinput','list','--name-only']):
             time.sleep(0.25)
             pass
         if not args.quiet:
             send_notif( notif , __file__ , args.device +  
                         ' disconnected. Resuming monitoring' )

if __name__ == '__main__':
    main()

Notes

  • Multiple instances can be started to monitor multiple devices but this is not recommended because multiple instances read the same config file.
  • Script is python 3 and python 2 compatible, can run with either.
  • Consult How can I edit/create new launcher items in Unity by hand? for creating a launcher or desktop shortcut for this script , if you desire to launch it with double-click
  • In order to link this script to a keyboard shortcut for easy access, consult How to add keyboard shortcuts?
Sergiy Kolodyazhnyy
  • 105,154
  • 20
  • 279
  • 497