1

I'm trying to write a GUI tool to restrict a pointer to a specific monitor (e.g. a touchscreen pointer should map onto its own screen and not across the union of all monitors). The tool is in Python (using pygtk).

For the UI, I need to list all the pointers, so you can select the one you mean, and then call xinput map-to-output pointer_id monitor_id. If this command is given the id of a non-pointer device, then it raises an error, so I'd like to avoid giving those IDs as options.

The output of xinput list looks like this:

⎡ Virtual core pointer                          id=2    [master pointer  (3)]
⎜   ↳ Virtual core XTEST pointer                id=4    [slave  pointer  (2)]
⎜   ↳ ELAN Touchscreen                          id=18   [slave  pointer  (2)]
⎜   ↳ SynPS/2 Synaptics TouchPad                id=21   [slave  pointer  (2)]
⎜   ↳ TPPS/2 IBM TrackPoint                     id=22   [slave  pointer  (2)]
⎜   ↳ Cherry USB Optical Mouse Consumer Control id=10   [slave  pointer  (2)]
⎜   ↳ Cherry USB Optical Mouse                  id=12   [slave  pointer  (2)]
⎜   ↳ HID 04b4:3003 Consumer Control            id=14   [slave  pointer  (2)]
⎜   ↳ HID 04b4:3003 Mouse                       id=24   [slave  pointer  (2)]
⎜   ↳          WALTOP     Graphics Tablet  Pen (0)      id=26   [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)]
    ↳ Sleep Button                              id=8    [slave  keyboard (3)]
    ↳ Integrated Camera: Integrated C           id=19   [slave  keyboard (3)]
    ↳ AT Translated Set 2 keyboard              id=20   [slave  keyboard (3)]
    ↳ ThinkPad Extra Buttons                    id=23   [slave  keyboard (3)]
    ↳ Cherry USB Optical Mouse System Control   id=9    [slave  keyboard (3)]
    ↳ Cherry USB Optical Mouse Consumer Control id=11   [slave  keyboard (3)]
    ↳ HID 04b4:3003 System Control              id=13   [slave  keyboard (3)]
    ↳ HID 04b4:3003 Consumer Control            id=15   [slave  keyboard (3)]
    ↳ HID 04b4:3003 Keyboard                    id=16   [slave  keyboard (3)]
    ↳ HID 04b4:3003                             id=17   [slave  keyboard (3)]
    ↳          WALTOP     Graphics Tablet       id=25   [slave  keyboard (3)]

To build the menu, I need to get the name and id of all pointers (I guess slave pointers, I don't know what would happen if I selected Virtual core pointer). On the one hand, xinput list --id-only and xinput list --name-only give the exact information I need, except that I need to filter out the ids and names which are not pointers. On the other hand, I could do xinput list | grep pointer to get the relevant lines, but the resulting thing does not look very nice to parse (there are extraneous brackets and the weird ↳ arrow character). I tried looking for options in man xinput to either do some filtering or to simplify the output, but couldn't find anything.

My project is based off ptxconf, and their solution is as follows. I'm hoping to find something more elegant.

    def getPenTouchIds(self):
        """Returns a list of input id/name pairs for all available pen/tablet xinput devices"""
        retval = subprocess.Popen("xinput list", shell=True, stdout=subprocess.PIPE).stdout.read()

        ids = {}
        for line in retval.split("]"):
            if "pointer" in line.lower() and "master" not in line.lower():
                id = int(line.split("id=")[1].split("[")[0].strip())
                name = line.split("id=")[0].split("\xb3",1)[1].strip()
                if self.getPointerDeviceMode(id) == "absolute":
                    ids[name+"(%d)"%id]={"id":id}
        return ids
4
  • Thanks. I had not realised that fixing my touchscreen (that I don't use), was so easy. I will add an answer to your filtering question. Jan 6, 2021 at 11:03
  • 1
    @ctrl-alt-delor No problem, glad it helped you! In case you're interested, the tool I'm writing is based off an existing one named ptxconf. It appears to depend on python 2, so I'm simply trying to update it so it'll run on my system. Jan 6, 2021 at 11:11
  • How did the old tool do it? Jan 6, 2021 at 11:12
  • They parsed the output of xinput list in python, but I didn't find it very easy to read so was hoping to take advantage of the --id-only and --name-only options to get something cleaner. i'll get the snippet and update the question. Jan 6, 2021 at 11:15

2 Answers 2

0

I can not find an answer within xinput though not spent much time (I assume that you looked here a lot.

So I went with awk is seems like a simple line based with context problem.

xinput list | awk 'BEGIN {is_pt=0}; /Virtual core pointer/ {is_pt=1}; /Virtual core keyboard/ {is_pt=0}; {if (is_pt==1) {print $0}} '

or with better formatting:

xinput list | awk '
  BEGIN {is_pt=0} 
  /Virtual core pointer/  {is_pt=1} 
  /Virtual core keyboard/ {is_pt=0} 
  { 
    if (is_pt==1) {
      print $0
    }
  }
'

Each block of code ({}) is executed if the proceeding pattern matches. There is one block (the one with the if in it, that has no proceeding pattern. It is run for every line. $0 means all of the current record (line). BEGIN is the start of the file.

1
  • Thanks for your answer. This seems to be very similar to the output of xinput list | grep pointer - is it straightforward to extract the name and id from the lines there? It would be easy if xinput have the output in simple columns, since then something like cut could extract them. I think grabbing something like the regex /id=[[:digit]]+/ would work for the ID, but I can't see a clean way to get the name. Jan 6, 2021 at 11:24
0

I had the same issue trying to match my mouse in the xinput list, I'm used to work with sed for these kind of problems so here's the sed based approach:

MOUSE_G903_ID=$(xinput --list | sed -rn '/^.*Virtual core pointer/,/^.*Virtual core keyboard/ s/^.*Logitech G903 LS[[:blank:]]+id=([[:digit:]]+).*/\1/p')

The -n switch makes sed to supress all output, so you have to print what you want explicitly with the p command, or in this case, the p flag of the s command.

There is a single sed expression (in single quotes) that first defines a range where to operate: the range of lines between the two "header lines" (Virtual core pointer, Virtual core keyboard).

After the range, an s command matches the line that corresponds to my mouse, captures the numeric id and prints it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.