<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6112936277054198647</id><updated>2026-03-13T20:50:56.531+10:00</updated><category term="xorg"/><category term="libinput"/><category term="workflow"/><category term="xi2"/><category term="synaptics"/><category term="xkb"/><category term="evdev"/><category term="git"/><category term="wacom"/><category term="gnome"/><category term="wayland"/><category term="multitouch"/><category term="tutorial"/><category term="configuration"/><category term="libei"/><category term="fedora"/><category term="freedesktop.org"/><category term="gitlab"/><category term="hid"/><category term="input device properties"/><category term="x"/><category term="xorg.conf"/><category term="evemu"/><category term="evtest"/><category term="kernel"/><category term="mpx"/><category term="compiz"/><category term="gnome-device-setup"/><category term="hal"/><category term="libevdev"/><category term="libinput. wayland"/><category term="libratbag"/><category term="outdoors"/><category term="tig"/><category term="tuhi"/><category term="xds"/><category term="xlib"/><category term="xts"/><title type='text'>Who-T</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>306</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-1329935646308529173</id><published>2025-08-20T11:12:00.003+10:00</published><updated>2025-08-20T11:12:18.142+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hid"/><category scheme="http://www.blogger.com/atom/ns#" term="kernel"/><title type='text'>Why is my device a touchpad and a mouse and a keyboard?</title><content type='html'>&lt;p&gt;
If you have spent any time around HID devices under Linux (for example if you
are an avid mouse, touchpad or keyboard user) then you may have noticed that
your single physical device actually shows up as multiple device nodes (for
free! and nothing happens for free these days!).
If you haven&#39;t noticed this, run &lt;code&gt;libinput record&lt;/code&gt; and you may be
part of the lucky roughly 50% who get free extra event nodes.
&lt;/p&gt;

&lt;p&gt;
The pattern is always the same. Assuming you have a device named 
&lt;code&gt;FooBar ExceptionalDog 2000 AI&lt;/code&gt;[1] what you will see are multiple devices

&lt;pre&gt;
/dev/input/event0: FooBar ExceptionalDog 2000 AI Mouse
/dev/input/event1: FooBar ExceptionalDog 2000 AI Keybard 
/dev/input/event2: FooBar ExceptionalDog 2000 AI Consumer Control 
&lt;/pre&gt;

The Mouse/Keyboard/Consumer Control/... suffixes are a quirk of the kernel&#39;s
HID implementation which splits out a device based on the &lt;b&gt;Application Collection&lt;/b&gt;. [2]
&lt;/p&gt;
&lt;p&gt;
A &lt;a
href=&quot;https://who-t.blogspot.com/2018/12/understanding-hid-report-descriptors.html&quot;&gt;HID report descriptor&lt;/a&gt;
may use collections to group things together. A &quot;Physical Collection&quot; indicates
&quot;these things are (on) the same physical thingy&quot;. A &quot;Logical Collection&quot; indicates
&quot;these things belong together&quot;.  And you can of course nest these things
near-indefinitely so e.g. a logical collection inside a physical collection is
a common thing.
&lt;/p&gt;
&lt;p&gt;
An &quot;Application Collection&quot; is a high-level abstractions to group something together
so it can be detected by software. The &quot;something&quot; is defined by the HID usage for this
collection. For example, you&#39;ll never guess what this device might be based on the
&lt;a href=&quot;https://github.com/hidutils/hid-recorder&quot;&gt;hid-recorder&lt;/a&gt; output:
&lt;pre&gt;
# 0x05, 0x01,                    // Usage Page (Generic Desktop)              0
# 0x09, 0x06,                    // Usage (Keyboard)                          2
# 0xa1, 0x01,                    // Collection (Application)                  4
...
# 0xc0,                          // End Collection                            74
&lt;/pre&gt;

Yep, it&#39;s a keyboard. Pop the champagne[3] and hooray, you deserve it.
&lt;/p&gt;
&lt;p&gt;
The kernel, ever eager to help, takes top-level application collections (i.e.
those not inside another collection) and applies a usage-specific suffix to the
device. For the above Generic Desktop/Keyboard usage you get &quot;Keyboard&quot;, the other
ones currently supported are &quot;Keypad&quot; and &quot;Mouse&quot; as well as the slightly more
niche &quot;System Control&quot;, &quot;Consumer Control&quot; and &quot;Wireless Radio Control&quot; and
&quot;System Multi Axis&quot;. In the Digitizer usage page we have &quot;Stylus&quot;, &quot;Pen&quot;,
&quot;Touchscreen&quot; and &quot;Touchpad&quot;. Any other Application Collection is
currently unsuffixed (though see [2] again, e.g. the hid-uclogic driver uses 
&quot;Touch Strip&quot; and other suffixes).
&lt;/p&gt;
&lt;p&gt;
This suffix is necessary because the kernel also splits out the data sent
within each collection as separate evdev event node. Since HID is (mostly)
hidden from userspace this makes it much easier for userspace to identify
different devices because you can look at a event node and say &quot;well, it has
buttons and x/y, so must be a mouse&quot; (this is exactly what udev does when applying
  the various &lt;code&gt;ID_INPUT&lt;/code&gt; properties, with varying
levels of success).
&lt;/p&gt;
&lt;p&gt;
The side effect of this however is that your device may show up as multiple
devices and &lt;i&gt;most of those extra devices will never send events&lt;/i&gt;. Sometimes
that is due to the device supporting multiple modes (e.g. a touchpad may by default emulate
a mouse for backwards compatibility but once the kernel toggles it to touchpad
mode the mouse feature is mute). Sometimes it&#39;s just laziness when vendors re-use
the same firmware and leave unused bits in place.
&lt;/p&gt;
&lt;p&gt;
It&#39;s &lt;i&gt;largely&lt;/i&gt; a cosmetic problem only, e.g. libinput treats every event
node as individual device and if there is a device that never sends events it
won&#39;t affect the other event nodes. It can cause user confusion though: &quot;why
does my laptop say there&#39;s a mouse?&quot; and in some cases it can cause functional
degradation - the two I can immediately recall are udev detecting the mouse
node of a touchpad as pointing stick (because i2c mice aren&#39;t a thing), hence
the pointing stick configuration may show up in unexpected places. And fake
mouse devices prevent features like &quot;disable touchpad if a mouse is plugged in&quot;
from working correctly. At the moment we don&#39;t have a good solution for detecting 
these fake devices - short of shipping giant databases with product-specific entries
we cannot easily detect which device is fake. After all, a Keyboard node on a
gaming mouse may only send events if the user configured the firmware to send 
keyboard events, and the same is true for a Mouse node on a gaming keyboard.
&lt;/p&gt;
&lt;p&gt;
So for now, the only solution to those is a per-user 
&lt;a href=&quot;https://wayland.freedesktop.org/libinput/doc/latest/ignoring-devices.html&quot;&gt;udev rule to ignore a device&lt;/a&gt;. If we ever
figure out a better fix, expect to find a gloating blog post in this very space.
&lt;/p&gt;

&lt;p&gt;
&lt;small&gt;
[1] input device naming is typically bonkers, so I&#39;m just sticking with precedence here&lt;br&gt;
[2] if there&#39;s a custom kernel driver this may not apply and there are quirks to change this so this isn&#39;t true for all devices&lt;br&gt;
[3] or sparkling wine, let&#39;s not be regionist here&lt;br&gt;
&lt;/small&gt;
&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/1329935646308529173/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/1329935646308529173' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/1329935646308529173'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/1329935646308529173'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2025/08/why-is-my-device-touchpad-and-mouse-and.html' title='Why is my device a touchpad and a mouse and a keyboard?'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-4888911876439819405</id><published>2025-08-11T11:44:00.004+10:00</published><updated>2025-08-11T11:44:58.391+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="xkb"/><title type='text'>xkeyboard-config 2.45 has a new install location</title><content type='html'>&lt;p&gt;
  This is a heads ups that if you install xkeyboard-config 2.45 (the package that provides the XKB &lt;b&gt;data&lt;/b&gt; files), some manual interaction may be needed. &lt;a href=&quot;https://xkeyboard-config.freedesktop.org/blog/2-45-release/#breaking-changes-2&quot;&gt;Version 2.45 has changed the install location&lt;/a&gt; after over 20 years to be a) more correct and b) more flexible.
&lt;/p&gt;
&lt;p&gt;
  When you select a keyboard layout like &quot;fr&quot; or &quot;de&quot; (or any other ones really), what typically happens in the background is that an XKB parser (xkbcomp if you&#39;re on X, libxkbcommon if you&#39;re on Wayland) goes off and parses the data files provided by xkeyboard-config to populate the layouts. For historical reasons these data files have resided in &lt;code&gt;/usr/share/X11/xkb&lt;/code&gt; and that directory is hardcoded in more places than it should be (i.e. more than zero).
  As of xkeyboard-config 2.45 however, the data files are now installed in the much more sensible directory &lt;code&gt;/usr/share/xkeyboard-config-2&lt;/code&gt; with a matching &lt;code&gt;xkeyboard-config-2.pc&lt;/code&gt; for anyone who relies on the data files. The old location is symlinked to the new location so everything keeps working, people are happy, no hatemail needs to be written, etc. Good times.
&lt;/p&gt;
&lt;p&gt;
  The reason for this change is two-fold: moving it to a package-specific directory opens up the (admittedly mostly theoretical) use-case of some other package providing XKB data files. But even more so, it finally allows us to start versioning the data files and introduce new formats that may be backwards-incompatible for current parsers. This is not yet the case however, the current format in the new location is &lt;b&gt;guaranteed&lt;/b&gt; to be the same as the format we&#39;ve always had, it&#39;s really just a location change in preparation for future changes.
&lt;/p&gt;
&lt;p&gt;
  Now, from an upstream perspective this is not just hunky, it&#39;s also dory. Distributions however struggle a bit more with this change because of packaging format restrictions. RPM for example is quite unhappy with a &lt;a href=&quot;https://docs.fedoraproject.org/en-US/packaging-guidelines/Directory_Replacement/&quot;&gt;directory being replaced by a symlink&lt;/a&gt; which means that &lt;a href=&quot;https://src.fedoraproject.org/rpms/xkeyboard-config/pull-request/3&quot;&gt;Fedora&lt;/a&gt; and &lt;a href=&quot;https://build.opensuse.org/request/show/1293638&quot;&gt;OpenSuSE&lt;/a&gt; have to resort to the &lt;code&gt;.rpmmoved&lt;/code&gt; hack. If you have ever used the &lt;a href=&quot;https://who-t.blogspot.com/2021/02/a-pre-supplied-custom-keyboard-layout.html&quot;&gt;custom layout&lt;/a&gt; and/or added other files to the XKB data files you will need to &lt;b&gt;manually move those files from &lt;code&gt;/usr/share/X11/xkb.rpmmoved/&lt;/code&gt; to the new equivalent location&lt;/b&gt;. If you have never used that layout and/or modified local you can just delete &lt;code&gt;/usr/share/X11/xkb.rpmmoved&lt;/code&gt;.  Of course, if you&#39;re on Wayland you shouldn&#39;t need to modify system directories anyway since you can do &lt;a href=&quot;https://xkbcommon.org/doc/current/user-configuration.html&quot;&gt;it in your $HOME&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
  Corresponding issues on what to do on &lt;a href=&quot;https://gitlab.archlinux.org/archlinux/packaging/packages/xkeyboard-config/-/issues/1&quot;&gt;Arch&lt;/a&gt; and &lt;a href=&quot;https://bugs.gentoo.org/957712&quot;&gt;Gentoo&lt;/a&gt;, I&#39;m not immediately aware of other distributions&#39;s issues but if you search for them in your bugtracker you&#39;ll find them.
&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/4888911876439819405/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/4888911876439819405' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/4888911876439819405'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/4888911876439819405'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2025/08/xkeyboard-config-245-has-new-install.html' title='xkeyboard-config 2.45 has a new install location'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-7454056039318003480</id><published>2025-08-07T20:00:00.003+10:00</published><updated>2025-08-07T20:00:55.696+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="libinput"/><title type='text'>libinput and Lua plugins (Part 2)</title><content type='html'>&lt;p&gt;Part 2 is, perhaps suprisingly, a follow-up to &lt;a href=&quot;https://who-t.blogspot.com/2025/05/libinput-and-lua-plugins.html&quot;&gt;libinput and lua-plugins (Part 1)&lt;/a&gt;.&lt;/p&gt;
  
 
&lt;p&gt;The moon has circled us a few times since that last post and some update is in order. First of all: all the internal work required for plugins was released as libinput 1.29 but that version does &lt;b&gt;not&lt;/b&gt; have any user-configurable plugins yet. But cry you not my little jedi and/or sith lord in training, because support for plugins has now been merged and, barring any significant issues, will be in libinput 1.30, due somewhen around October or November. This year. 2025 that is.
&lt;/p&gt;

&lt;p&gt;
  Which means now is the best time to jump in and figure out if your favourite bug can be solved with a plugin. And if so, let us know and if not, then definitely let us know so we can figure out if the API needs changes.
  The &lt;a href=&quot;https://wayland.freedesktop.org/libinput/doc/latest/lua-plugins.html&quot;&gt;API Documentation for Lua plugins&lt;/a&gt; is now online too and will auto-update as changes to it get merged. There have been a few minor changes to the API since the last post so please refer to the documentation for details. Notably, the version negotiation was re-done so both libinput and plugins can support select versions of the plugin API. This will allow us to iterate the API over time while designating some APIs as effectively LTS versions, minimising plugin breakages. Or so we hope.
&lt;/p&gt;
&lt;p&gt;
  What warrants a new post is that we merged a new feature for plugins, or rather, ahaha, a non-feature. Plugins now have an API accessible that allows them to disable certain internal features that are not publicly exposed, e.g. palm detection. The reason &lt;a href=&quot;http://who-t.blogspot.com/2016/04/why-libinput-doesnt-have-lot-of-config.html&quot;&gt;why libinput doesn&#39;t have a lot of configuration options&lt;/a&gt; have been explained previously (though we actually have &lt;a href=&quot;https://wayland.freedesktop.org/libinput/doc/latest/configuration.html&quot;&gt;quite a few options&lt;/a&gt;) but let me recap for this particular use-case: libinput doesn&#39;t have a config option for e.g. palm detection because we have several different palm detection heuristics and they depend on device capabilities. Very few people want no palm detection at all[1] so disabling it means you get a broken touchpad and we now get to add configuration options for every palm detection mechanism. And keep those supported forever because, well, &lt;a href=&quot;https://xkcd.com/1172/&quot;&gt;workflows&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
  But plugins are different, they are designed to take over some functionality. So the Lua API has a &lt;code&gt;EvdevDevice:disable_feature(&quot;touchpad-palm-detection&quot;)&lt;/code&gt; function that takes a string with the feature&#39;s name (easier to make backwards/forwards compatible this way). This example will disable &lt;b&gt;all&lt;/b&gt; palm detection within libinput and the plugin can implement said palm detection itself. At the time of writing, the following self-explanatory features can be disabled: &quot;button-debouncing&quot;, &quot;touchpad-hysteresis&quot;, &quot;touchpad-jump-detection&quot;, &quot;touchpad-palm-detection&quot;, &quot;wheel-debouncing&quot;. This list is mostly based on &quot;probably good enough&quot; so as above - if there&#39;s something else then we can expose that too.
&lt;/p&gt;
&lt;p&gt;
  So hooray for fewer features and happy implementing!
&lt;/p&gt;

&lt;p&gt;
  &lt;small&gt;
    [1] Something easily figured out by disabling palm detection or using a laptop where palm detection doesn&#39;t work thanks to device issues
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/7454056039318003480/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/7454056039318003480' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/7454056039318003480'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/7454056039318003480'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2025/08/libinput-and-lua-plugins-part-2.html' title='libinput and Lua plugins (Part 2)'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-6203820517985337269</id><published>2025-08-04T16:09:00.000+10:00</published><updated>2025-08-04T16:09:40.316+10:00</updated><title type='text'>unplug - a tool to test input devices via uinput</title><content type='html'>&lt;p&gt;Yet another day, yet another need for testing a device I don&#39;t have. That&#39;s fine and that&#39;s why many years ago I wrote &lt;code&gt;libinput record&lt;/code&gt; and &lt;code&gt;libinput replay&lt;/code&gt; (more powerful successors to evemu and evtest). Alas, this time I had a dependency on multiple devices to be present in the system, in a specific order, sending specific events. And juggling this many terminal windows with &lt;code&gt;libinput replay&lt;/code&gt; open was annoying. So I decided it&#39;s worth the time fixing this once and for all (haha, lolz) and wrote &lt;a href=&quot;https://gitlab.freedesktop.org/whot/unplug&quot;&gt;unplug&lt;/a&gt;. The target market for this is niche, but if you&#39;re in the same situation, it&#39;ll be quite useful.
&lt;/p&gt;

&lt;p&gt;
  Pictures cause a thousand words to finally shut up and be quiet so here&#39;s the screenshot after running &lt;code&gt;pip install unplug&lt;/code&gt;[1]:
&lt;/p&gt;
  &lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijlCOzK_KrkOa1GE-3ut009TtCAGE44HSRTSW9_DEXz2hWEx8NjGE8V1wyvG4ZcYHL_msUMrwr5_fPc0j3a7i6iCHAavbvkzFxO6KnZV19R7CTXAeZKE0H29qEjPvQNB8OYxi2IfbONc5bhyYnHDL58J_6WdYXPrfTTVtb996Qk5ILqEMV8-3LRFDDOJqT/s2586/Screenshot%20From%202025-08-04%2015-50-30.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;320&quot; data-original-height=&quot;1930&quot; data-original-width=&quot;2586&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijlCOzK_KrkOa1GE-3ut009TtCAGE44HSRTSW9_DEXz2hWEx8NjGE8V1wyvG4ZcYHL_msUMrwr5_fPc0j3a7i6iCHAavbvkzFxO6KnZV19R7CTXAeZKE0H29qEjPvQNB8OYxi2IfbONc5bhyYnHDL58J_6WdYXPrfTTVtb996Qk5ILqEMV8-3LRFDDOJqT/s320/Screenshot%20From%202025-08-04%2015-50-30.png&quot;/&gt;&lt;/a&gt;&lt;/div&gt;
  &lt;p&gt;
   This shows the currently pre-packaged set of recordings that you get for free when you install unplug. For your use-case you can run &lt;code&gt;libinput record&lt;/code&gt;, save the output in a directory and then start &lt;code&gt;unplug path/to/directory&lt;/code&gt;. The navigation is as expected, hitting enter on the devices plugs them in, hitting enter on the selected sequence sends that event sequence through the previously plugged device.
&lt;/p&gt;
&lt;p&gt;
  Annotation of the recordings (which must end in &lt;code&gt;.yml&lt;/code&gt; to be found) can be done by adding a YAML &lt;code&gt;unplug:&lt;/code&gt; entry with a &lt;code&gt;name&lt;/code&gt; and optionally a multiline &lt;code&gt;description&lt;/code&gt;. If you have recordings that should be included in the default set, please file a &lt;a href=&quot;https://gitlab.freedesktop.org/whot/unplug&quot;&gt;merge request&lt;/a&gt;. Happy emulating!
&lt;/p&gt;
&lt;p&gt;
  &lt;small&gt;
    [1] And allowing access to &lt;i&gt;/dev/uinput&lt;/i&gt;. Details, schmetails...
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/6203820517985337269/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/6203820517985337269' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/6203820517985337269'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/6203820517985337269'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2025/08/unplug-tool-to-test-input-devices-via.html' title='unplug - a tool to test input devices via uinput'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijlCOzK_KrkOa1GE-3ut009TtCAGE44HSRTSW9_DEXz2hWEx8NjGE8V1wyvG4ZcYHL_msUMrwr5_fPc0j3a7i6iCHAavbvkzFxO6KnZV19R7CTXAeZKE0H29qEjPvQNB8OYxi2IfbONc5bhyYnHDL58J_6WdYXPrfTTVtb996Qk5ILqEMV8-3LRFDDOJqT/s72-c/Screenshot%20From%202025-08-04%2015-50-30.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-105062532333643006</id><published>2025-06-19T11:44:00.000+10:00</published><updated>2025-06-19T11:44:02.379+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="libinput"/><title type='text'>libinput and tablet tool eraser buttons</title><content type='html'>&lt;p&gt;This is, to some degree, a followup to &lt;a href=&quot;https://who-t.blogspot.com/2014/09/stylus-behaviour-on-microsoft-surface-3.html&quot;&gt;this 2014 post&lt;/a&gt;. The TLDR of that is that, many a moon ago, the corporate overlords at Microsoft that decide all PC hardware behaviour &lt;a href=&quot;https://learn.microsoft.com/en-us/windows-hardware/design/component-guidelines/windows-pen-states&quot;&gt;decreed&lt;/a&gt; that the best way to handle an eraser emulation on a stylus is by having a button that is hardcoded in the firmware to, upon press, send a proximity out event for the pen followed by a proximity in event for the eraser tool. Upon release, they dogma&#39;d, said eraser button shall virtually move the eraser out of proximity followed by the pen coming back into proximity. Or, in other words, the pen simulates being inverted to use the eraser, at the push of a button. Truly the future, back in the happy times of the mid 20-teens.
&lt;/p&gt;
&lt;p&gt;In a world where you don&#39;t want to update your software for a new hardware feature, this of course makes perfect sense. In a world where you write software to handle such hardware features, significantly less so.&lt;/p&gt;

&lt;p&gt;
  Anyway, it is now 11 years later, the happy 2010s are over, and Benjamin and I have fixed this very issue in a few &lt;a href=&quot;https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/&quot;&gt;udev-hid-bpf&lt;/a&gt; programs but I wanted something that&#39;s a) more generic and b) configurable by the user. Somehow I am still convinced that disabling the eraser button at the udev-hid-bpf level will make users that use said button angry and, dear $deity, we can&#39;t have angry users, can we? So many angry people out there anyway, let&#39;s not add to that.
&lt;/p&gt;
&lt;p
  So after a few rather busy weeks (months?) and some three failed attempts and somewhere around 50-80 patches into libinput, I can now happily announce that libinput 1.29 will support re-mapable eraser buttons. That is, if you have a tool where one of the buttons is firmware-hardcoded to toggle into eraser mode you can now say &quot;No, this button is a button and it shall behave like a button, make it so!&quot;[1] and it shall happen this way. Pressing said eraser button will simply give you an e.g. BTN_STYLUS2 event or whatever you configured it to in your ecstatic anticipation of this new feature.
&lt;/p&gt;
&lt;p&gt;
  To get there, libinput&#39;s guts had to be changed. Previously libinput would read the kernel events, update the tablet state struct and then generate events based on various state changes. This of course works great when you e.g. get a button toggle, it doesn&#39;t work quite as great when your state change was one or two event frames ago (because prox-out of one tool, prox-in of another tool are at least 2 events). Extracing that older state change was like swapping the type of meatballs from an ikea meal after it&#39;s been served - doable in theory, but very messy.
&lt;/p&gt;
&lt;p&gt;
  Long story short, libinput now has a internal plugin system that can modify the evdev event stream as it comes in. It works like a pipeline, the events are passed from the kernel to the first plugin, modified, passed to the next plugin, etc. Eventually the last plugin is our actual tablet backend which will update tablet state, generate libinput events, and generally be grateful about having fewer quirks to worry about. With this architecture we can hold back the proximity events and filter them (if the eraser comes into proximity) or replay them (if the eraser does not come into proximity). The tablet backend is none the wiser, it either sees proximity events when those are valid or it sees a button event (depending on configuration).
&lt;/p&gt;
&lt;p&gt;
  This architecture approach is so successful that I have now switched a bunch of other internal features over to use that internal infrastructure (proximity timers, button debouncing, etc.). And of course it laid the ground work for the (presumably highly) anticipated &lt;a href=&quot;https://who-t.blogspot.com/2025/05/libinput-and-lua-plugins.html&quot;&gt;Lua plugin support&lt;/a&gt;. Either way, happy times. For a bit. Because for those not needing the eraser feature, we&#39;ve just increased your available tool button count by 100%[2] - now there&#39;s a headline for tech journalists that just blindly copy claims from blog posts.
&lt;/p&gt;
&lt;p&gt;
  &lt;small&gt;
  [1] Since this is a bit wordy, the libinput API call is just &lt;i&gt;libinput_tablet_tool_config_eraser_button_set_button()&lt;/i&gt;&lt;br&gt;
  [2] A very small number of styli have two buttons and an eraser button so those only get what, 50% increase? Anyway, that would make for a less clickbaity headline so let&#39;s handwave those away.&lt;br&gt;
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/105062532333643006/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/105062532333643006' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/105062532333643006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/105062532333643006'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2025/06/libinput-and-tablet-tool-eraser-buttons.html' title='libinput and tablet tool eraser buttons'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-2208813721864311812</id><published>2025-05-21T14:09:00.005+10:00</published><updated>2025-08-07T10:44:12.358+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="libinput"/><title type='text'>libinput and Lua plugins</title><content type='html'>&lt;p&gt;&lt;em&gt;First of all, what&#39;s outlined here &lt;strong&gt;should&lt;/strong&gt; be available in libinput &lt;strike&gt;1.29&lt;/strike&gt; 1.30 but I&#39;m not 100% certain on all the details yet so any feedback (in the libinput issue tracker) would be appreciated.  Right now this is all still sitting in the &lt;a href=&quot;https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1192&quot;&gt;libinput!1192&lt;/a&gt; merge request. I&#39;d specifically like to see some feedback from people familiar with Lua APIs. With this out of the way:&lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;Come libinput &lt;strike&gt;1.29&lt;/strike&gt; 1.30, libinput will support plugins written in Lua. These plugins sit logically between the kernel and libinput and allow modifying the evdev device and its events before libinput gets to see them.&lt;/p&gt;

&lt;p&gt;The motivation for this are a few unfixable issues - issues we knew &lt;b&gt;how&lt;/b&gt; to fix but we cannot actually implement and/or ship the fixes without breaking other devices. One example for this is the inverted Logitech MX Master 3S horizontal wheel. libinput ships quirks for the USB/Bluetooth connection but not for the Bolt receiver. Unlike the Unifying Receiver the Bolt receiver doesn&#39;t give the kernel sufficient information to know which device is currently connected. Which means our quirks could only apply to the Bolt receiver (and thus any mouse connected to it) - that&#39;s a rather bad idea though, we&#39;d break every other mouse using the same receiver. Another example is an issue with worn out mouse buttons - on that device the behavior was predictable enough but any heuristics would catch a lot of legitimate buttons. That&#39;s fine when you know your mouse is slightly broken and at least it works again. But it&#39;s not something we can ship as a general solution. There are plenty more examples like that - custom pointer deceleration, different disable-while-typing, etc.
&lt;/p&gt;
&lt;p&gt;
  libinput has quirks but they are internal API and subject to change without notice at any time. They&#39;re very definitely not for configuring a device and the local quirk file libinput parses is merely to bridge over the time until libinput ships the (hopefully upstreamed) quirk.
&lt;/p&gt;
&lt;p&gt;
  So the obvious solution is: let the users fix it themselves. And this is where the plugins come in. They are not full access into libinput, they are closer to a &lt;a href=&quot;https://who-t.blogspot.com/2024/04/udev-hid-bpf-quickstart-tooling-to-fix.html&quot;&gt;udev-hid-bpf&lt;/a&gt; in userspace. Logically they sit between the kernel event devices and libinput: input events are read from the kernel device, passed to the plugins, then passed to libinput. A plugin can look at and modify devices (add/remove buttons for example) and look at and modify the event stream as it comes from the kernel device. For this libinput changed internally to now process something called an &quot;evdev frame&quot; which is a struct that contains all &lt;i&gt;struct input_events&lt;/i&gt; up to the terminating &lt;i&gt;SYN_REPORT&lt;/i&gt;. This is the logical grouping of events anyway but so far we didn&#39;t explicitly carry those around as such. Now we do and we can pass them through to the plugin(s) to be modified.
&lt;/p&gt;
&lt;p&gt;
  The aforementioned Logitech MX master plugin would look like this: it registers itself with a version number, then sets a callback for the &quot;new-evdev-device&quot; notification and (where the device matches) we connect that device&#39;s &quot;evdev-frame&quot; notification to our actual code:
&lt;pre&gt;
libinput:register(1) -- register plugin version 1
libinput:connect(&quot;new-evdev-device&quot;, function (_, device)
    if device:vid() == 0x046D and device:pid() == 0xC548 then
        device:connect(&quot;evdev-frame&quot;, function (_, frame)
            for _, event in ipairs(frame.events) do
                if event.type == evdev.EV_REL and 
                   (event.code == evdev.REL_HWHEEL or 
                    event.code == evdev.REL_HWHEEL_HI_RES) then
                    event.value = -event.value
                end
            end
            return frame
        end)
    end
end)
&lt;/pre&gt;
This file can be dropped into &lt;i&gt;/etc/libinput/plugins/10-mx-master.lua&lt;/i&gt; and will be loaded on context creation. 
I&#39;m hoping the approach using named signals (similar to e.g. GObject) makes it easy to add different calls in future versions. Plugins also have access to a timer so you can filter events and re-send them at a later point in time. This is useful for implementing something like disable-while-typing based on certain conditions.
&lt;/p&gt;
&lt;p&gt;
  So why Lua? Because it&#39;s very easy to sandbox. I very explicitly did not want the plugins to be a side-channel to get into the internals of libinput - specifically no IO access to anything. This ruled out using C (or anything that&#39;s a .so file, really) because those would run a) in the address space of the compositor and b) be unrestricted in what they can do. Lua solves this easily. And, as a nice side-effect, it&#39;s also very easy to write plugins in.[1]
&lt;/p&gt;
&lt;p&gt;
  Whether plugins are loaded or not will depend on the compositor: an explicit call to set up the paths to load from and to actually load the plugins is required. No run-time plugin changes at this point either, they&#39;re loaded on libinput context creation and that&#39;s it. Otherwise, all the usual implementation details apply: files are sorted and if there are files with identical names the one from the highest-precedence directory will be used. Plugins that are buggy will be unloaded immediately.
&lt;/p&gt;
&lt;p&gt;
  If all this sounds interesting, please have a try and report back any APIs that are broken, or missing, or generally ideas of the good or bad persuation. Ideally before we ship it and the API is stable forever :)
&lt;/p&gt;
&lt;p&gt;
  &lt;small&gt;[1] Benjamin Tissoires actually had a go at WASM plugins (via rust). But ... a lot of effort for rather small gains over Lua&lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/2208813721864311812/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/2208813721864311812' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/2208813721864311812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/2208813721864311812'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2025/05/libinput-and-lua-plugins.html' title='libinput and Lua plugins'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-4691799641317172037</id><published>2025-02-24T15:38:00.003+10:00</published><updated>2025-02-24T15:38:55.313+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="libinput"/><title type='text'>libinput and 3-finger dragging</title><content type='html'>&lt;p&gt;
  Ready in time for libinput 1.28 [1] and after a number of attempts over the years we now finally have 3-finger dragging in libinput. This is a long-requested feature that allows users to drag by using a 3-finger swipe on the touchpad. Instead of the normal swipe gesture you simply get a button down, pointer motion, button up sequence. Without having to tap or physically click and hold a button, so you might be able to see the appeal right there.
&lt;/p&gt;
&lt;p&gt;
  Now, as with any interaction that relies on the mere handful of fingers that are on our average user&#39;s hand, we are starting to have usage overlaps. Since the only difference between a swipe gesture and a 3-finger drag is in the intention of the user (and we can&#39;t detect that yet, stay tuned), 3-finger swipes are disabled when 3-finger dragging is enabled. Otherwise it does fit in quite nicely with the rest of the features we have though.
  
&lt;/p&gt;

&lt;p&gt;
  There really isn&#39;t much more to say about the new feature except: 
  It&#39;s configurable to work on 4-finger drag too so if you mentally substitute all the threes with fours in this article before re-reading it that would save me having to write another blog post. Thanks.
&lt;/p&gt;

&lt;p&gt;
  &lt;small&gt;
    [1] &quot;soonish&quot; at the time of writing&lt;br&gt;
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/4691799641317172037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/4691799641317172037' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/4691799641317172037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/4691799641317172037'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2025/02/libinput-and-3-finger-dragging.html' title='libinput and 3-finger dragging'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-3705505984992357614</id><published>2025-02-24T14:17:00.003+10:00</published><updated>2025-02-24T14:17:29.957+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="gnome"/><title type='text'>GNOME 48 and a changed tap-and-drag drag lock behaviour</title><content type='html'>&lt;p&gt;
  This is a heads up as &lt;a href=&quot;https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4292#note_2356530&quot;&gt;mutter PR!4292&lt;/a&gt; got merged in time for GNOME 48. It (subtly) changes the behaviour of drag lock on touchpads, but (IMO) very much so for the better. Note that this feature is currently not exposed in GNOME Settings so users will have to set it via e.g. the gsettings commandline tool. I don&#39;t expect this change to affect many users.
&lt;/p&gt;
&lt;p&gt;
  This is a feature of a feature of a feature, so let&#39;s start at the top.
&lt;/p&gt;
&lt;p&gt;
  &quot;Tapping&quot; on touchpads refers to the ability to emulate button presses via short touches (&quot;taps&quot;) on the touchpad. When enabled, a single-finger tap corresponds emulates a left mouse button click, a two-finger tap a right button click, etc. Taps are short interactions and to be recognised the finger must be set down and released again within a certain time and not move more than a certain distance. Clicking is useful but it&#39;s not everything we do with touchpads.
&lt;p&gt;
&lt;p&gt;
  &quot;Tap-and-drag&quot; refers to the ability to keep the pointer down so it&#39;s possible to drag something while the mouse button is logically down. The sequence required to do this is a tap immediately followed by the finger down (and held down). This will press the left mouse button so that any finger movement results in a drag. Releasing the finger releases the button. This is convenient but especially on large monitors or for users with different-than-whatever-we-guessed-is-average dexterity this can make it hard to drag something to it&#39;s final position - a user may run out of touchpad space before the pointer reaches the destination. For those, the tap-and-drag &quot;drag lock&quot; is useful.
&lt;/p&gt;
&lt;p&gt;
  &quot;Drag lock&quot; refers to the ability of keeping the mouse button pressed until &quot;unlocked&quot;, even if the finger moves off the touchpads. It&#39;s the same sequence as before: tap followed by the finger down and held down. But releasing the finger will &lt;b&gt;not&lt;/b&gt; release the mouse button, instead another tap is required to unlock and release the mouse button. The whole sequence thus becomes tap, down, move.... tap with any number of finger releases in between. Sounds (and is) complicated to explain, is quite easy to try and once you&#39;re used to it it will feel quite natural.
&lt;/p&gt;
&lt;p&gt;
  The above behaviour is the new behaviour which non-coincidentally also matches the macOS behaviour (if you can find the toggle in the settings, good practice for easter eggs!). The previous behaviour used a timeout instead so the mouse button was released automatically if the finger was up after a certain timeout. This was less predictable and caused issues with users who weren&#39;t fast enough. The new &quot;sticky&quot; behaviour resolves this issue and is (alanis morissette-stylue ironically) faster to release (a tap can be performed before the previous timeout would&#39;ve expired).
&lt;/p&gt;
&lt;p&gt;
  Anyway, TLDR, a feature that very few people use has changed defaults subtly. Bring out the pitchforks!
&lt;/p&gt;
&lt;p&gt;
  As said above, this is currently only accessible via gsettings and the drag-lock behaviour change only takes effect if tapping, tap-and-drag &lt;b&gt;and&lt;/b&gt; drag lock are enabled:
  &lt;pre&gt;
  $ gsettings set org.gnome.desktop.peripherals.touchpad tap-to-click true
  $ gsettings set org.gnome.desktop.peripherals.touchpad tap-and-drag true
  $ gsettings set org.gnome.desktop.peripherals.touchpad tap-and-drag-lock true
  &lt;/pre&gt;
  All features above are actually handled by libinput, this is just about a default change in GNOME.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/3705505984992357614/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/3705505984992357614' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/3705505984992357614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/3705505984992357614'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2025/02/gnome-48-and-changed-tap-and-drag-drag.html' title='GNOME 48 and a changed tap-and-drag drag lock behaviour'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-18030263704022564</id><published>2024-12-18T13:21:00.000+10:00</published><updated>2024-12-18T13:21:14.686+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="libinput"/><title type='text'>A new issue policy for libinput - closing and reopening issues for fun and profit</title><content type='html'>&lt;p&gt;
  This is a heads up that if you file an issue in the &lt;a href=&quot;https://gitlab.freedesktop.org/libinput/libinput/-/issues&quot;&gt;libinput issue tracker&lt;/a&gt;, it&#39;s very likely this issue will be closed. And this post explains why that&#39;s a good thing, why it doesn&#39;t mean what you want, and most importantly why you shouldn&#39;t get angry about it.
&lt;/p&gt;
&lt;p&gt;
  Unfixed issues have, roughly, two states: they&#39;re either waiting for someone who can triage and ideally fix it (let&#39;s call those someones &quot;maintainers&quot;) or they&#39;re waiting on the reporter to provide some more info or test something. Let&#39;s call the former state &quot;actionable&quot; and the second state &quot;needinfo&quot;. The first state is typically not explicitly communicated but the latter can be via different means, most commonly via a &quot;needinfo&quot; label. Labels are of course great because you can be explicit about what is needed and with &lt;a href=&quot;http://who-t.blogspot.com/2023/07/gitlabfreedesktoporg-now-has-bugbot-for.html&quot;&gt;our bugbot&lt;/a&gt; you can automate much of this.
&lt;/p&gt;
&lt;p&gt;
  Alas, using labels has one disadvantage: GitLab does not allow the typical bug reporter to set or remove labels - you need to have at least the Planner role in the project (or group) and, well, suprisingly reporting an issue doesn&#39;t mean you get immediately added to the project. So setting a &quot;needinfo&quot; label requires the maintainer to remove the label. And until that happens you have a open bug that has needinfo set and looks like it&#39;s still needing info. Not a good look, that is.
&lt;/p&gt;
&lt;p&gt;
  So how about we use something other than labels, so the reporter can communicate that the bug has changed to actionable? Well, as it turns out there is exactly thing a reporter can do on their own bugs other than post comments: close it and re-open it. That&#39;s it [1]. So given this vast array of options (one button!), we shall use them (click it!).
&lt;/p&gt;
&lt;p&gt;
  So for the forseeable future libinput will follow the following pattern:
  &lt;ul&gt;
    &lt;li&gt;Reporter files an issue&lt;/li&gt;
    &lt;li&gt;Maintainer looks at it, posts a comment requesting some information, closes the bug&lt;/li&gt;
    &lt;li&gt;Reporter attaches information, re-opens bug&lt;/li&gt;
    &lt;li&gt;Maintainer looks at it and either: files a PR to fix the issue or closes the bug with the wontfix/notourbug/cantfix label&lt;/li&gt;
  &lt;/ul&gt;
  Obviously the close/reopen stage may happen a few times. For the final closing where the issue isn&#39;t fixed the labels actually work well: they preserve for posterity why the bug was closed and in this case they do not need to be changed by the reporter anyway. But until that final closing the result of this approach is that an open bug is a bug that is actionable for a maintainer.
&lt;/p&gt;
&lt;p&gt;
  This process should work (in libinput at least), all it requires is for reporters to not get grumpy about issue being closed. And that&#39;s where this blog post (and the comments bugbot will add when closing) come in. So here&#39;s hoping. And to stave off the first question: yes, I too wish there was a better (and equally simple) way to go about this.
&lt;/p&gt;
&lt;p&gt;
  &lt;small&gt;
    [1] we shall ignore magic comments that are parsed by language-understanding bots because that future isn&#39;t yet the present&lt;br&gt;
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/18030263704022564/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/18030263704022564' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/18030263704022564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/18030263704022564'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2024/12/a-new-issue-policy-for-libinput-closing.html' title='A new issue policy for libinput - closing and reopening issues for fun and profit'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-2835036459766477246</id><published>2024-11-19T11:54:00.000+10:00</published><updated>2024-11-19T11:54:16.914+10:00</updated><title type='text'>hidreport and hut: two crates for handling HID Report Descriptors and HID Reports</title><content type='html'>   &lt;p&gt;
  A while ago I was looking at Rust-based parsing of HID reports but, surprisingly, outside of C wrappers and the usual cratesquatting I couldn&#39;t find anything ready to use. So I figured, why not write my own, NIH style. Yay! Gave me a good excuse to learn API design for Rust and whatnot. Anyway, the result of this effort is the &lt;a href=&quot;https://github.com/hidutils/&quot; target=&quot;_blank&quot;&gt;hidutils collection of repositories&lt;/a&gt; which includes commandline tools like &lt;a href=&quot;https://github.com/hidutils/hid-recorder&quot; target=&quot;_blank&quot;&gt;hid-recorder&lt;/a&gt; and &lt;a href=&quot;https://github.com/hidutils/hid-replay&quot; target=&quot;_blank&quot;&gt;hid-replay&lt;/a&gt; but, more importantly, the &lt;a href=&quot;https://github.com/hidutils/hidreport/&quot; target=&quot;_blank&quot;&gt;hidreport&lt;/a&gt; (&lt;a href=&quot;https://docs.rs/hidreport/latest/hidreport/&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt;) and &lt;a href=&quot;https://github.com/hidutils/hut/&quot; target=&quot;_blank&quot;&gt;hut&lt;/a&gt; (&lt;a href=&quot;https://docs.rs/hut/latest/hut/&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt;) crates. Let&#39;s have a look at the latter two.
&lt;/p&gt;
&lt;p&gt;
  Both crates were intentionally written with minimal dependencies, they currently only depend on &lt;a href=&quot;https://docs.rs/thiserror/latest/thiserror/&quot; target=&quot;_blank&quot;&gt;thiserror&lt;/a&gt; and arguably even that dependency can be removed.
&lt;/p&gt;

&lt;h2&gt;HID Usage Tables (HUT)&lt;/h2&gt;
&lt;p&gt;
  As you know, HID Fields have a so-called &quot;Usage&quot; which is divided into a Usage Page (like a chapter) and a Usage ID. The HID Usage tells us what a sequence of bits in a HID Report represents, e.g. &quot;this is the X axis&quot; or &quot;this is button number 5&quot;. These usages are specified in the HID Usage Tables (HUT) (currently at &lt;a href=&quot;https://usb.org/sites/default/files/hut1_5.pdf&quot; target=&quot;_blank&quot;&gt;version 1.5 (PDF)&lt;/a&gt;). The &lt;a href=&quot;https://docs.rs/hut/latest/hut/&quot; target=&quot;_blank&quot;&gt;hut&lt;/a&gt; crate is generated from the official HUT json file and contains all current HID Usages together with the various conversions you will need to get from a numeric value in a report descriptor to the named usage and vice versa. Which means you can do things like this:
  &lt;pre&gt;
  let gd_x = GenericDesktop::X;
  let usage_page = gd_x.usage_page();
  assert!(matches!(usage_page, UsagePage::GenericDesktop));
  &lt;/pre&gt;
  Or the more likely need: convert from a numeric page/id tuple to a named usage.
  &lt;pre&gt;
  let usage = Usage::new_from_page_and_id(0x1, 0x30); // GenericDesktop / X
  println!(&quot;Usage is {}&quot;, usage.name());
  &lt;/pre&gt;
  90% of this crate are the various conversions from a named usage to the numeric value and vice versa. It&#39;s a huge crate in that there are lots of enum values but the actual functionality is relatively simple.
&lt;/p&gt;

&lt;h2&gt;hidreport - Report Descriptor parsing&lt;/h2&gt;
&lt;p&gt;
  The &lt;a href=&quot;https://docs.rs/hidreport/latest/hidreport/&quot; target=&quot;_blank&quot;&gt;hidreport&lt;/a&gt; crate is the one that can take a set of HID Report Descriptor bytes obtained from a device and parse the contents. Or extract the value of a HID Field from a HID Report, given the HID Report Descriptor. So let&#39;s assume we have a bunch of bytes that are HID report descriptor read from the device (or sysfs) we can do this:
  
  &lt;pre&gt;
  let rdesc: ReportDescriptor = ReportDescriptor::try_from(bytes).unwrap();
  &lt;/pre&gt;
  I&#39;m not going to copy/paste the code to run through this report descriptor but suffice to day it will give us access to the input, output and feature reports on the device together with every field inside those reports. Now let&#39;s read from the device and parse the data for whatever the first field is in the report (this is obviously device-specific, could be a button, a coordinate, anything):
  &lt;pre&gt;
   let input_report_bytes = read_from_device();
   let report = rdesc.find_input_report(&amp;input_report_bytes).unwrap();
   let field = report.fields().first().unwrap();
   match field {
       Field::Variable(var) =&gt; {
          let val: u32 = var.extract(&amp;input_report_bytes).unwrap().into();
          println!(&quot;Field {:?} is of value {}&quot;, field, val);
       },
       _ =&gt; {}
   }
  &lt;/pre&gt;
The full documentation is of course on &lt;a href=&quot;https://docs.rs/hidreport/latest/hidreport/&quot; target=&quot;_blank&quot;&gt;docs.rs&lt;/a&gt; and I&#39;d be happy to take suggestions on how to improve the API and/or add features not currently present.

&lt;/p&gt;

&lt;h2&gt;hid-recorder&lt;/h2&gt;

&lt;p&gt;The hidreport and hut crates are still quite new but we have an existing test bed that we use regularly. The venerable hid-recorder tool has been rewritten twice already. Benjamin Tissoires&#39; first version was in C, then a Python version of it became part of &lt;a href=&quot;https://gitlab.freedesktop.org/libevdev/hid-tools/&quot; target=&quot;_blank&quot;&gt;hid-tools&lt;/a&gt; and now we have the third version written in Rust. Which has a few nice features over the Python version and we&#39;re using it heavily for e.g. &lt;a href=&quot;https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/&quot; target=&quot;_blank&quot;&gt;udev-hid-bpf&lt;/a&gt; debugging and development. An examle output of that is below and it shows that you can get all the information out of the device via the hidreport and hut crates.
&lt;pre&gt;
$ sudo hid-recorder /dev/hidraw1
# Microsoft Microsoft® 2.4GHz Transceiver v9.0
# Report descriptor length: 223 bytes
# 0x05, 0x01,                    // Usage Page (Generic Desktop)              0
# 0x09, 0x02,                    // Usage (Mouse)                             2
# 0xa1, 0x01,                    // Collection (Application)                  4
# 0x05, 0x01,                    //   Usage Page (Generic Desktop)            6
# 0x09, 0x02,                    //   Usage (Mouse)                           8
# 0xa1, 0x02,                    //   Collection (Logical)                    10
# 0x85, 0x1a,                    //     Report ID (26)                        12
# 0x09, 0x01,                    //     Usage (Pointer)                       14
# 0xa1, 0x00,                    //     Collection (Physical)                 16
# 0x05, 0x09,                    //       Usage Page (Button)                 18
# 0x19, 0x01,                    //       UsageMinimum (1)                    20
# 0x29, 0x05,                    //       UsageMaximum (5)                    22
# 0x95, 0x05,                    //       Report Count (5)                    24
# 0x75, 0x01,                    //       Report Size (1)                     26
... omitted for brevity
# 0x75, 0x01,                    //     Report Size (1)                       213
# 0xb1, 0x02,                    //     Feature (Data,Var,Abs)                215
# 0x75, 0x03,                    //     Report Size (3)                       217
# 0xb1, 0x01,                    //     Feature (Cnst,Arr,Abs)                219
# 0xc0,                          //   End Collection                          221
# 0xc0,                          // End Collection                            222
R: 223 05 01 09 02 a1 01 05 01 09 02 a1 02 85 1a 09 ... omitted for previty
N: Microsoft Microsoft® 2.4GHz Transceiver v9.0
I: 3 45e 7a5
# Report descriptor:
# ------- Input Report -------
# Report ID: 26
#    Report size: 80 bits
#  |   Bit:    8       | Usage: 0009/0001: Button / Button 1                          | Logical Range:     0..=1     |
#  |   Bit:    9       | Usage: 0009/0002: Button / Button 2                          | Logical Range:     0..=1     |
#  |   Bit:   10       | Usage: 0009/0003: Button / Button 3                          | Logical Range:     0..=1     |
#  |   Bit:   11       | Usage: 0009/0004: Button / Button 4                          | Logical Range:     0..=1     |
#  |   Bit:   12       | Usage: 0009/0005: Button / Button 5                          | Logical Range:     0..=1     |
#  |   Bits:  13..=15  | ######### Padding                                            |
#  |   Bits:  16..=31  | Usage: 0001/0030: Generic Desktop / X                        | Logical Range: -32767..=32767 |
#  |   Bits:  32..=47  | Usage: 0001/0031: Generic Desktop / Y                        | Logical Range: -32767..=32767 |
#  |   Bits:  48..=63  | Usage: 0001/0038: Generic Desktop / Wheel                    | Logical Range: -32767..=32767 | Physical Range:     0..=0     |
#  |   Bits:  64..=79  | Usage: 000c/0238: Consumer / AC Pan                          | Logical Range: -32767..=32767 | Physical Range:     0..=0     |
# ------- Input Report -------
# Report ID: 31
#    Report size: 24 bits
#  |   Bits:   8..=23  | Usage: 000c/0238: Consumer / AC Pan                          | Logical Range: -32767..=32767 | Physical Range:     0..=0     |
# ------- Feature Report -------
# Report ID: 18
#    Report size: 16 bits
#  |   Bits:   8..=9   | Usage: 0001/0048: Generic Desktop / Resolution Multiplier    | Logical Range:     0..=1     | Physical Range:     1..=12    |
#  |   Bits:  10..=11  | Usage: 0001/0048: Generic Desktop / Resolution Multiplier    | Logical Range:     0..=1     | Physical Range:     1..=12    |
#  |   Bits:  12..=15  | ######### Padding                                            |
# ------- Feature Report -------
# Report ID: 23
#    Report size: 16 bits
#  |   Bits:   8..=9   | Usage: ff00/ff06: Vendor Defined Page 0xFF00 / Vendor Usage 0xff06 | Logical Range:     0..=1     | Physical Range:     1..=12    |
#  |   Bits:  10..=11  | Usage: ff00/ff0f: Vendor Defined Page 0xFF00 / Vendor Usage 0xff0f | Logical Range:     0..=1     | Physical Range:     1..=12    |
#  |   Bit:   12       | Usage: ff00/ff04: Vendor Defined Page 0xFF00 / Vendor Usage 0xff04 | Logical Range:     0..=1     | Physical Range:     0..=0     |
#  |   Bits:  13..=15  | ######### Padding                                            |
##############################################################################
# Recorded events below in format:
# E: &lt;seconds&gt;.&lt;microseconds&gt; &lt;length-in-bytes&gt; [bytes ...]
#
# Current time: 11:31:20
# Report ID: 26 /
#                Button 1:     0 | Button 2:     0 | Button 3:     0 | Button 4:     0 | Button 5:     0 | X:     5 | Y:     0 |
#                Wheel:     0 |
#                AC Pan:     0 |
E: 000000.000124 10 1a 00 05 00 00 00 00 00 00 00
  &lt;/pre&gt;
  
&lt;/p&gt;
</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/2835036459766477246/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/2835036459766477246' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/2835036459766477246'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/2835036459766477246'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2024/11/hidreport-and-hut-two-crates-for.html' title='hidreport and hut: two crates for handling HID Report Descriptors and HID Reports'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-5576489425191643261</id><published>2024-10-04T10:27:00.002+10:00</published><updated>2024-10-04T10:27:16.380+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="hid"/><title type='text'>HIOCREVOKE merged for kernel 6.12</title><content type='html'>&lt;p&gt;
  TLDR: if you know what &lt;b&gt;EVIOCREVOKE&lt;/b&gt; does, the same now works for hidraw devices via &lt;b&gt;HIDIOCREVOKE&lt;/b&gt;.
&lt;/p&gt;
&lt;p&gt;
  The &lt;a href=&quot;https://who-t.blogspot.com/2018/12/understanding-hid-report-descriptors.html&quot;&gt;HID standard&lt;/a&gt; is the most common hardware protocol for input devices. In the Linux kernel HID is typically translated to the &lt;a href=&quot;https://who-t.blogspot.com/2016/09/understanding-evdev.html&quot;&gt;evdev protocol&lt;/a&gt; which is what libinput and all Xorg input drivers use. evdev is the kernel&#39;s input API and used for all devices, not just HID ones.
&lt;/p&gt;
&lt;p&gt;
  
  evdev is &lt;i&gt;mostly&lt;/i&gt; compatible with HID but there are quite a few niche cases where they differ a fair bit. And some cases where evdev doesn&#39;t work well because of different assumptions, e.g. it&#39;s near-impossible to correctly express a device with 40 generic buttons (as opposed to named buttons like &quot;left&quot;, &quot;right&quot;, ...[0]). In particular for gaming devices it&#39;s quite common to access the HID device directly via the &lt;i&gt;/dev/hidraw&lt;/i&gt; nodes. And of course for configuration of devices accessing the hidraw node is a must too (see Solaar, openrazer, libratbag, etc.). Alas, &lt;i&gt;/dev/hidraw&lt;/i&gt; nodes are only accessible as root - right now applications work around this by either &quot;run as root&quot; or shipping udev rules tagging the device with uaccess.
&lt;/p&gt;
&lt;p&gt;
  evdev too can only be accessed as root (or the input group) but many many moons ago when dinosaurs still roamed the earth (version 3.12 to be precise), David Rheinsberg merged the &lt;b&gt;EVIOCREVOKE&lt;/b&gt; ioctl. When called the file descriptor immediately becomes invalid, any further reads/writes will fail with &lt;i&gt;ENODEV&lt;/i&gt;. This is a cornerstone for systemd-logind: it hands out a file descriptor via DBus to Xorg or the Wayland compositor but keeps a copy. On VT switch it calls the ioctl, thus preventing any events from reaching said X server/compositor. In turn this means that a) X no longer needs to run as root[1] since it can get input devices from logind and b) X loses access to those input devices at logind&#39;s leisure so we don&#39;t have to worry about leaking passwords.
&lt;/p&gt;
&lt;p&gt;
  Real-time forward to 2024 and kernel 6.12 now gained the &lt;b&gt;HIDIOCREVOKE&lt;/b&gt; for &lt;i&gt;/dev/hidraw&lt;/i&gt; nodes. The corresponding &lt;a href=&quot;https://github.com/systemd/systemd/pull/33970&quot;&gt;logind support&lt;/a&gt; has also been merged. The principle is the same: logind can hand out an fd to a hidraw node and can revoke it at will so we don&#39;t have to worry about data leakage to processes that should not longer receive events. This is the first of many steps towards more general HID support in userspace. It&#39;s not immediately usable since logind will only hand out those fds to the session leader (read: compositor or Xorg) so if you as application want that fd you need to convince your display server to give it to you. For that we &lt;i&gt;may&lt;/i&gt; have something like the &lt;a href=&quot;https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/110#note_1080252&quot;&gt;inputfd Wayland protocol&lt;/a&gt; (or maybe a &lt;a href=&quot;https://github.com/flatpak/xdg-desktop-portal/issues/611&quot; target=&quot;_blank&quot;&gt;portal&lt;/a&gt; but right now it seems a Wayland protocol is more likely).
  
  But that aside, let&#39;s hooray nonetheless. One step down, many more to go.
&lt;/p&gt;
&lt;p&gt;
  One of the other side-effects of this is that logind now has an fd to any device opened by a user-space process. With &lt;a href=&quot;https://who-t.blogspot.com/2024/04/udev-hid-bpf-quickstart-tooling-to-fix.html&quot;&gt;HID-BPF&lt;/a&gt; this means we can eventually &quot;firewall&quot; these devices from malicious applications: we could e.g. allow libratbag to configure your mouse&#39; buttons but block any attempts to upload a new firmware. This is very much an idea for now, there&#39;s a lot of code that needs to be written to get there. But getting there we can now, so full of optimism we go[2].
&lt;/p&gt;


&lt;p&gt;
  &lt;small&gt;
    [0] to illustrate: the button that goes back in your browser is actually evdev&#39;s &lt;i&gt;BTN_SIDE&lt;/i&gt; and &lt;i&gt;BTN_BACK&lt;/i&gt; is ... just another button assigned to nothing particular by default.&lt;br&gt;
    [1] and c) I have to care less about X server CVEs.&lt;br&gt;
    [2] mind you, optimism is just another word for naïveté&lt;br&gt;
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/5576489425191643261/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/5576489425191643261' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/5576489425191643261'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/5576489425191643261'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2024/10/hiocrevoke-merged-for-kernel-612.html' title='HIOCREVOKE merged for kernel 6.12'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-162985663517883833</id><published>2024-06-26T14:59:00.002+10:00</published><updated>2024-06-26T14:59:49.785+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="gnome"/><title type='text'>GNOME tablet support papercut fixes</title><content type='html'>&lt;p&gt;
  Over the last months I&#39;ve started looking into a few of the papercuts that affects graphics tablet users in GNOME. So now that most of those have gone in, let&#39;s see what has happened:
&lt;/p&gt;

&lt;h2&gt;Calibration fixes and improvements (GNOME 47)&lt;/h2&gt;
 
&lt;p&gt;The calibration code, a descendent of the old xinput_calibrator tool was in a pretty rough shape and didn&#39;t work particularly well. That&#39;s now fixed and I&#39;ve made the calibrator a little bit easier to use too. Previously the timeout was quite short which made calibration quite stressfull, that timeout is now per target rather than to complete the whole calibration process. Likewise, the calibration targets now accept larger variations - something probably not needed for real use-cases (you want the calibration to be exact) but it certainly makes testing easier since clicking near the target is good enough.&lt;/p&gt;
&lt;p&gt;The other feature added was to allow calibration even when the tablet is manually mapped to a monitor. Previously this only worked in the &quot;auto&quot; configuration but some tablets don&#39;t correctly map to the right screen and lost calibration abilities. That&#39;s fixed now too.&lt;/p&gt;

&lt;p&gt;A picture says a thousand words, except in this case where the screenshot provides no value whatsoever. But here you have it anyway.&lt;/p&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLtwW_iPWD4jBZUEmmB9EcXlTCUMh9sDHo0p1OcDpcQDSr9qBBi409wjkfDY6q-yLzhZX9RKwxFtDPHq4RscTAXzLYeiQYH7pVAX7mfxP9LQyWA3ek2bwyhyphenhyphenw8lNNdq4pwG2RkYpO7oePLLaGyV7c343y0gRfoXEEmS_piaaPSDAzRRWTVUgrRR4tddbwR/s3840/Screenshot%20from%202024-06-26%2014-56-30.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;320&quot; data-original-height=&quot;2160&quot; data-original-width=&quot;3840&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLtwW_iPWD4jBZUEmmB9EcXlTCUMh9sDHo0p1OcDpcQDSr9qBBi409wjkfDY6q-yLzhZX9RKwxFtDPHq4RscTAXzLYeiQYH7pVAX7mfxP9LQyWA3ek2bwyhyphenhyphenw8lNNdq4pwG2RkYpO7oePLLaGyV7c343y0gRfoXEEmS_piaaPSDAzRRWTVUgrRR4tddbwR/s320/Screenshot%20from%202024-06-26%2014-56-30.png&quot;/&gt;&lt;/a&gt;&lt;/div&gt;


&lt;h2&gt;Generic tablet fallback (GNOME 47)&lt;/h2&gt;
&lt;p&gt;
  Traditionally, GNOME would rely on libwacom to get some information about tablets so it could present users with the right configuration options. The drawback was that a tablet not recognised by libwacom didn&#39;t exist in GNOME Settings - and there was no immediately obvious way of fixing this, the panel either didn&#39;t show up or (with multiple tablets) the unrecognised one was missing. The tablet worked (because the kernel and libinput didn&#39;t require libwacom) but it just couldn&#39;t be configured.
&lt;/p&gt;
&lt;p&gt;
  libwacom 2.11 changed the default fallback tablet to be a built-in one since this is now the most common unsupported tablet we see. Together with the new fallback handling in GNOME settings this means that any unsupported tablet is treated as a generic built-in tablet and provides the basic configuration options for those (Map to Monitor, Calibrate, assigning stylus buttons). The tablet &lt;i&gt;should&lt;/i&gt; still be added to libwacom but at least it&#39;s no longer a requirement for configuration. Plus there&#39;s now a link to the GNOME Help to explain things. Below is a screenshot on how this looks like (after modifying my libwacom to no longer recognise the tablet, poor Intuos).
&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBG39YFZUs_eqY-HzPavJRCBqCaLBDEIX_IHal3IAn_sB5sS8VOsxEeDb0QGGYtOo1x59cYoUEL0XO5izbwl23hU40r0oS6CwFEggqyG5bP8AbqZoFUsnMmZH-Dli5C3ZHnuc8NnSmFeNZLhl1Y8jzK67qczOrAeiJ4EMqjq54nCi6C09DJiD2DyvMrxC1/s927/Screenshot%20from%202024-06-26%2014-41-07.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;320&quot; data-original-height=&quot;490&quot; data-original-width=&quot;927&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBG39YFZUs_eqY-HzPavJRCBqCaLBDEIX_IHal3IAn_sB5sS8VOsxEeDb0QGGYtOo1x59cYoUEL0XO5izbwl23hU40r0oS6CwFEggqyG5bP8AbqZoFUsnMmZH-Dli5C3ZHnuc8NnSmFeNZLhl1Y8jzK67qczOrAeiJ4EMqjq54nCi6C09DJiD2DyvMrxC1/s320/Screenshot%20from%202024-06-26%2014-41-07.png&quot;/&gt;&lt;/a&gt;
&lt;/div&gt;

&lt;h2&gt;Monitor mapping names (GNOME 47)&lt;/h2&gt;
&lt;p&gt;
  For historical reasons, the names of the display in the GNOME Settings Display configuration differed from the one used by the Wacom panel. Not ideal and that bit is now fixed with the Wacom panel listing the name of the monitor and the connector name if multiple monitors share the same name. You get the best value out of this if you have a monitor vendor with short names. (This is not a purchase recommendation).
&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQQV-6mUdcEC7dYU7L1irf-9Fq-t3U84Ju8JdZ5YJi8ycM7xnODIJzLfTbxuPyqLHnITtu9YLGDLzc312pAIK0Nvr1o_6ckcR7XF_Wd3dW04e_mC9tt3pZTXe2vbYk74hHNkSQnDgJV1bH4YB_n_xXgjQJsJeyftLk2PINNO4Cq7IS2ufY28PXp2b4QCdi/s955/Screenshot%20from%202024-06-26%2014-37-10.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;320&quot; data-original-height=&quot;281&quot; data-original-width=&quot;955&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQQV-6mUdcEC7dYU7L1irf-9Fq-t3U84Ju8JdZ5YJi8ycM7xnODIJzLfTbxuPyqLHnITtu9YLGDLzc312pAIK0Nvr1o_6ckcR7XF_Wd3dW04e_mC9tt3pZTXe2vbYk74hHNkSQnDgJV1bH4YB_n_xXgjQJsJeyftLk2PINNO4Cq7IS2ufY28PXp2b4QCdi/s320/Screenshot%20from%202024-06-26%2014-37-10.png&quot;/&gt;&lt;/a&gt;&lt;/div&gt;

&lt;h2&gt;Highlighted SVGs (GNOME 46)&lt;/h2&gt;
&lt;p&gt;
  If you&#39;re an avid tablet user, you may have multiple stylus tools - but it&#39;s also likely that you have multiple tools of the same type which makes differentiating them in the GUI hard. Which is why they&#39;re highlighted now - if you bring the tool into proximity, the matching image is highlighted to make it easier to know which stylus you&#39;re about to configure. Oh, and in the process we added a new SVG for AES styli too to make the picture look more like the actual physical tool. The &amp;lt;blink&amp;gt; tag may no longer be cool but at least we can disco our way through the stylus configuration now.
  
  &lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.blogger.com/video.g?token=AD6v5dwA12lGRbt57ksVF_FUOqDAUNspX_7x5ij1UnyXphckuffzsKefR0yYFf91RGA9Nh24z-UEhDakV7v2s8sElQ&#39; class=&#39;b-hbp-video b-uploaded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;/p&gt;

&lt;h2&gt;More Pressure Curves (GNOME 46)&lt;/h2&gt;
&lt;p&gt;
  GNOME Settings historically presents a slider from &quot;Soft&quot; to &quot;Firm&quot; to adjust the feel of the tablet tip (which influences the pressure values sent to the application). Behind the scenes this was converted into a set of 7 fixed curves but thanks to a &lt;a href=&quot;https://gitlab.gnome.org/GNOME/mutter/-/issues/3158&quot; target=&quot;_blank&quot;&gt;old mutter bug&lt;/a&gt; those curves only covered a small amount of the possible range. This is now fixed so you can really go from pencil-hard to jelly-soft and the slider now controls an almost-continous range instead of just 7 curves. Behold, a picture of slidery goodness:
&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsIl2E_y9vKEU-Iwah8U284SMBNQCTmFdS5q0jSiPZWXlRBLWWYyRqLW3zNcvLrFCUz74Vpy7oiBsML-C8KVy_Ujkb8ZSCrrFFN18I_iru-eibulcirjgJJ2nXOjGbWH5CXYF-uemyyyZyc8Kpd_84frPgz63_45uTnhMp91GcVDkFttwCZfZ-2ZqVc9mU/s946/Screenshot%20from%202024-06-26%2014-38-20.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;320&quot; data-original-height=&quot;88&quot; data-original-width=&quot;946&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsIl2E_y9vKEU-Iwah8U284SMBNQCTmFdS5q0jSiPZWXlRBLWWYyRqLW3zNcvLrFCUz74Vpy7oiBsML-C8KVy_Ujkb8ZSCrrFFN18I_iru-eibulcirjgJJ2nXOjGbWH5CXYF-uemyyyZyc8Kpd_84frPgz63_45uTnhMp91GcVDkFttwCZfZ-2ZqVc9mU/s320/Screenshot%20from%202024-06-26%2014-38-20.png&quot;/&gt;&lt;/a&gt;&lt;/div&gt;


&lt;h2&gt;Miscellaneous fixes&lt;/h2&gt;
&lt;p&gt;
  And of course a bunch of miscellaneous fixes. Things that I quickly found were support for &lt;i&gt;Alt&lt;/i&gt; in the tablet pad keymappings, fixing of erroneous backwards movement when wrapping around on the ring, a long-standing stylus button mismatch, better stylus naming and a rather odd fix causing configuration issues if the eraser was the first tool ever to be brought into proximity.
&lt;/p&gt;

&lt;p&gt;
  There are a few more things in the pipe but I figured this is enough to write a blog post so I no longer have to remember to write a blog post about all this.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/162985663517883833/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/162985663517883833' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/162985663517883833'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/162985663517883833'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2024/06/gnome-tablet-support-papercut-fixes.html' title='GNOME tablet support papercut fixes'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLtwW_iPWD4jBZUEmmB9EcXlTCUMh9sDHo0p1OcDpcQDSr9qBBi409wjkfDY6q-yLzhZX9RKwxFtDPHq4RscTAXzLYeiQYH7pVAX7mfxP9LQyWA3ek2bwyhyphenhyphenw8lNNdq4pwG2RkYpO7oePLLaGyV7c343y0gRfoXEEmS_piaaPSDAzRRWTVUgrRR4tddbwR/s72-c/Screenshot%20from%202024-06-26%2014-56-30.png" height="72" width="72"/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-7054682772701394801</id><published>2024-06-06T16:22:00.000+10:00</published><updated>2024-06-06T16:22:07.469+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="gnome"/><category scheme="http://www.blogger.com/atom/ns#" term="wacom"/><title type='text'>goodbye xsetwacom, hello gsetwacom </title><content type='html'>&lt;p&gt;
  Back in the day when presumably at least someone was young, the venerable xsetwacom tool was commonly used to configure wacom tablets devices on Xorg [1]. This tool is &lt;a href=&quot;https://who-t.blogspot.com/2016/12/the-future-of-xinput-xmodmap-setxkbmap.html&quot;&gt;going dodo in Wayland&lt;/a&gt; because, well, a tool that is specific to an X input driver kinda stops working when said X input driver is no longer being used. Such is technology, let&#39;s go back to sheep farming.
&lt;/p&gt;

&lt;p&gt;
  There&#39;s nothing hugely special about xsetwacom, it&#39;s effectively identical to the xinput commandline tool except for the CLI that guides you towards the various wacom driver-specific properties and knows the right magic values to set. Like xinput, xsetwacom has one big peculiarity: it is a fire-and-forget tool and nothing is persistent - unplugging the device or logging out would vanish the current value without so much as a &quot;poof&quot; noise [2]. 
&lt;/p&gt;
&lt;p&gt;
  If also somewhat clashes with GNOME (or any DE, really). GNOME configuration works so that GNOME Settings (gnome-control-center) and GNOME Tweaks write the various values to the gsettings. mutter [3] picks up changes to those values and in response toggles the X driver properties (or in Wayland the libinput context). xsetwacom short-cuts that process by writing directly to the driver but properties are &quot;last one wins&quot; so there were plenty of use-cases over the years where changes by xsetwacom were overwritten.
&lt;/p&gt;
&lt;p&gt;
  Anyway, there are plenty of use-cases where xsetwacom is actually quite useful, in particular where tablet behaviour needs to be scripted, e.g. switching between pressure curves at the press of a button or key. But xsetwacom cannot work under Wayland because a) the xf86-input-wacom driver is no longer in use, b) only the compositor (i.e. mutter) has access to the libinput context (and some behaviours are now implemented in the compositor anyway) and c) we&#39;re constantly trying to think of new ways to make life worse for angry commenters on the internets. So if xsetwacom cannot work, what can we do?
&lt;/p&gt;
&lt;p&gt;
  Well, &lt;i&gt;most&lt;/i&gt; configurations possible with xsetwacom are actually available in GNOME. So let&#39;s make those available to a commandline utility! And voila, I present to you &lt;a href=&quot;https://github.com/linuxwacom/gsetwacom&quot;&gt;gsetwacom&lt;/a&gt;, a commandline utility to toggle the various tablet settings under GNOME:
  &lt;pre&gt;
$ gsetwacom list-devices
devices:
- name: &quot;HUION Huion Tablet_H641P Pen&quot;
  usbid: &quot;256C:0066&quot;
- name: &quot;Wacom Intuos Pro M Pen&quot;
  usbid: &quot;056A:0357&quot;
 
$ gsetwacom tablet &quot;056A:0357&quot; set-left-handed true
$ gsetwacom tablet &quot;056A:0357&quot; set-button-action A keybinding &quot;&amp;lt;Control&amp;gt;&amp;lt;Alt&amp;gt;t&quot;
$ gsetwacom tablet &quot;056A:0357&quot; map-to-monitor --connector DP-1
  &lt;/pre&gt;
&lt;/p&gt;
&lt;p&gt;
  Just like xsetwacom was effectively identical to xinput but with a domain-specific CLI, gsetwacom is effectively identical to the gsettings tool but with a domain-specific CLI. &lt;b&gt;gsetwacom is not intended to be a drop-in replacement for xsetwacom&lt;/b&gt;, the CLI is very different. That&#39;s mostly on purpose because I don&#39;t want to have to chase bug-for-bug compatibility for something that is very different after all.
&lt;/p&gt;
&lt;p&gt;
  I &lt;i&gt;almost&lt;/i&gt; spent more time writing this blog post than on the implementation so it&#39;s still a bit rough. Also, (partially) due to how relocatable schemas work error checking is virtually nonexistent - if you want to configure Button 16 on your 2-button tablet device you can do that. Just don&#39;t expect 14 new buttons to magically sprout from your tablet. This could all be worked around with e.g. libwacom integration but right now I&#39;m too lazy for that [4]
&lt;/p&gt;
&lt;p&gt;
  Oh, and because gsetwacom writes the gsettings configuration it is persistent, GNOME Settings will pick up those values and they&#39;ll be re-applied by mutter after unplug. And because mutter-on-Xorg still works, gsetwacom will work the same under Xorg. It&#39;ll also work under the GNOME derivatives as long as they use the same gsettings schemas and keys.
&lt;/p&gt;
&lt;p&gt;
   Le utilitaire est mort, vive le utilitaire!
&lt;/p&gt;
&lt;p&gt;
  &lt;small&gt;
  [1] The git log claims libwacom was originally written in 2009. By me. That was a surprise...&lt;br&gt;
  [2] Though if you have the same speakers as I do you at least get a loud &quot;pop&quot; sound whenever you log in/out and the speaker gets woken up&lt;br&gt;
  [3] It used to be gnome-settings-daemon but with mutter now controlling the libinput context this all moved to mutter&lt;br&gt;
  [4] Especially because I don&#39;t want to write Python bindings for libwacom right now&lt;br&gt;
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/7054682772701394801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/7054682772701394801' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/7054682772701394801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/7054682772701394801'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2024/06/goodbye-xsetwacom-hello-gsetwacom.html' title='goodbye xsetwacom, hello gsetwacom '/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-254700240350278351</id><published>2024-05-09T10:01:00.002+10:00</published><updated>2024-05-09T10:01:28.621+10:00</updated><title type='text'>libwacom and Huion/Gaomon devices</title><content type='html'>&lt;p&gt;TLDR: Thanks to José Exposito, libwacom 2.12 will support all [1] Huion and Gaomon devices when running on a 6.10 kernel.
&lt;/p&gt;

&lt;p&gt;
  libwacom, now almost 13 years old, is a C library that provides a bunch of &lt;b&gt;static&lt;/b&gt; information about graphics tablets that is not otherwise available by looking at the kernel device. Basically, it&#39;s a set of APIs in the form of &lt;i&gt;libwacom_get_num_buttons&lt;/i&gt; and so on. This is used by various components to be more precise about initializing devices, even though libwacom itself has &lt;b&gt;no effect on whether the device works&lt;/b&gt;. It&#39;s only a library for historical reasons [2], if I were to rewrite it today, I&#39;d probably ship libwacom as a set of static json or XML files with a specific schema.
&lt;/p&gt;

&lt;p&gt;
  Here are a few examples on how this information is used: libinput uses libwacom to query information about tablet tools.The kernel event node always supports tilt but the individual tool that is currently in proximity may not. libinput can get the tool ID from the kernel, query libwacom and then initialize the tool struct correctly so the compositor and Wayland clients will get the right information. GNOME Settings uses libwacom&#39;s information to e.g. detect if a tablet is built-in or an external display (to show you the &quot;Map to Monitor&quot; button or not, if builtin), GNOME&#39;s mutter uses the SVGs provided by libwacom to show you an OSD where you can assign keystrokes to the buttons. All these features require that the tablet is supported by libwacom.
&lt;/p&gt;
&lt;p&gt;
  Huion and Gamon devices [3] were not well supported by libwacom because they re-use USB ids, i.e. different tablets from seemingly different manufacturers have the same vendor and product ID. This is understandable, the 16-bit product id only allows for 65535 different devices and if you&#39;re a company that thinks about more than just the current quarterly earnings you realise that if you release a few devices every year (let&#39;s say 5-7), you may run out of product IDs in about 10000 years. Need to think ahead! So between the 140 Huion and Gaomon devices we now have in libwacom I only counted 4 different USB ids. 
  Nine years ago we added name matching too to work around this (i.e. the vid/pid/name combo must match) but, lo and behold, we may run out of unique strings before the heat death of the universe so device names are re-used too! [4] Since we had no other information available to userspace this meant that if you plugged in e.g. a Gaomon M106 and it was detected as S620 and given wrong button numbers, a wrong SVG, etc.
&lt;/p&gt;
&lt;p&gt;
  A while ago José got himself a tablet and started contributing to &lt;a href=&quot;https://digimend.github.io/&quot;&gt;DIGIMEND&lt;/a&gt; (and upstreaming a bunch of things). At some point we realised that the kernel actually had the information we needed: the firmware version string from the tablet which conveniently gave us the tablet model too. With &lt;a href=&quot;https://patchwork.kernel.org/project/linux-input/patch/20240322100210.107152-2-jose.exposito89@gmail.com/&quot;&gt;this kernel patch scheduled for 6.10&lt;/a&gt; this is now exported as the &lt;b&gt;uniq&lt;/b&gt; property (&lt;b&gt;HID_UNIQ&lt;/b&gt; in the uevent) and that means it&#39;s available to userspace. After a bit of rework in libwacom we can now match on the trifecta of vid/pid/uniq or the quadrella of vid/pid/name/uniq. So hooray, for the first time we can actually detect Huion and Gaomon devices correctly.
&lt;/p&gt;
&lt;p&gt;
  The second thing Jose did was to extract all model names from the .deb packages Huion and Gaomon provide and auto-generate all libwacom descriptions for all supported devices. Which meant, in one &lt;a href=&quot;https://github.com/linuxwacom/libwacom/pull/659&quot;&gt;pull request&lt;/a&gt; we added around 130 devices. Nice!
&lt;/p&gt;
&lt;p&gt;
  As said above, this requires the future kernel 6.10 but you can apply the patches to your current kernel if you want. If you do have one of the newly added devices, please verify the &lt;a href=&quot;https://github.com/linuxwacom/libwacom/tree/master/data&quot;&gt;.tablet file&lt;/a&gt; for your device and let us know so we can remove the &quot;this is autogenerated&quot; warnings and fix any issues with the file. Some of the new files may now take precedence over the old hand-added ones so over time we&#39;ll likely have to merge them. But meanwhile, for a brief moment in time, things may actually work.
&lt;/p&gt;


&lt;p&gt;
  &lt;small&gt;
    [1] fsvo of all but should be all current and past ones provided they were supported by Huions driver&lt;br&gt;
    [2] anecdote: in 2011 Jason Gerecke from Wacom and I sat down to and decided on a generic tablet handling library independent of the xf86-input-wacom driver. libwacom was supposed to be that library but it never turned into more than a static description library, libinput is now what our original libwacom idea was.&lt;br&gt;
    [3] and XP Pen and UCLogic but we don&#39;t yet have a fix for those at the time of writing&lt;br&gt;
    [4] names like &quot;HUION PenTablet Pen&quot;... &lt;br&gt;
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/254700240350278351/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/254700240350278351' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/254700240350278351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/254700240350278351'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2024/05/libwacom-and-huiongaomon-devices.html' title='libwacom and Huion/Gaomon devices'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-6156149132305853287</id><published>2024-04-18T14:17:00.000+10:00</published><updated>2024-04-18T14:17:38.892+10:00</updated><title type='text'>udev-hid-bpf: quickstart tooling to fix your HID devices with eBPF</title><content type='html'>&lt;p&gt;
  For the last few months, Benjamin Tissoires and I have been working on and polishing a little tool called &lt;a href=&quot;https://libevdev.pages.freedesktop.org/udev-hid-bpf/index.html&quot;&gt;udev-hid-bpf&lt;/a&gt; [1]. This is the scaffolding required quickly and easily write, test and eventually fix your HID input devices (mouse, keyboard, etc.) via a BPF program instead of a full-blown custom kernel driver or a semi-full-blown kernel patch. To understand how it works, you need to know two things: HID and BPF [2]. 
&lt;/p&gt;
&lt;h2&gt;Why BPF for HID?&lt;/h2&gt;
&lt;p&gt;
  HID is the Human Interface Device standard and the most common way input devices communicate with the host (HID over USB, HID over Bluetooth, etc.). It has two core components: the &quot;report descriptor&quot; and &quot;reports&quot;, both of which are byte arrays. The report descriptor is a fixed burnt-in-ROM byte array that (in rather convoluted terms) tells us what we&#39;ll find in the reports. Things like &quot;bits 16 through to 24 is the delta x coordinate&quot; or &quot;bit 5 is the binary button state for button 3 in degrees celcius&quot;. The reports themselves are sent at (usually) regular intervals and contain the data in the described format, as the devices perceives reality. If you&#39;re interested in more details, see &lt;a href=&quot;https://who-t.blogspot.com/2018/12/understanding-hid-report-descriptors.html&quot; target=&quot;_blank&quot;&gt;Understanding HID report descriptors&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
  BPF or more correctly eBPF is a Linux kernel technology to write programs in a subset of C, compile it and load it into the kernel. The magic thing here is that the kernel will &lt;a href=&quot;https://docs.kernel.org/bpf/verifier.html&quot;&gt;verify it&lt;/a&gt;, so once loaded, the program is &quot;safe&quot;. And because it&#39;s safe it can be run in kernel space which means it&#39;s fast. eBPF was originally written for network packet filters but as of kernel v6.3 and thanks to Benjamin, we have BPF in the HID subsystem. HID actually lends itself really well to BPF because, well, we have a byte array and to fix our devices we need to do complicated things like &quot;toggle that bit to zero&quot; or &quot;swap those two values&quot;.
&lt;/p&gt;
&lt;p&gt;
  If we want to fix our devices we usually need to do one of two things: fix the report descriptor to enable/disable/change some of the values the device pretends to support. For example, we can say we support 5 buttons instead of the supposed 8. Or we need to fix the report by e.g. inverting the y value for the device. This can be done in a custom kernel driver but a HID BPF program is quite a lot more convenient.
&lt;/p&gt;

&lt;h2&gt;HID-BPF programs&lt;/h2&gt;
&lt;p&gt;
  For illustration purposes, here&#39;s the example program to flip the y coordinate. HID BPF programs are usually device specific, we need to know that the e.g. the y coordinate is 16 bits and sits in bytes 3 and 4 (little endian):
  &lt;pre&gt;
SEC(&quot;fmod_ret/hid_bpf_device_event&quot;)
int BPF_PROG(hid_y_event, struct hid_bpf_ctx *hctx)
{
	s16 y;
	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 9 /* size */);

	if (!data)
		return 0; /* EPERM check */

	y = data[3] | (data[4] &lt;&lt; 8);
	y = -y;

	data[3] = y &amp; 0xFF;
	data[4] = (y &gt;&gt; 8) &amp; 0xFF;

	return 0;
}
  &lt;/pre&gt;
That&#39;s it. HID-BPF is invoked before the kernel handles the HID report/report descriptor so to the kernel the modified report looks as if it came from the device.
&lt;/p&gt;
&lt;p&gt;
  As said above, this is device specific because where the coordinates is in the report depends on the device (the report descriptor will tell us). In this example we want to ensure the BPF program is only loaded for our device (vid/pid of 04d9/a09f), and for extra safety we also double-check that the report descriptor matches.
&lt;pre&gt;
// The bpf.o will only be loaded for devices in this list
HID_BPF_CONFIG(
	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, 0x04D9, 0xA09F)
);

SEC(&quot;syscall&quot;)
int probe(struct hid_bpf_probe_args *ctx)
{
	/*
	* The device exports 3 interfaces.
	* The mouse interface has a report descriptor of length 71.
	* So if report descriptor size is not 71, mark as -EINVAL
	*/
	ctx-&gt;retval = ctx-&gt;rdesc_size != 71;
	if (ctx-&gt;retval)
		ctx-&gt;retval = -EINVAL;

	return 0;
}
&lt;/pre&gt;
Obviously the check in probe() can be as complicated as you want.
&lt;/p&gt;
&lt;p&gt;
  This is pretty much it, the &lt;a href=&quot;https://gitlab.freedesktop.org/libevdev/udev-hid-bpf/-/blob/main/src/bpf/userhacks/10-mouse_invert_y.bpf.c?ref_type=heads&quot;&gt;full working program&lt;/a&gt; only has a few extra includes and boilerplate. So it mostly comes down to compiling and running it, and this is where udev-hid-bpf comes in.
&lt;/p&gt;

&lt;h2&gt;udev-hid-bpf as loader&lt;/h2&gt;
&lt;p&gt;
   udev-hid-bpf is a tool to make the &lt;i&gt;development and testing&lt;/i&gt; of HID BPF programs simple, and collect HID BPF programs. You basically run &lt;i&gt;meson compile&lt;/i&gt; and &lt;i&gt;meson install&lt;/i&gt; and voila, whatever BPF program applies to your devices will be auto-loaded next time you plug those in. If you just want to test a single bpf.o file you can &lt;i&gt;udev-hid-bpf install /path/to/foo.bpf.o&lt;/i&gt; and it will install the required udev rule for it to get loaded whenever the device is plugged in. If you don&#39;t know how to compile, you can grab a tarball from our CI and test the pre-compiled bpf.o. Hooray, even simpler.
&lt;/p&gt;
&lt;p&gt;
  udev-hid-bpf is written in Rust but you don&#39;t need to know Rust, it&#39;s just the scaffolding. The BPF programs are all in C. Rust just gives us a relatively easy way to provide a static binary that will work on most tester&#39;s machines.
&lt;/p&gt;
&lt;p&gt;
  The documentation for udev-hid-bpf is &lt;a href=&quot;https://libevdev.pages.freedesktop.org/udev-hid-bpf/&quot;&gt;here&lt;/a&gt;. So if you have a device that needs a hardware quirk or just has an annoying behaviour that you always wanted to fix, well, now&#39;s the time. Fixing your device has never been easier! [3].
&lt;/p&gt;
&lt;p&gt;
  &lt;small&gt;
  [1] Yes, the name is meh but you&#39;re welcome to come up with a better one and go back in time to suggest it a few months ago. &lt;br&gt;
  [2] Because I&#39;m lazy the terms eBPF and BPF will be used interchangeably in this article. Because the difference doesn&#39;t really matter in this context, it&#39;s all eBPF anyway but nobody has the time to type that extra &quot;e&quot;.&lt;br&gt;
  [3] Citation needed
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/6156149132305853287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/6156149132305853287' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/6156149132305853287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/6156149132305853287'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2024/04/udev-hid-bpf-quickstart-tooling-to-fix.html' title='udev-hid-bpf: quickstart tooling to fix your HID devices with eBPF'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-682853112869739175</id><published>2024-03-12T14:33:00.002+10:00</published><updated>2024-03-12T14:33:44.427+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="gnome"/><title type='text'>Enforcing a touchscreen mapping in GNOME</title><content type='html'>&lt;p&gt;
  Touchscreens are quite prevalent by now but one of the not-so-hidden secrets is that they&#39;re actually two devices: the monitor and the actual touch input device. Surprisingly, users want the touch input device to work on the underlying monitor which means your desktop environment needs to somehow figure out which of the monitors belongs to which touch input device. Often these two devices come from two different vendors, so mutter needs to use ... */me holds torch under face* .... HEURISTICS! :scary face:
&lt;/p&gt;

&lt;p&gt;
  Those heuristics are actually quite simple: same vendor/product ID? same dimensions? is one of the monitors a built-in one? [1] But unfortunately in some cases those heuristics don&#39;t produce the correct result. In particular external touchscreens seem to be getting more common again and plugging those into a (non-touch) laptop means you usually get that external screen mapped to the internal display.
&lt;/p&gt;

&lt;p&gt;
  Luckily mutter does have a configuration to it though it is not exposed in the GNOME Settings (yet). But you, my $age $jedirank, can access this via a commandline interface to at least work around the immediate issue. But first: we need to know the monitor details and you need to know about gsettings relocatable schemas.
&lt;/p&gt;
&lt;p&gt;
  Finding the right monitor information is relatively trivial: look at &lt;b&gt;$HOME/.config/monitors.xml&lt;/b&gt; and get your monitor&#39;s vendor, product and serial from there. e.g. in my case this is:
  &lt;pre&gt;
  &amp;lt;monitors version=&amp;quot;2&amp;quot;&amp;gt;
   &amp;lt;configuration&amp;gt;
    &amp;lt;logicalmonitor&amp;gt;
      &amp;lt;x&amp;gt;0&amp;lt;/x&amp;gt;
      &amp;lt;y&amp;gt;0&amp;lt;/y&amp;gt;
      &amp;lt;scale&amp;gt;1&amp;lt;/scale&amp;gt;
      &amp;lt;monitor&amp;gt;
        &amp;lt;monitorspec&amp;gt;
          &amp;lt;connector&amp;gt;DP-2&amp;lt;/connector&amp;gt;
          &amp;lt;vendor&amp;gt;DEL&amp;lt;/vendor&amp;gt;              &lt;--- this one
          &amp;lt;product&amp;gt;DELL S2722QC&amp;lt;/product&amp;gt;   &lt;--- this one
          &amp;lt;serial&amp;gt;59PKLD3&amp;lt;/serial&amp;gt;          &lt;--- and this one
        &amp;lt;/monitorspec&amp;gt;
        &amp;lt;mode&amp;gt;
          &amp;lt;width&amp;gt;3840&amp;lt;/width&amp;gt;
          &amp;lt;height&amp;gt;2160&amp;lt;/height&amp;gt;
          &amp;lt;rate&amp;gt;59.997&amp;lt;/rate&amp;gt;
        &amp;lt;/mode&amp;gt;
      &amp;lt;/monitor&amp;gt;
    &amp;lt;/logicalmonitor&amp;gt;
    &amp;lt;logicalmonitor&amp;gt;
      &amp;lt;x&amp;gt;928&amp;lt;/x&amp;gt;
      &amp;lt;y&amp;gt;2160&amp;lt;/y&amp;gt;
      &amp;lt;scale&amp;gt;1&amp;lt;/scale&amp;gt;
      &amp;lt;primary&amp;gt;yes&amp;lt;/primary&amp;gt;
      &amp;lt;monitor&amp;gt;
        &amp;lt;monitorspec&amp;gt;
          &amp;lt;connector&amp;gt;eDP-1&amp;lt;/connector&amp;gt;
          &amp;lt;vendor&amp;gt;IVO&amp;lt;/vendor&amp;gt;
          &amp;lt;product&amp;gt;0x057d&amp;lt;/product&amp;gt;
          &amp;lt;serial&amp;gt;0x00000000&amp;lt;/serial&amp;gt;
        &amp;lt;/monitorspec&amp;gt;
        &amp;lt;mode&amp;gt;
          &amp;lt;width&amp;gt;1920&amp;lt;/width&amp;gt;
          &amp;lt;height&amp;gt;1080&amp;lt;/height&amp;gt;
          &amp;lt;rate&amp;gt;60.010&amp;lt;/rate&amp;gt;
        &amp;lt;/mode&amp;gt;
      &amp;lt;/monitor&amp;gt;
    &amp;lt;/logicalmonitor&amp;gt;
  &amp;lt;/configuration&amp;gt;
&amp;lt;/monitors&amp;gt;
  &lt;/pre&gt;
  Well, so we know the monitor details we want. Note there are two monitors listed here, in this case I want to map the touchscreen to the external Dell monitor. Let&#39;s move on to gsettings.
&lt;/p&gt;
&lt;p&gt;
gsettings is of course the configuration storage wrapper GNOME uses (and the CLI tool with the same name). GSettings follow a specific schema, i.e. a description of a schema name and possible keys and values for each key. You can list all those, set them, look up the available values, etc.:
  &lt;pre&gt;&lt;b&gt;&lt;/b&gt;
    $ gsettings list-recursively
    ... lots of output ...
    $ gsettings set org.gnome.desktop.peripherals.touchpad click-method &#39;areas&#39;
    $ gsettings range org.gnome.desktop.peripherals.touchpad click-method
    enum
    &#39;default&#39;
    &#39;none&#39;
    &#39;areas&#39;
    &#39;fingers&#39;
  &lt;/pre&gt;
Now, schemas work fine as-is as long as there is only one instance. Where the same schema is used for different devices (like touchscreens) we use a so-called &quot;relocatable schema&quot; and that requires also specifying a path - and this is where it gets tricky. I&#39;m not aware of any functionality to get the specific path for a relocatable schema so often it&#39;s down to reading the source. In the case of touchscreens, the path includes the USB vendor and product ID (in lowercase), e.g. in my case the path is:
&lt;pre&gt;
  /org/gnome/desktop/peripherals/touchscreens/04f3:2d4a/
&lt;/pre&gt;
In your case you can get the touchscreen details from lsusb, libinput record, /proc/bus/input/devices, etc. Once you have it, 
gsettings takes a &lt;b&gt;schema:path&lt;/b&gt; argument like this:
&lt;pre&gt;
  $ gsettings list-recursively org.gnome.desktop.peripherals.touchscreen:/org/gnome/desktop/peripherals/touchscreens/04f3:2d4a/
  org.gnome.desktop.peripherals.touchscreen output [&#39;&#39;, &#39;&#39;, &#39;&#39;]
&lt;/pre&gt;
Looks like the touchscreen is bound to no monitor. Let&#39;s bind it with the data from above:
&lt;pre&gt; 
   $ gsettings set org.gnome.desktop.peripherals.touchscreen:/org/gnome/desktop/peripherals/touchscreens/04f3:2d4a/ output &quot;[&#39;DEL&#39;, &#39;DELL S2722QC&#39;, &#39;59PKLD3&#39;]&quot;
&lt;/pre&gt;
Note the quotes so your shell doesn&#39;t misinterpret things.
&lt;/p&gt;
&lt;p&gt;
  And that&#39;s it. Now I have my internal touchscreen mapped to my external monitor which makes no sense at all but shows that you can map a touchscreen to any screen if you want to.
&lt;/p&gt;

&lt;p&gt;
  &lt;small&gt;[1] Probably the one that most commonly takes effect since it&#39;s the vast vast majority of devices&lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/682853112869739175/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/682853112869739175' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/682853112869739175'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/682853112869739175'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2024/03/enforcing-touchscreen-mapping-in-gnome.html' title='Enforcing a touchscreen mapping in GNOME'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-8214834066896729794</id><published>2024-01-29T17:58:00.003+10:00</published><updated>2024-01-29T17:58:25.671+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="freedesktop.org"/><category scheme="http://www.blogger.com/atom/ns#" term="gitlab"/><title type='text'>New gitlab.freedesktop.org 🚯 emoji-based spamfighting abilities</title><content type='html'>&lt;p&gt;
  This is a follow-up from &lt;a href=&quot;https://who-t.blogspot.com/2023/03/new-gitlabfreedesktoporg-spamfighting.html&quot;&gt;our Spam-label approach&lt;/a&gt;, but this time with MOAR EMOJIS because that&#39;s what the world is turning into.
&lt;/p&gt;
&lt;p&gt;
  Since March 2023 projects could apply the &quot;Spam&quot; label on any new issue and have a magic bot come in and purge the user account plus all issues they&#39;ve filed, see the &lt;a href=&quot;https://who-t.blogspot.com/2023/03/new-gitlabfreedesktoporg-spamfighting.html&quot;&gt;earlier post&lt;/a&gt; for details. This works quite well and gives every project member the ability to quickly purge spam. Alas, pesky spammers are using other approaches to trick google into indexing their pork [1] (because at this point I think all this crap is just SEO spam anyway). Such as commenting on issues and merge requests. We can&#39;t apply labels to comments, so we found a way to work around that: emojis!
&lt;/p&gt;
&lt;p&gt;
  In GitLab you can add &quot;reactions&quot; to issue/merge request/snippet comments and in recent GitLab versions you can register for a &lt;a href=&quot;https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#emoji-events&quot; target=&quot;_blank&quot;&gt;webhook&lt;/a&gt; to be notified when that happens. So what we&#39;ve added to the gitlab.freedesktop.org instance is support for the &lt;b&gt;:do_not_litter:&lt;/b&gt; (🚯) emoji [2] - if you set that on an comment the author of said comment will be blocked and the comment content will be removed. After some safety checks of course, so you can&#39;t just go around blocking everyone by shotgunning emojis into gitlab. Unlike the &quot;Spam&quot; label this does not currently work recursively so it&#39;s best to report the user so admins can purge them properly - ideally &lt;i&gt;before&lt;/i&gt; setting the emoji so the abuse report contains the actual spam comment instead of the redacted one. Also note that there is a 30 second grace period to quickly undo the emoji if you happen to set it accidentally.
&lt;/p&gt;
&lt;p&gt;
  Note that for purging issues, the &quot;Spam&quot; label is still required, the emojis only work for comments.
&lt;/p&gt;
&lt;p&gt;
  Happy cleanup!
&lt;/p&gt;
&lt;p&gt;
  &lt;small&gt;
    [1] or pork-ish &lt;br&gt;
    [2] Benjamin wanted to use &lt;i&gt;:poop:&lt;/i&gt; but there&#39;s a chance that may get used for expressing disagreement with the comment in question &lt;br&gt;
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/8214834066896729794/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/8214834066896729794' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/8214834066896729794'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/8214834066896729794'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2024/01/new-gitlabfreedesktoporg-emoji-based.html' title='New gitlab.freedesktop.org 🚯 emoji-based spamfighting abilities'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-5336622876289443339</id><published>2023-12-14T14:13:00.002+10:00</published><updated>2023-12-14T14:13:17.820+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="xorg"/><title type='text'>Xorg being removed. What does this mean?</title><content type='html'>&lt;p&gt;
  You may have seen the news that &lt;a href=&quot;https://www.redhat.com/en/blog/rhel-10-plans-wayland-and-xorg-server&quot;&gt;Red Hat Enterprise Linux 10 plans to remove Xorg&lt;/a&gt;. But Xwayland will stay around, and given the name overloading and them sharing a git repository there&#39;s some confusion over what is Xorg. So here&#39;s a very simple &quot;picture&quot;. This is the xserver git repository:
  &lt;pre&gt;
$ tree -d -L 2 xserver
xserver
├── composite
├── config
├── damageext
├── dbe
├── dix
├── doc
│   └── dtrace
├── dri3
├── exa
├── fb
├── glamor
├── glx
├── hw
│   ├── kdrive
│   ├── vfb
│   ├── xfree86              &lt;- this one is Xorg
│   ├── xnest
│   ├── xquartz
│   ├── xwayland
│   └── xwin
├── include
├── m4
├── man
├── mi
├── miext
│   ├── damage
│   ├── rootless
│   ├── shadow
│   └── sync
├── os
├── present
├── pseudoramiX
├── randr
├── record
├── render
├── test
│   ├── bigreq
│   ├── bugs
│   ├── damage
│   ├── scripts
│   ├── sync
│   ├── xi1
│   └── xi2
├── Xext
├── xfixes
├── Xi
└── xkb
&lt;/pre&gt;
  The git repo produces several X servers, including the one designed to run on bare metal: Xorg (in &lt;i&gt;hw/xfree86&lt;/i&gt; for historical reasons). The other &lt;i&gt;hw&lt;/i&gt; directories are the other X servers including Xwayland. All the other directories are core X server functionality that&#39;s shared between all X servers [1]. Removing Xorg from a distro but keeping Xwayland means building with &lt;i&gt;--disable-xfree86 -enable-xwayland&lt;/i&gt; [1]. That&#39;s simply it (plus the resulting distro packaging work of course).
&lt;/p&gt;
&lt;p&gt;Removing Xorg means you need something else that runs on bare metal and that is your favourite Wayland compositor. Xwayland then talks to that while presenting an X11-compatible socket to existing X11 applications.&lt;/p&gt;
&lt;p&gt;
  Of course all this means that the X server repo will continue to see patches and many of those will also affect Xorg. For those who are running git master anyway. Don&#39;t get your hopes up for more Xorg releases beyond the security update background noise [2]. 
&lt;/p&gt;
&lt;p&gt;
  Xwayland on the other hand is actively maintained and will continue to see releases. But those releases are a sequence [1] of
&lt;pre&gt;
$ git new-branch xwayland-23.x.y
$ git rm hw/{kdrive/vfb/xfree86/xnest,xquartz,xwin}
$ git tag xwayland-23.x.y
&lt;/pre&gt; 
In other words, an Xwayland release is the xserver git master branch &lt;i&gt;with all X servers but Xwayland removed&lt;/i&gt;. That&#39;s how Xwayland can see new updates and releases without Xorg ever seeing those (except on git master of course). And that&#39;s how your installed Xwayland has code from 2023 while your installed Xorg is still stuck on the branch created and barely updated after 2021.
&lt;/p&gt;

&lt;p&gt;I hope this helps a bit with the confusion of the seemingly mixed messages sent when you see headlines like  &quot;Xorg is unmaintained&quot;, &quot;X server patches to fix blah&quot;, &quot;Xorg is abandoned&quot;, &quot;new Xwayland release.&lt;/p&gt;
&lt;p&gt;
  &lt;small&gt;
    [1] not 100% accurate but close enough&lt;br&gt;
    [2] historically an Xorg release included all other X servers (Xquartz, Xwin, Xvfb, ...) too so this applies to those servers too unless they adopt the Xwayland release model&lt;br&gt;
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/5336622876289443339/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/5336622876289443339' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/5336622876289443339'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/5336622876289443339'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2023/12/xorg-being-removed-what-does-this-mean.html' title='Xorg being removed. What does this mean?'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-8714051925343282212</id><published>2023-11-10T13:22:00.001+10:00</published><updated>2023-11-10T13:22:46.102+10:00</updated><title type='text'>PSA: For Xorg GNOME sessions, use the xf86-input-wacom driver for your tablets</title><content type='html'>&lt;p&gt;
  TLDR: see the title of this blog post, it&#39;s really that trivial.  
&lt;/p&gt;
&lt;p&gt;
  Now that &lt;strike&gt;Godot&lt;/strike&gt;Wayland has been coming for ages and all new development focuses on a pile of software 
  that steams significantly less, we&#39;re seeing cracks appear in the old Xorg support. Not intentionally, 
  but there&#39;s only so much time that can be spent on testing and things that are more niche fall through. 
  One of these was a bug I just had the pleasure of debugging and was triggered by GNOME on Xorg user using the xf86-input-libinput driver for tablet devices. 
&lt;/p&gt;
&lt;p&gt;
  On the surface of it, this should be fine because libinput (and thus xf86-input-libinput) handles tablets just fine. But libinput is the new kid on the block.
  The old kid on said block is the xf86-input-wacom driver, older than libinput by slightly over a decade. And oh man,
  history has baked things into the driver that are worse than raisins in apple strudel [1]. 
&lt;/p&gt;
&lt;p&gt;
  The xf86-input-libinput driver was written as a wrapper around libinput and makes use of fancy things that (from libinput&#39;s POV) have always been around: things like 
  input device hotplugging. Fancy, I know. For tablet devices the driver creates an
  X device for each new tool as it comes into proximity first. Future events from that tool will go through that device. A second tool, be it a new pen or the eraser on the original pen, will create a 
  second X device and events from that tool will go through that X device. Configuration on any device will thus only affect that particular pen. 
  Almost like the whole thing makes sense.
&lt;/p&gt;
&lt;p&gt;
  The wacom driver of course doesn&#39;t do this. It pre-creates X devices for some possible types of tools (pen, eraser, and cursor [2] but not airbrush or artpen). When a tool 
  goes into proximity the events are sent through the respective device, i.e. all pens go through the pen tool, all erasers through the eraser tool.
  To actually track pens there is the &quot;Wacom Serial IDs&quot; property that contains the current tool&#39;s serial number. If you want to 
  track multiple tools you need to query the property on proximity in [4]. At the time this was within a reasonable error margin of a good idea.
&lt;/p&gt;
&lt;p&gt;
  Of course and because MOAR CONFIGURATION! will save us all from the great filter you can specify the &quot;ToolSerials&quot; xorg.conf option as 
  e.g. &quot;airbrush;12345;artpen&quot; and get some extra X devices pre-created, in this case a airbrush and artpen X device and an
  X device just for the tool with the serial number 12345. All other tools multiplex through the default devices. Again, at the time this was a great improvement. [5]
&lt;/p&gt;
&lt;p&gt;
  Anyway, where was I? Oh, right. The above should serve as a good approximation of a reason why the xf86-input-libinput driver does
  not try to be fullly compatible to the xf86-input-wacom driver. In everyday use these things barely matter [6] but for the desktop 
  environment which needs to configure these devices all these differences mean multiple code paths. Those paths need to be tested but they aren&#39;t, 
  so things fall through the cracks.
&lt;/p&gt;
&lt;p&gt;
  So quite a while ago, we made the decision that until Xorg goes dodo, the xf86-input-wacom driver is the tablet driver to use in GNOME.
  So if you&#39;re using a GNOME on Xorg session [7], do make sure the xf86-input-wacom driver is installed. It will make both of us happier and that&#39;s a good aim to strive for.
&lt;/p&gt;
&lt;p&gt;
  &lt;small&gt;
    [1] It&#39;s just a joke. Put the pitchforks down already.&lt;br&gt;
    [2] The cursor is the mouse-like thing Wacom sells. Which is called cursor [3] because the English language has a limited vocabulary and we need to re-use words as much as possible lest we run out of them.&lt;br&gt;
    [3] It&#39;s also called puck. Because [2].&lt;br&gt;
    [4] And by &quot;query&quot; I mean &quot;wait for the XI2 event notifying you of a property change&quot;. Because of lolz the driver cannot update the property on proximity in but needs to schedule that as idle func so the
    property update for the serial always arrives at some unspecified time after the proximity in but hopefully before more motion events happen. Or not, and that&#39;s how hope dies.&lt;br&gt;
    [5]  Think about this next time someone says they long for some unspecified good old days.&lt;br&gt;
    [6] Except the strip axis which on the wacom driver is actually a bit happily moving left/right as your finger moves up/down on the touch strip and any X client needs to know this. libinput normalizes this to...well, a normal value but now the X client needs to know which driver is running so, oh deary deary.&lt;br&gt;
    [7] e.g because your&#39;e stockholmed into it by your graphics hardware&lt;br&gt;
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/8714051925343282212/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/8714051925343282212' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/8714051925343282212'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/8714051925343282212'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2023/11/psa-for-xorg-gnome-sessions-use-xf86.html' title='PSA: For Xorg GNOME sessions, use the xf86-input-wacom driver for your tablets'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-299849164520848859</id><published>2023-07-03T16:34:00.000+10:00</published><updated>2023-07-03T16:34:09.720+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="freedesktop.org"/><title type='text'>gitlab.freedesktop.org now has a bugbot for automatic issue/merge request processing</title><content type='html'>&lt;p&gt;
  As of today, gitlab.freedesktop.org provides easy hooks to invoke the &lt;a href=&quot;https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage&quot; target=&quot;_blank&quot;&gt;gitlab-triage&lt;/a&gt; tool for your project. &lt;i&gt;gitlab-triage&lt;/i&gt; allows for the automation of recurring tasks, for example something like
  &lt;blockquote&gt;
   If the label FOO is set, close the issue and add a comment containing &quot;.... blah ...&quot;
  &lt;/blockquote&gt;
Many project have recurring tasks like this, e.g. the wayland project gets a lot of issues that are compositor (not protocol) issues. Being able to just set a label and have things happen is much more convenient than having to type out the same explanations over and over again.
&lt;/p&gt;
&lt;p&gt;
  The goal for us was to provide automated handling for these with as little friction as possible. And of course each project must be able to decide what actions should be taken. Usually &lt;i&gt;gitlab-triage&lt;/i&gt; is run as part of project-specific scheduled pipelines but since we already have webhook-based &lt;a href=&quot;https://who-t.blogspot.com/2023/03/new-gitlabfreedesktoporg-spamfighting.html&quot; target=&quot;_blank&quot;&gt;spam-fighting tools&lt;/a&gt; we figured we could make this even easier.
&lt;/p&gt;

&lt;p&gt;
So, bugbot was born. Any project registered with bugbot can use labels prefixed with &lt;b&gt;&quot;bugbot::&quot;&lt;/b&gt; to have &lt;i&gt;gitlab-triage&lt;/i&gt; invoked &lt;b&gt;against the project&#39;s policies file&lt;/b&gt;. These labels thus serve as mini-commands for bugbot, though each project decides what happens for any particular label. bugbot effectively works like this:
    
&lt;pre style=&quot;background-color: #ddffdd;&quot;&gt;
sleep 30
for label in {issue|merge_request}.current_labels:
  if label.startswith(&quot;bugbot::&quot;):
     wget https://gitlab.freedesktop.org/foo/bar/-/raw/{main|master}/.triage-policies.yml
     run-gitlab-triage --as-user @bugbot --use-file .triage-policies.yml
     break
&lt;/pre&gt;
  And this is triggered on every issue/merge request update for any registered project which means that all you need to do is set the label and you&#39;re done.
  The things of note here:
  &lt;ul&gt;
    &lt;li&gt;bugbot delays by 30 seconds, giving you time to unset an accidentally applied label before it takes effect&lt;/li&gt;
    &lt;li&gt;bugbot doesn&#39;t care about the label beyond the (hard-coded) &lt;i&gt;&quot;bugbot::&quot;&lt;/i&gt; prefix&lt;/li&gt;
    &lt;li&gt;bugbot always runs &lt;b&gt;your&lt;/b&gt; project&#39;s triage policies, from &lt;b&gt;your&lt;/b&gt; main or master branch (whichever succeeds first)&lt;/li&gt;
    &lt;li&gt;The actions are performed as the bugbot user, not your user&lt;/li&gt;
  &lt;/ul&gt;

The full documentation of what you can do in a policies file is available at the &lt;a href=&quot;https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage&quot; target=&quot;_blank&quot;&gt;gitlab-triage&lt;/a&gt; documentation but let&#39;s look at a simple example that shouldn&#39;t even need explanation:
&lt;pre style=&quot;background-color: #ddffdd;&quot;&gt;
resource_rules:
  issues:
    rules:
      - name: convert bugbot label to other label
        conditions:
          labels:
            - &quot;bugbot::foo&quot;
        actions:
          labels:
            - &quot;foo&quot;
          remove_labels:
            - &quot;bugbot::foo&quot;
          comment: |
            Nice label you have there. Would be a shame 
            if someone removed it
          status: &quot;close&quot;
  merge_requests:
    rules:
      []
&lt;/pre&gt;
  And the effect of this file can be seen in &lt;a href=&quot;https://gitlab.freedesktop.org/whot/ci-playground/-/issues/4&quot; target=&quot;_blank&quot;&gt;this issue&lt;/a&gt; here.
 &lt;/p&gt;
  
&lt;p&gt;
   &lt;h2&gt;Registering a project&lt;/h2&gt;
    &lt;p&gt;
      Bugbot is part of the &lt;a href=&quot;https://gitlab.freedesktop.org/freedesktop/damspam&quot; target=&quot;_blank&quot;&gt;damspam&lt;/a&gt; project and registering a project can be done with a single command.
      &lt;b&gt;Note: this can only be done by someone with the Maintainer role or above.&lt;/b&gt;
&lt;/p&gt;&lt;p&gt;
   Create a &lt;a href=&quot;https://gitlab.freedesktop.org/-/profile/personal_access_token&quot;&gt;personal access token&lt;/a&gt; with API access and save the token value as &lt;b&gt;$XDG_CONFIG_HOME/bugbot/user.token&lt;/b&gt;
Then run the following commands with your project&#39;s full path (e.g. mesa/mesa, pipewire/wireplumber, xorg/lib/libX11):
&lt;pre&gt;
$ pip install git+https://gitlab.freedesktop.org/freedesktop/damspam
$ bugbot request-webhook foo/bar
&lt;/pre&gt;
After this you may remove the token file and the package
&lt;pre&gt;
$ pip uninstall damspam
$ rm $XDG_CONFIG_HOME/bugbot/user.token
&lt;/pre&gt;
The bugbot command will file an issue in the &lt;a href=&quot;https://gitlab.freedesktop.org/freedesktop/fdo-bots&quot;&gt;freedesktop/fdo-bots&lt;/a&gt; repository. This issue will be automatically processed and should be done by the time you finish the above commands, see &lt;a href=&quot;https://gitlab.freedesktop.org/freedesktop/fdo-bots/-/issues/89&quot;&gt;this issue for an example&lt;/a&gt;. Note: the issue processing requires a git push to an internal repo - if you script this for multiple repos please put a sleep(30) in to avoid conflicts.
 &lt;/p&gt;

&lt;p&gt;
&lt;h2&gt;Adding triage policies&lt;/h2&gt;
Once registered, the &lt;b&gt;.triage-policies.yml&lt;/b&gt; file must be added to the root directory of your project. What bugbot commands you want to respond to (and the actions to take) is up to you, though there are two things of note: you should always remove the bugbot label you are reacting to to avoid duplicate processing and &lt;i&gt;gitlab-triage&lt;/i&gt; &lt;b&gt;does not create new labels&lt;/b&gt;. So any label in your actions must be manually created in the project first. Beyond that - the sky&#39;s your limit.
&lt;/p&gt;

&lt;p&gt;
  Remember you can test your policies file with
&lt;pre&gt;
 $ gitlab-triage --dry-run --token $GITLAB_TOKEN \
   --source-id foo/bar  --resource-reference 1234
&lt;/pre&gt;
&lt;hr&gt;

As usual, many thanks to Benjamin Tissoires for reviews and the magic of integrating this into infrastructure.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/299849164520848859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/299849164520848859' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/299849164520848859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/299849164520848859'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2023/07/gitlabfreedesktoporg-now-has-bugbot-for.html' title='gitlab.freedesktop.org now has a bugbot for automatic issue/merge request processing'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-403878284366003238</id><published>2023-06-06T19:36:00.001+10:00</published><updated>2023-06-06T19:36:27.980+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="libei"/><title type='text'>snegg - Python bindings for libei</title><content type='html'>&lt;p&gt;
  After what was basically a flurry of typing, the &lt;a href=&quot;https://gitlab.freedesktop.org/libinput/snegg&quot;&gt;snegg Python bindings for libei&lt;/a&gt; are now available. This is a Python package that provides bindings to the &lt;a href=&quot;https://gitlab.freedesktop.org/libinput/libei/&quot;&gt;libei/libeis/liboeffis&lt;/a&gt; C libraries with a little bit of API improvement to make it not completely terrible. The main goal of these bindings (at least for now) is to provide some quick and easy way to experiment with what could possibly be done using libei - both server-side and client-side. [1] The &lt;a href=&quot;https://gitlab.freedesktop.org/libinput/snegg/-/tree/main/examples&quot;&gt;examples directory&lt;/a&gt; has a minimal EI client (with portal support via liboeffis) and a minimal EIS implementation. The bindings are still quite rough and the API is nowhere near stable.
&lt;/p&gt;

&lt;p&gt;
  A proper way to support EI in Python would be to implement the protocol directly - there&#39;s no need for the C API quirkiness this way and you can make full use of things like &lt;i&gt;async&lt;/i&gt; and whatnot. If you&#39;re interested in that, get in touch! Meanwhile, writing something roughly resemling xdotool is probably only a few hundred lines of python code. [2]
&lt;/p&gt;

&lt;p&gt;
  &lt;small&gt;
    [1] writing these also exposed a few bugs in libei itself so I&#39;m happy 1.0 wasn&#39;t out just yet&lt;br/&gt;
    [2] at least the input emulation parts of xdotool
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/403878284366003238/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/403878284366003238' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/403878284366003238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/403878284366003238'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2023/06/snegg-python-bindings-for-libei.html' title='snegg - Python bindings for libei'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-2553014492687294516</id><published>2023-05-09T10:51:00.003+10:00</published><updated>2023-05-09T10:51:24.239+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="libei"/><title type='text'>libei and a fancy protocol</title><content type='html'>&lt;p&gt;
  &lt;a href=&quot;https://gitlab.freedesktop.org/libinput/libei/&quot;&gt;libei&lt;/a&gt; is the library for Emulated Input - see &lt;a href=&quot;https://who-t.blogspot.com/2020/08/libei-library-to-support-emulated-input.html&quot;&gt;this post&lt;/a&gt; for an introduction. Like many projects, libei was started when it was still unclear if it could be the right solution to the problem. In the years (!) since, we&#39;ve upgraded the answer to that question from &quot;hopefully&quot; to &quot;yeah, I reckon&quot; - doubly so since we added support for &lt;a href=&quot;https://who-t.blogspot.com/2022/03/libei-adding-support-for-passive.html&quot;&gt;receiver contexts&lt;/a&gt; and got &lt;a href=&quot;https://github.com/input-leap/input-leap/pull/1594#&quot;&gt;InputLeap&lt;/a&gt; working through the various &lt;a href=&quot;https://gitlab.freedesktop.org/libinput/libei/-/issues/1&quot;&gt;portal changes&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
  Emulating or capturing input needs two processes to communicate for obvious reasons so the communication protocol is a core part of it. But initially, libei was a quickly written prototype and the protocol was hacked up on an as-needed let&#39;s-get-this-working basis. The rest of the C API got stable enough but the protocol was the missing bit. Long-term the protocol &lt;b&gt;must&lt;/b&gt; be stable - without a stable protocol updating your compositor may break all flatpaks still shipping an older libei. Or updating a flatpak may not work with an older compositor. So in the last weeks/months, a lot of work as gone into making the protocol stable. This consisted of two parts: drop protobuf and make the variuos features interface-dependent, unashamedly quite like the Wayland protocol which is also split into a number of interfaces that can be independently versioned. Initially, I attempted to make the protocol binary compatible with Wayland but dropped that goal eventually - the benefits were minimal and the effort and limitations (due to different requirements) were quite significant. 
&lt;/p&gt;

&lt;p&gt;
  The protocol is defined in a single XML file and can be used directly from language bindings (if any). The &lt;a href=&quot;https://libinput.pages.freedesktop.org/libei/index.html&quot;&gt;protocol documentation&lt;/a&gt; is quite extensive but it&#39;s relatively trivial in principal: the first 8 bytes of each message are the object ID, then we have 4 bytes for the message length in bytes, then 4 for the object-specific opcode. That opcode is one of the requests or events in the object&#39;s interface - which is defined at object creation time. Unlike Wayland, the majority of objects in libei are created in server-side (the EIS implementation decides which seats are available and which devices in those seats). The remainder of the message are the arguments. Note that unlike other protocols the message does not carry a signature - prior knowledge of the message is required to parse the arguments. This is a direct effect of initially making it wayland-compatible and I didn&#39;t really find it worth the effort to add this.
&lt;/p&gt;

&lt;p&gt;
  Anyway, long story short: swapping the protocol out didn&#39;t initially have any effect on the C library but with the changes came some minor updates to remove some of the warts in the API. Perhaps the biggest change is that the previous capabilities of a device are now split across several interfaces. Your average mouse-like emulated device will have the &quot;pointer&quot;, &quot;button&quot; and &quot;scroll&quot; interfaces, or maybe the &quot;pointer_absolute&quot;, &quot;button&quot; and &quot;scroll&quot; interface. The touch and keyboard interfaces were left as-is. Future interfaces will likely include gestures and tablet tools, I have done some rough prototyping locally and it will fit in nicely enough with the current protocol.
&lt;/p&gt;

&lt;p&gt;
  At the time of writing, the protocol is not officialy stable but I have no intention of changing it short of some bug we may discover. Expect libei 1.0 very soon.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/2553014492687294516/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/2553014492687294516' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/2553014492687294516'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/2553014492687294516'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2023/05/libei-and-fancy-protocol.html' title='libei and a fancy protocol'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-1596428771942998696</id><published>2023-03-28T19:30:00.001+10:00</published><updated>2023-03-29T17:31:52.813+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="freedesktop.org"/><category scheme="http://www.blogger.com/atom/ns#" term="gitlab"/><title type='text'>New gitlab.freedesktop.org spamfighting abilities</title><content type='html'>&lt;p&gt;
  As of today, gitlab.freedesktop.org allows anyone with a &lt;a href=&quot;https://docs.gitlab.com/ee/user/permissions.html#permissions-and-roles&quot; target=&quot;_blank&quot;&gt;GitLab Developer role or above&lt;/a&gt; to remove spam issues. If you are reading this article a while after it&#39;s published, it&#39;s best to refer to the &lt;a href=&quot;https://gitlab.freedesktop.org/freedesktop/damspam&quot;&gt;damspam README&lt;/a&gt; for up-to-date details.
  I&#39;m going to start with the TLDR first.
&lt;/p&gt;
  &lt;h2&gt;For Maintainers&lt;/h2&gt;
    &lt;p&gt;
    Create a &lt;a href=&quot;https://gitlab.com/-/profile/personal_access_tokens&quot;&gt;personal access token&lt;/a&gt; with API access and save the token value as &lt;b&gt;$XDG_CONFIG_HOME/damspam/user.token&lt;/b&gt;
Then run the following commands with your project&#39;s full path (e.g. mesa/mesa, pipewire/wireplumber, xorg/lib/libX11):
&lt;pre&gt;
$ pip install git+https://gitlab.freedesktop.org/freedesktop/damspam
$ damspam request-webhook foo/bar
# clean up, no longer needed.
$ pip uninstall damspam
$ rm $XDG_CONFIG_HOME/damspam/user.token
&lt;/pre&gt;
The damspam command will file an issue in the &lt;a href=&quot;https://gitlab.freedesktop.org/freedesktop/fdo-bots&quot;&gt;freedesktop/fdo-bots&lt;/a&gt; repository. This issue will be automatically processed by a bot and should be done by the time you finish the above commands, see &lt;a href=&quot;https://gitlab.freedesktop.org/freedesktop/fdo-bots/-/issues/9&quot;&gt;this issue for an example&lt;/a&gt;. Note: the issue processing requires a git push to an internal repo - if you script this for multiple repos please put a sleep(30) in to avoid conflicts.
    &lt;/p&gt;
  &lt;p&gt;
    Once the request has been processed (and again, this should be instant), any issue in your project that gets assigned the label &lt;b&gt;Spam&lt;/b&gt; will be processed automatically by damspam. See the next section for details.
  &lt;/p&gt;
    
   &lt;h2&gt;For Developers&lt;/h2&gt;
    &lt;p&gt;
      Once the maintainer for your project has requested the webhook, simply assign the &lt;b&gt;Spam&lt;/b&gt; label to any issue that is spam. The issue creator will be blocked (i.e. cannot login), this issue &lt;b&gt;and any other issue filed by the same user&lt;/b&gt; will be closed and made confidential (i.e. they are no longer visible to the public). In the future, one of the GitLab admins can remove that user completely but meanwhile, they and their spam are gone from the public eye and they&#39;re blocked from producing more. This should happen within seconds of assigning the Spam label.
 &lt;/p&gt;
     
   &lt;h2&gt;For GitLab Admins&lt;/h2&gt;
    &lt;p&gt;
      Create a &lt;a href=&quot;https://gitlab.com/-/profile/personal_access_tokens&quot;&gt;personal access token&lt;/a&gt; with API access &lt;b&gt;for the @spambot user&lt;/b&gt; and save the token value as &lt;b&gt;$XDG_CONFIG_HOME/damspam/spambot.token&lt;/b&gt;. This is so you can operate as spambot instead of your own user.
Then run the following command to remove all tagged spammers:
&lt;pre&gt;
$ pip install git+https://gitlab.freedesktop.org/freedesktop/damspam
$ damspam purge-spammers
&lt;/pre&gt;
     The last command will list any users that are spammers (together with an issue that should make it simple to check whether it is indeed spam) and &lt;b&gt;after interactive confirmation&lt;/b&gt; purge them as requested. At the time of writing, the output looks like this:
     &lt;pre&gt;
$ damspam purge-spammers
0: naughtyuser              : https://gitlab.freedesktop.org/somenamespace/project/-/issues/1234: [STREAMING@TV]!* LOOK AT ME
1: abcuseless               : https://gitlab.freedesktop.org/somenamespace/project/-/issues/4567: ((@))THIS STREAM IS IMPORTANT
2: anothergit               : https://gitlab.freedesktop.org/somenamespace/project/-/issues/8778: Buy something, really
3: whatawasteofalife        : https://gitlab.freedesktop.org/somenamespace/project/-/issues/9889: What a waste of oxygen I am
Purging a user means a full delete including all issues, MRs, etc. This is nonrecoverable!
Please select the users to purge:
[q]uit, purge [a]ll, or the index: 
     &lt;/pre&gt;
Purging the spammers will hard-delete them and remove anything they ever did on gitlab. This is irreversible.
 &lt;/p&gt;
     

&lt;h2&gt;How it works&lt;/h2&gt;
&lt;p&gt;
  There are two components at play here: &lt;a href=&quot;https://gitlab.freedesktop.org/freedesktop/hookiedookie/&quot;&gt;hookiedookie&lt;/a&gt;, a generic webhook dispatcher, and &lt;a href=&quot;https://gitlab.freedesktop.org/freedesktop/damspam/&quot;&gt;damspam&lt;/a&gt; which handles the actual spam issues. Hookiedookie provides an HTTP server and &quot;does things&quot; with JSON data on request. What it does is relatively generic (see the &lt;a href=&quot;https://gitlab.freedesktop.org/bentiss/hookiedookie/-/blob/main/Settings.yaml&quot;&gt;Settings.yaml&lt;/a&gt; example file) but it&#39;s set up to be triggered by a GitLab webhook and thus receives &lt;a href=&quot;https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#issue-events&quot;&gt;this payload&lt;/a&gt;. For damspam the rules we have for hookiedookie come down to something like this: if the URL is &quot;webhooks/namespace/project&quot; and damspam is set up for this project and the payload is an issue event and it has the &quot;Spam&quot; label in the issue labels, call out to damspam and pass the payload on. Other rules we currently use are automatic reload on push events or the rule to trigger the webhook request processing bot as above.
&lt;/p&gt;
&lt;p&gt;
  This is also the reason a maintainer has to request the webhook. When the request is processed, the spambot installs a webhook with a secret token (a uuid) in the project. That token will be sent as header (a standard GitLab feature). The project/token pair is also added to hookiedookie and any webhook data must contain the project name and matching token, otherwise it is discarded. Since the token is write-only, no-one (not even the maintainers of the project) can see it.
&lt;/p&gt;

&lt;p&gt;
  damspam gets the payload forwarded but is otherwise unaware of how it is invoked. It checks the issue, fetches the data needed, does some safety check and if it determines that yes, this is spam, then it closes the issue, makes it confidential, blocks the user and then recurses into every issue this user ever filed. Not necessarily in that order. There are some safety checks, so you don&#39;t have to worry about it suddenly blocking every project member.
&lt;/p&gt;

&lt;h2&gt;Why?&lt;/h2&gt;
&lt;p&gt;
  For a while now, we&#39;ve suffered from a deluge of spam (and worse) that makes it through the spam filters. GitLab has a &lt;a href=&quot;https://docs.gitlab.com/ee/user/report_abuse.html&quot;&gt;Report Abuse&lt;/a&gt; feature for this but it&#39;s... woefully incomplete. The UI guides users to do the right thing - as reporter you can tick &quot;the user is sending spam&quot; and it automatically adds a link to the reported issue. But: &lt;b&gt;none of this useful data is visible to admins&lt;/b&gt;. Seriously, look at the &lt;a href=&quot;https://docs.gitlab.com/ee/user/admin_area/review_abuse_reports.html&quot;&gt;official screenshots&lt;/a&gt;. There is no link to the issue, all you get is a username, the user that reported it and the content of a textbox that &lt;i&gt;almost never has any useful information&lt;/i&gt;. The link to the issue? Not there. The selection that the user is a spammer? Not there.
&lt;/p&gt;
&lt;p&gt;
  For an admin, this is frustrating at best. To verify that the user is indeed sending spam, you have to find the issue first. Which, at best, requires several clicks and digging through the profile activities. At worst you know that the user is a spammer because you trust the reporter but you just can&#39;t find the issue for whatever reason.
&lt;/p&gt;
&lt;p&gt;
  But even worse: reporting spam does nothing immediately. The spam stays up until an admin wakes up, reviews the abuse reports and removes that user. Meanwhile, the spammer can happily keep filing issues against the project. Overall, it is not a particularly great situation.
&lt;/p&gt;
&lt;p&gt;
  With hookiedookie and damspam, we&#39;re now better equipped to stand against the tide of spam. Anyone who can assign labels can help fight spam and the effect is immediate. And it&#39;s - for our use-cases - safe enough: if you trust someone to be a developer on your project, we can trust them to not willy-nilly remove issues pretending they&#39;re spam. In fact, they probably could&#39;ve deleted issues beforehand already anyway if they wanted to make them disappear.
&lt;/p&gt;

&lt;h2&gt;Other instances&lt;/h2&gt;
&lt;p&gt;
  While we&#39;re definitely aiming at gitlab.freedesktop.org, there&#39;s nothing in particular that requires this instance. If you&#39;re the admin for a public gitlab instance feel free to talk to Benjamin Tissoires or me to check whether this could be useful for you too, and what changes would be necessary.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/1596428771942998696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/1596428771942998696' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/1596428771942998696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/1596428771942998696'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2023/03/new-gitlabfreedesktoporg-spamfighting.html' title='New gitlab.freedesktop.org spamfighting abilities'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-8786401603320798921</id><published>2023-01-17T14:47:00.001+10:00</published><updated>2023-01-17T14:47:22.315+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="libinput"/><title type='text'>libinput and the custom pointer acceleration function</title><content type='html'>&lt;p&gt;
  After 8 months of work by Yinon Burgansky, libinput now has a new pointer acceleration profile: the &quot;custom&quot; profile. This profile allows users to tweak the exact response of their device based on their input speed.
&lt;/p&gt;
&lt;p&gt;
  A short primer: the pointer acceleration profile is a function that multiplies the incoming deltas with a given factor F, so that your input delta &lt;i&gt;(x, y)&lt;/i&gt; becomes &lt;i&gt;(Fx, Fy)&lt;/i&gt;. How this is done is specific to the profile, libinput&#39;s existing profiles had either a flat factor or an adaptive factor that roughly resembles what Xorg used to have, see
the &lt;a href=&quot;https://wayland.freedesktop.org/libinput/doc/latest/pointer-acceleration.html#pointer-acceleration-profiles&quot;&gt;libinput documentation&lt;/a&gt; for the details. The adaptive curve however has a fixed behaviour, all a user could do was scale the curve up/down, but not actually adjust the curve.
&lt;p&gt;
  
&lt;h2&gt;Input speed to output speed&lt;/h2&gt;
&lt;p&gt;
  The new custom filter allows exactly that: it allows a user to configure a completely custom ratio between input speed and output speed. That ratio will then influence the current delta. There is a whole new API to do this but simplified: the profile is defined via a series of points of &lt;i&gt;(x, f(x))&lt;/i&gt; that are linearly interpolated. Each point is defined as &lt;b&gt;input speed in device units/ms&lt;/b&gt; to &lt;b&gt;output speed in device units/ms&lt;/b&gt;. For example, to provide a flat acceleration equivalent, specify &lt;i&gt;[(0.0, 0.0), (1.0, 1.0)]&lt;/i&gt;. With the linear interpolation this is of course a 45-degree function, and any incoming speed will result in the equivalent output speed.
&lt;/p&gt;
 
&lt;p&gt;
  Noteworthy: we are talking about the &lt;b&gt;speed&lt;/b&gt; here, not any individual delta. This is not exactly the same as the flat acceleration profile (which merely multiplies the deltas by a constant factor) - it does take the speed of the device into account, i.e. device units moved per ms. For most use-cases this is the same but for particularly slow motion, the speed may be calculated across multiple deltas (e.g. &quot;user moved 1 unit over 21ms&quot;). This avoids some jumpyness at low speeds.
&lt;/p&gt;
&lt;p&gt;
  But &lt;i&gt;because&lt;/i&gt; the curve is speed-based, it allows for some interesting features too: the curve &lt;i&gt;[(0.0, 1.0), (1.0, 1.0)]&lt;/i&gt; is a horizontal function at 1.0. Which means that &lt;i&gt;any&lt;/i&gt; input speed results in an output speed of 1 unit/ms. So regardless how fast the user moves the mouse, the output speed is always constant. I&#39;m not immediately sure of a real-world use case for this particular case (some accessibility needs maybe) but I&#39;m sure it&#39;s a good prank to play on someone.
&lt;/p&gt;

&lt;p&gt;
  Because libinput is written in C, &lt;a href=&quot;https://wayland.freedesktop.org/libinput/doc/latest/api/group__config.html#ga2e87262b38c8ac3fe6b4f9a60c5a69e4&quot; target=&quot;_blank&quot;&gt;the API&lt;/a&gt; is not necessarily immediately obvious but: to configure you pass an array of (what will be) y-values and set the &lt;i&gt;step-size&lt;/i&gt;. The curve then becomes: &lt;i&gt;[(0 * step-size, array[0]), (1 * step-size, array[1]), (2 * step-size, array[2]), ...]&lt;/i&gt;. There are some limitations on the number of points but they&#39;re high enough that they should not matter.
&lt;/p&gt;

&lt;p&gt;
  Note that any curve is still &lt;b&gt;device-resolution dependent&lt;/b&gt;, so the same curve will not behave the same on two devices with different resolution (DPI).
And since the curves uploaded by the user are hand-polished, the speed setting has no effect - we cannot possibly know how a custom curve is supposed to scale. The setting will simply update with the provided value and return that but the behaviour of the device won&#39;t change in response.
&lt;/p&gt;

&lt;h2&gt;Motion types&lt;/h2&gt;
&lt;p&gt;
  Finally, there&#39;s another feature in this PR - the so-called &quot;movement type&quot; which must be set when defining a curve. Right now, we have two types, &quot;fallback&quot; and &quot;motion&quot;. The &quot;motion&quot; type applies to, you guessed it, pointer motion. The only other type available is fallback which applies to everything &lt;i&gt;but&lt;/i&gt; pointer motion. The idea here is of course that we can apply custom acceleration curves for various different device behaviours - in the future this could be scrolling, gesture motion, etc. And since those will have a different requirements, they can be configure separately.
&lt;/p&gt;

&lt;h2&gt;How to use this?&lt;/h2&gt;
&lt;p&gt;
  As usual, the availability of this feature depends on your Wayland compositor and how this is exposed. For the Xorg + xf86-input-libinput case however, the &lt;a href=&quot;https://gitlab.freedesktop.org/xorg/driver/xf86-input-libinput/-/merge_requests/39&quot; target=&quot;_blank&quot;&gt;merge request&lt;/a&gt; adds a few properties so that you can play with this using the &lt;i&gt;xinput&lt;/i&gt; tool:
  &lt;pre&gt;
  # Set the flat-equivalent function described above
  $ xinput set-prop &quot;devname&quot; &quot;libinput Accel Custom Motion Points&quot; 0.0 1.0
  # Set the step, i.e. the above points are on 0 u/ms, 1 u/ms, ...
  # Can be skipped, 1.0 is the default anyway
  $ xinput set-prop &quot;devname&quot; &quot;libinput Accel Custom Motion Points&quot; 1.0 
  # Now enable the custom profile
  $ xinput set-prop &quot;devname&quot; &quot;libinput Accel Profile Enabled&quot; 0 0 1
  &lt;/pre&gt;
The above sets a custom pointer accel for the &quot;motion&quot; type. Setting it for fallback is left as an exercise to the reader (though right now, I think the fallback curve is pretty much only used if there is no motion curve defined). 
&lt;/p&gt;
&lt;p&gt;
  Happy playing around (and no longer filing bug reports if you don&#39;t like the default pointer acceleration ;)
&lt;/p&gt;
&lt;h2&gt;Availability&lt;/h2&gt;
&lt;p&gt;
  This custom profile will be available in libinput 1.23 and xf86-input-libinput-1.3.0. No release dates have been set yet for either of those.
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/8786401603320798921/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/8786401603320798921' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/8786401603320798921'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/8786401603320798921'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2023/01/libinput-and-custom-pointer.html' title='libinput and the custom pointer acceleration function'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6112936277054198647.post-8186285007657142114</id><published>2023-01-06T12:15:00.003+10:00</published><updated>2023-01-18T08:20:25.116+10:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="xorg"/><title type='text'>X servers no longer allow byte-swapped clients (by default)</title><content type='html'>&lt;p&gt;
  In the beginning, there was the egg. Then &lt;a href=&quot;https://en.wikipedia.org/wiki/Gulliver%27s_Travels&quot;&gt;fictional people&lt;/a&gt; started eating that from different ends, and the terms of &quot;little endians&quot; and &quot;Big Endians&quot; was born.
&lt;/p&gt;
&lt;p&gt;
  Computer architectures (mostly) come with one of either byte order: MSB first or LSB first. The two are incompatible of course, and many a bug was introduced trying to convert between the two (or, more common: failing to do so). The two byte orders were termed Big Endian and little endian, because that hilarious naming scheme at least gives us something to laugh about while contemplating throwing it all away and considering a future as, I don&#39;t know, a strawberry plant.
&lt;/p&gt;
&lt;p&gt;
  Back in the mullet-infested 80s when the X11 protocol was designed both little endian and big endian were common enough. And back then running the X server on a different host than the client was common too - the X terminals back then had less processing power than a smart toilet seat today so the cpu-intensive clients were running on some mainfraime. To avoid overtaxing the poor mainframe already running dozens of clients for multiple users, the job of converting between the two byte orders was punted to the X server. So to this day whenever a client connects, the first byte it sends is a literal &quot;l&quot; or &quot;B&quot; to inform the server of the client&#39;s byte order. Where the byte order doesn&#39;t match the X server&#39;s byte order, the client is a &quot;swapped client&quot; in X server terminology and all 16, 32, and 64-bit values must be &quot;byte-swapped&quot; into the server&#39;s byte order. All of those values in all requests, and then again back to the client&#39;s byte order in all outgoing replies and events. Forever, till a crash do them part.
&lt;/p&gt;

&lt;p&gt;
  If you get one of those wrong, the number is no longer correct. And it&#39;s properly wrong too, the difference between 0x1 and 0x01000000 is rather significant. [0]
  Which has the hilarious side-effect of... well, pretty much anything. But usually it ranges from crashing the server (thus taking all other clients down in commiseration) to leaking random memory locations. The list of security issues affecting the various &lt;b&gt;SProcFoo&lt;/b&gt; implementations (X server naming scheme for &lt;b&gt;S&lt;/b&gt;wapped&lt;b&gt; Proc&lt;/b&gt;edure for request &lt;b&gt;Foo&lt;/b&gt;) is so long that I&#39;m too lazy to pull out the various security advisories and link to them. Just believe me, ok? &lt;i&gt;*jedi handwave*&lt;/i&gt;
&lt;/p&gt;

&lt;p&gt;
  These days, encountering a Big Endian host is increasingly niche, letting it run an X client that connects to your local little-endian X server is even more niche [1]. I think the only regular real-world use-case for this is running X clients on an s390x, connecting to your local intel-ish (and thus little endian) workstation. Not something most users do on a regular basis. So right now, the byte-swapping code is mainly a free attack surface that 99% of users never actually use for anything real. So... let&#39;s not do that?
&lt;/p&gt;

&lt;p&gt;
  I just &lt;a href=&quot;https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1029&quot;&gt;merged a PR&lt;/a&gt; into the X server repo that prohibits byte-swapped clients by default. A Big Endian client connecting to an X server will fail the connection with an error message of &lt;i&gt;&quot;Prohibited client endianess, see the Xserver man page&quot;&lt;/i&gt;. [2]  Thus, a whole class of future security issues avoided - yay!
&lt;/p&gt;

&lt;p&gt;
  For the use-cases where you &lt;b&gt;do&lt;/b&gt; need to let Big Endian clients connect to your little endian X server, you have two options: start your X server (Xorg, Xwayland, Xnest, ...) with the &lt;b&gt;+byteswappedclients&lt;/b&gt; commandline option. Alternatively, and this only applies for Xorg: add &lt;b&gt;Option &quot;AllowByteSwappedClients&quot; &quot;on&quot;&lt;/b&gt; to the xorg.conf &lt;b&gt;ServerFlags&lt;/b&gt; section. Both of these will change the default back to the original setting. Both are documented in the &lt;b&gt;Xserver(1)&lt;/b&gt; and &lt;b&gt;xorg.conf(5)&lt;/b&gt; man pages, respectively.
&lt;/p&gt;

&lt;p&gt;
  Now, there&#39;s a drawback: in the Wayland stack, the compositor is in charge of starting Xwayland which means the compositor needs to expose a way of passing &lt;b&gt;+byteswappedclients&lt;/b&gt; to Xwayland. This is compositor-specific, bugs are filed for &lt;a href=&quot;https://gitlab.gnome.org/GNOME/mutter/-/issues/2576&quot;&gt;mutter&lt;/a&gt; (merged for GNOME 44), &lt;a href=&quot;https://invent.kde.org/plasma/kwin/-/issues/131&quot;&gt;kwin&lt;/a&gt; and &lt;a href=&quot;https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3558&quot;&gt;wlroots&lt;/a&gt;. Until those are addressed, you cannot easily change this default (short of changing /usr/bin/Xwayland into a wrapper script that passes the option through).
&lt;/p&gt;

&lt;p&gt;
  There&#39;s no specific plan yet which X releases this will end up in, primarily because the release cycle for X is...undefined. Probably &lt;i&gt;xserver-23.0&lt;/i&gt; if and when that happens. It&#39;ll probably find its way into the &lt;i&gt;xwayland-23.0&lt;/i&gt; release, if and when that happens. Meanwhile, distributions interested in this particular change should consider backporting it to their X server version.
  This has been accepted as a &lt;a href=&quot;https://fedoraproject.org/w/index.php?title=Changes/XServerProhibitsByteSwappedClients&quot;&gt;Fedora 38 change&lt;/a&gt;.
&lt;/p&gt;

&lt;p&gt;
  &lt;small&gt;
    [0] Also, it doesn&#39;t help that much of the X server&#39;s protocol handling code was written with the attitude of &quot;surely the client wouldn&#39;t lie about that length value&quot;&lt;br/&gt;
    [1] little-endian client to Big Endian X server is so rare that it&#39;s barely worth talking about. But suffice to say, the exact same applies, just with little and big swapped around.&lt;br/&gt;
    [2] That message is unceremoniously dumped to stderr, but that bit is unfortunately a libxcb issue.&lt;br/&gt;
  &lt;/small&gt;
&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://who-t.blogspot.com/feeds/8186285007657142114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment/fullpage/post/6112936277054198647/8186285007657142114' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/8186285007657142114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6112936277054198647/posts/default/8186285007657142114'/><link rel='alternate' type='text/html' href='http://who-t.blogspot.com/2023/01/x-servers-no-longer-allow-byte-swapped.html' title='X servers no longer allow byte-swapped clients (by default)'/><author><name>Peter Hutterer</name><uri>http://www.blogger.com/profile/17204066043271384535</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry></feed>