tag:blogger.com,1999:blog-61129362770541986472024-03-12T14:34:38.235+10:00Who-TPeter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.comBlogger291125tag:blogger.com,1999:blog-6112936277054198647.post-6828531128697391752024-03-12T14:33:00.002+10:002024-03-12T14:33:44.427+10:00Enforcing a touchscreen mapping in GNOME<p>
Touchscreens are quite prevalent by now but one of the not-so-hidden secrets is that they'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:
</p>
<p>
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'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.
</p>
<p>
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.
</p>
<p>
Finding the right monitor information is relatively trivial: look at <b>$HOME/.config/monitors.xml</b> and get your monitor's vendor, product and serial from there. e.g. in my case this is:
<pre>
<monitors version="2">
<configuration>
<logicalmonitor>
<x>0</x>
<y>0</y>
<scale>1</scale>
<monitor>
<monitorspec>
<connector>DP-2</connector>
<vendor>DEL</vendor> <--- this one
<product>DELL S2722QC</product> <--- this one
<serial>59PKLD3</serial> <--- and this one
</monitorspec>
<mode>
<width>3840</width>
<height>2160</height>
<rate>59.997</rate>
</mode>
</monitor>
</logicalmonitor>
<logicalmonitor>
<x>928</x>
<y>2160</y>
<scale>1</scale>
<primary>yes</primary>
<monitor>
<monitorspec>
<connector>eDP-1</connector>
<vendor>IVO</vendor>
<product>0x057d</product>
<serial>0x00000000</serial>
</monitorspec>
<mode>
<width>1920</width>
<height>1080</height>
<rate>60.010</rate>
</mode>
</monitor>
</logicalmonitor>
</configuration>
</monitors>
</pre>
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's move on to gsettings.
</p>
<p>
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.:
<pre><b></b>
$ gsettings list-recursively
... lots of output ...
$ gsettings set org.gnome.desktop.peripherals.touchpad click-method 'areas'
$ gsettings range org.gnome.desktop.peripherals.touchpad click-method
enum
'default'
'none'
'areas'
'fingers'
</pre>
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 "relocatable schema" and that requires also specifying a path - and this is where it gets tricky. I'm not aware of any functionality to get the specific path for a relocatable schema so often it'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:
<pre>
/org/gnome/desktop/peripherals/touchscreens/04f3:2d4a/
</pre>
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 <b>schema:path</b> argument like this:
<pre>
$ gsettings list-recursively org.gnome.desktop.peripherals.touchscreen:/org/gnome/desktop/peripherals/touchscreens/04f3:2d4a/
org.gnome.desktop.peripherals.touchscreen output ['', '', '']
</pre>
Looks like the touchscreen is bound to no monitor. Let's bind it with the data from above:
<pre>
$ gsettings set org.gnome.desktop.peripherals.touchscreen:/org/gnome/desktop/peripherals/touchscreens/04f3:2d4a/ output "['DEL', 'DELL S2722QC', '59PKLD3']"
</pre>
Note the quotes so your shell doesn't misinterpret things.
</p>
<p>
And that'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.
</p>
<p>
<small>[1] Probably the one that most commonly takes effect since it's the vast vast majority of devices</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-82148340668967297942024-01-29T17:58:00.003+10:002024-01-29T17:58:25.671+10:00New gitlab.freedesktop.org π― emoji-based spamfighting abilities<p>
This is a follow-up from <a href="https://who-t.blogspot.com/2023/03/new-gitlabfreedesktoporg-spamfighting.html">our Spam-label approach</a>, but this time with MOAR EMOJIS because that's what the world is turning into.
</p>
<p>
Since March 2023 projects could apply the "Spam" label on any new issue and have a magic bot come in and purge the user account plus all issues they've filed, see the <a href="https://who-t.blogspot.com/2023/03/new-gitlabfreedesktoporg-spamfighting.html">earlier post</a> 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't apply labels to comments, so we found a way to work around that: emojis!
</p>
<p>
In GitLab you can add "reactions" to issue/merge request/snippet comments and in recent GitLab versions you can register for a <a href="https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#emoji-events" target="_blank">webhook</a> to be notified when that happens. So what we've added to the gitlab.freedesktop.org instance is support for the <b>:do_not_litter:</b> (π―) 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't just go around blocking everyone by shotgunning emojis into gitlab. Unlike the "Spam" label this does not currently work recursively so it's best to report the user so admins can purge them properly - ideally <i>before</i> 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.
</p>
<p>
Note that for purging issues, the "Spam" label is still required, the emojis only work for comments.
</p>
<p>
Happy cleanup!
</p>
<p>
<small>
[1] or pork-ish <br>
[2] Benjamin wanted to use <i>:poop:</i> but there's a chance that may get used for expressing disagreement with the comment in question <br>
</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com2tag:blogger.com,1999:blog-6112936277054198647.post-53366228762894433392023-12-14T14:13:00.002+10:002023-12-14T14:13:17.820+10:00Xorg being removed. What does this mean?<p>
You may have seen the news that <a href="https://www.redhat.com/en/blog/rhel-10-plans-wayland-and-xorg-server">Red Hat Enterprise Linux 10 plans to remove Xorg</a>. But Xwayland will stay around, and given the name overloading and them sharing a git repository there's some confusion over what is Xorg. So here's a very simple "picture". This is the xserver git repository:
<pre>
$ tree -d -L 2 xserver
xserver
βββ composite
βββ config
βββ damageext
βββ dbe
βββ dix
βββ doc
βΒ Β βββ dtrace
βββ dri3
βββ exa
βββ fb
βββ glamor
βββ glx
βββ hw
βΒ Β βββ kdrive
βΒ Β βββ vfb
βΒ Β βββ xfree86 <- 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
</pre>
The git repo produces several X servers, including the one designed to run on bare metal: Xorg (in <i>hw/xfree86</i> for historical reasons). The other <i>hw</i> directories are the other X servers including Xwayland. All the other directories are core X server functionality that's shared between all X servers [1]. Removing Xorg from a distro but keeping Xwayland means building with <i>--disable-xfree86 -enable-xwayland</i> [1]. That's simply it (plus the resulting distro packaging work of course).
</p>
<p>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.</p>
<p>
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't get your hopes up for more Xorg releases beyond the security update background noise [2].
</p>
<p>
Xwayland on the other hand is actively maintained and will continue to see releases. But those releases are a sequence [1] of
<pre>
$ git new-branch xwayland-23.x.y
$ git rm hw/{kdrive/vfb/xfree86/xnest,xquartz,xwin}
$ git tag xwayland-23.x.y
</pre>
In other words, an Xwayland release is the xserver git master branch <i>with all X servers but Xwayland removed</i>. That's how Xwayland can see new updates and releases without Xorg ever seeing those (except on git master of course). And that'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.
</p>
<p>I hope this helps a bit with the confusion of the seemingly mixed messages sent when you see headlines like "Xorg is unmaintained", "X server patches to fix blah", "Xorg is abandoned", "new Xwayland release.</p>
<p>
<small>
[1] not 100% accurate but close enough<br>
[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<br>
</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-87140519253432822122023-11-10T13:22:00.001+10:002023-11-10T13:22:46.102+10:00PSA: For Xorg GNOME sessions, use the xf86-input-wacom driver for your tablets<p>
TLDR: see the title of this blog post, it's really that trivial.
</p>
<p>
Now that <strike>Godot</strike>Wayland has been coming for ages and all new development focuses on a pile of software
that steams significantly less, we're seeing cracks appear in the old Xorg support. Not intentionally,
but there'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.
</p>
<p>
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].
</p>
<p>
The xf86-input-libinput driver was written as a wrapper around libinput and makes use of fancy things that (from libinput'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.
</p>
<p>
The wacom driver of course doesn'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 "Wacom Serial IDs" property that contains the current tool'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.
</p>
<p>
Of course and because MOAR CONFIGURATION! will save us all from the great filter you can specify the "ToolSerials" xorg.conf option as
e.g. "airbrush;12345;artpen" 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]
</p>
<p>
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't,
so things fall through the cracks.
</p>
<p>
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'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's a good aim to strive for.
</p>
<p>
<small>
[1] It's just a joke. Put the pitchforks down already.<br>
[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.<br>
[3] It's also called puck. Because [2].<br>
[4] And by "query" I mean "wait for the XI2 event notifying you of a property change". 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's how hope dies.<br>
[5] Think about this next time someone says they long for some unspecified good old days.<br>
[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.<br>
[7] e.g because your'e stockholmed into it by your graphics hardware<br>
</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com2tag:blogger.com,1999:blog-6112936277054198647.post-2998491645208488592023-07-03T16:34:00.000+10:002023-07-03T16:34:09.720+10:00gitlab.freedesktop.org now has a bugbot for automatic issue/merge request processing<p>
As of today, gitlab.freedesktop.org provides easy hooks to invoke the <a href="https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage" target="_blank">gitlab-triage</a> tool for your project. <i>gitlab-triage</i> allows for the automation of recurring tasks, for example something like
<blockquote>
If the label FOO is set, close the issue and add a comment containing ".... blah ..."
</blockquote>
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.
</p>
<p>
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 <i>gitlab-triage</i> is run as part of project-specific scheduled pipelines but since we already have webhook-based <a href="https://who-t.blogspot.com/2023/03/new-gitlabfreedesktoporg-spamfighting.html" target="_blank">spam-fighting tools</a> we figured we could make this even easier.
</p>
<p>
So, bugbot was born. Any project registered with bugbot can use labels prefixed with <b>"bugbot::"</b> to have <i>gitlab-triage</i> invoked <b>against the project's policies file</b>. These labels thus serve as mini-commands for bugbot, though each project decides what happens for any particular label. bugbot effectively works like this:
<pre style="background-color: #ddffdd;">
sleep 30
for label in {issue|merge_request}.current_labels:
if label.startswith("bugbot::"):
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
</pre>
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're done.
The things of note here:
<ul>
<li>bugbot delays by 30 seconds, giving you time to unset an accidentally applied label before it takes effect</li>
<li>bugbot doesn't care about the label beyond the (hard-coded) <i>"bugbot::"</i> prefix</li>
<li>bugbot always runs <b>your</b> project's triage policies, from <b>your</b> main or master branch (whichever succeeds first)</li>
<li>The actions are performed as the bugbot user, not your user</li>
</ul>
The full documentation of what you can do in a policies file is available at the <a href="https://gitlab.com/gitlab-org/ruby/gems/gitlab-triage" target="_blank">gitlab-triage</a> documentation but let's look at a simple example that shouldn't even need explanation:
<pre style="background-color: #ddffdd;">
resource_rules:
issues:
rules:
- name: convert bugbot label to other label
conditions:
labels:
- "bugbot::foo"
actions:
labels:
- "foo"
remove_labels:
- "bugbot::foo"
comment: |
Nice label you have there. Would be a shame
if someone removed it
status: "close"
merge_requests:
rules:
[]
</pre>
And the effect of this file can be seen in <a href="https://gitlab.freedesktop.org/whot/ci-playground/-/issues/4" target="_blank">this issue</a> here.
</p>
<p>
<h2>Registering a project</h2>
<p>
Bugbot is part of the <a href="https://gitlab.freedesktop.org/freedesktop/damspam" target="_blank">damspam</a> project and registering a project can be done with a single command.
<b>Note: this can only be done by someone with the Maintainer role or above.</b>
</p><p>
Create a <a href="https://gitlab.freedesktop.org/-/profile/personal_access_token">personal access token</a> with API access and save the token value as <b>$XDG_CONFIG_HOME/bugbot/user.token</b>
Then run the following commands with your project's full path (e.g. mesa/mesa, pipewire/wireplumber, xorg/lib/libX11):
<pre>
$ pip install git+https://gitlab.freedesktop.org/freedesktop/damspam
$ bugbot request-webhook foo/bar
</pre>
After this you may remove the token file and the package
<pre>
$ pip uninstall damspam
$ rm $XDG_CONFIG_HOME/bugbot/user.token
</pre>
The bugbot command will file an issue in the <a href="https://gitlab.freedesktop.org/freedesktop/fdo-bots">freedesktop/fdo-bots</a> repository. This issue will be automatically processed and should be done by the time you finish the above commands, see <a href="https://gitlab.freedesktop.org/freedesktop/fdo-bots/-/issues/89">this issue for an example</a>. 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.
</p>
<p>
<h2>Adding triage policies</h2>
Once registered, the <b>.triage-policies.yml</b> 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 <i>gitlab-triage</i> <b>does not create new labels</b>. So any label in your actions must be manually created in the project first. Beyond that - the sky's your limit.
</p>
<p>
Remember you can test your policies file with
<pre>
$ gitlab-triage --dry-run --token $GITLAB_TOKEN \
--source-id foo/bar --resource-reference 1234
</pre>
<hr>
As usual, many thanks to Benjamin Tissoires for reviews and the magic of integrating this into infrastructure.
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-4038782843660032382023-06-06T19:36:00.001+10:002023-06-06T19:36:27.980+10:00snegg - Python bindings for libei<p>
After what was basically a flurry of typing, the <a href="https://gitlab.freedesktop.org/libinput/snegg">snegg Python bindings for libei</a> are now available. This is a Python package that provides bindings to the <a href="https://gitlab.freedesktop.org/libinput/libei/">libei/libeis/liboeffis</a> 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 <a href="https://gitlab.freedesktop.org/libinput/snegg/-/tree/main/examples">examples directory</a> 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.
</p>
<p>
A proper way to support EI in Python would be to implement the protocol directly - there's no need for the C API quirkiness this way and you can make full use of things like <i>async</i> and whatnot. If you're interested in that, get in touch! Meanwhile, writing something roughly resemling xdotool is probably only a few hundred lines of python code. [2]
</p>
<p>
<small>
[1] writing these also exposed a few bugs in libei itself so I'm happy 1.0 wasn't out just yet<br/>
[2] at least the input emulation parts of xdotool
</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-25530144926872945162023-05-09T10:51:00.003+10:002023-05-09T10:51:24.239+10:00libei and a fancy protocol<p>
<a href="https://gitlab.freedesktop.org/libinput/libei/">libei</a> is the library for Emulated Input - see <a href="https://who-t.blogspot.com/2020/08/libei-library-to-support-emulated-input.html">this post</a> 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've upgraded the answer to that question from "hopefully" to "yeah, I reckon" - doubly so since we added support for <a href="https://who-t.blogspot.com/2022/03/libei-adding-support-for-passive.html">receiver contexts</a> and got <a href="https://github.com/input-leap/input-leap/pull/1594#">InputLeap</a> working through the various <a href="https://gitlab.freedesktop.org/libinput/libei/-/issues/1">portal changes</a>.
</p>
<p>
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'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 <b>must</b> 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.
</p>
<p>
The protocol is defined in a single XML file and can be used directly from language bindings (if any). The <a href="https://libinput.pages.freedesktop.org/libei/index.html">protocol documentation</a> is quite extensive but it'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'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't really find it worth the effort to add this.
</p>
<p>
Anyway, long story short: swapping the protocol out didn'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 "pointer", "button" and "scroll" interfaces, or maybe the "pointer_absolute", "button" and "scroll" 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.
</p>
<p>
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.
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-15964287719429986962023-03-28T19:30:00.001+10:002023-03-29T17:31:52.813+10:00New gitlab.freedesktop.org spamfighting abilities<p>
As of today, gitlab.freedesktop.org allows anyone with a <a href="https://docs.gitlab.com/ee/user/permissions.html#permissions-and-roles" target="_blank">GitLab Developer role or above</a> to remove spam issues. If you are reading this article a while after it's published, it's best to refer to the <a href="https://gitlab.freedesktop.org/freedesktop/damspam">damspam README</a> for up-to-date details.
I'm going to start with the TLDR first.
</p>
<h2>For Maintainers</h2>
<p>
Create a <a href="https://gitlab.com/-/profile/personal_access_tokens">personal access token</a> with API access and save the token value as <b>$XDG_CONFIG_HOME/damspam/user.token</b>
Then run the following commands with your project's full path (e.g. mesa/mesa, pipewire/wireplumber, xorg/lib/libX11):
<pre>
$ 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
</pre>
The damspam command will file an issue in the <a href="https://gitlab.freedesktop.org/freedesktop/fdo-bots">freedesktop/fdo-bots</a> repository. This issue will be automatically processed by a bot and should be done by the time you finish the above commands, see <a href="https://gitlab.freedesktop.org/freedesktop/fdo-bots/-/issues/9">this issue for an example</a>. 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.
</p>
<p>
Once the request has been processed (and again, this should be instant), any issue in your project that gets assigned the label <b>Spam</b> will be processed automatically by damspam. See the next section for details.
</p>
<h2>For Developers</h2>
<p>
Once the maintainer for your project has requested the webhook, simply assign the <b>Spam</b> label to any issue that is spam. The issue creator will be blocked (i.e. cannot login), this issue <b>and any other issue filed by the same user</b> 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're blocked from producing more. This should happen within seconds of assigning the Spam label.
</p>
<h2>For GitLab Admins</h2>
<p>
Create a <a href="https://gitlab.com/-/profile/personal_access_tokens">personal access token</a> with API access <b>for the @spambot user</b> and save the token value as <b>$XDG_CONFIG_HOME/damspam/spambot.token</b>. This is so you can operate as spambot instead of your own user.
Then run the following command to remove all tagged spammers:
<pre>
$ pip install git+https://gitlab.freedesktop.org/freedesktop/damspam
$ damspam purge-spammers
</pre>
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 <b>after interactive confirmation</b> purge them as requested. At the time of writing, the output looks like this:
<pre>
$ 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:
</pre>
Purging the spammers will hard-delete them and remove anything they ever did on gitlab. This is irreversible.
</p>
<h2>How it works</h2>
<p>
There are two components at play here: <a href="https://gitlab.freedesktop.org/freedesktop/hookiedookie/">hookiedookie</a>, a generic webhook dispatcher, and <a href="https://gitlab.freedesktop.org/freedesktop/damspam/">damspam</a> which handles the actual spam issues. Hookiedookie provides an HTTP server and "does things" with JSON data on request. What it does is relatively generic (see the <a href="https://gitlab.freedesktop.org/bentiss/hookiedookie/-/blob/main/Settings.yaml">Settings.yaml</a> example file) but it's set up to be triggered by a GitLab webhook and thus receives <a href="https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#issue-events">this payload</a>. For damspam the rules we have for hookiedookie come down to something like this: if the URL is "webhooks/namespace/project" and damspam is set up for this project and the payload is an issue event and it has the "Spam" 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.
</p>
<p>
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.
</p>
<p>
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't have to worry about it suddenly blocking every project member.
</p>
<h2>Why?</h2>
<p>
For a while now, we've suffered from a deluge of spam (and worse) that makes it through the spam filters. GitLab has a <a href="https://docs.gitlab.com/ee/user/report_abuse.html">Report Abuse</a> feature for this but it's... woefully incomplete. The UI guides users to do the right thing - as reporter you can tick "the user is sending spam" and it automatically adds a link to the reported issue. But: <b>none of this useful data is visible to admins</b>. Seriously, look at the <a href="https://docs.gitlab.com/ee/user/admin_area/review_abuse_reports.html">official screenshots</a>. 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 <i>almost never has any useful information</i>. The link to the issue? Not there. The selection that the user is a spammer? Not there.
</p>
<p>
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't find the issue for whatever reason.
</p>
<p>
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.
</p>
<p>
With hookiedookie and damspam, we'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'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're spam. In fact, they probably could've deleted issues beforehand already anyway if they wanted to make them disappear.
</p>
<h2>Other instances</h2>
<p>
While we're definitely aiming at gitlab.freedesktop.org, there's nothing in particular that requires this instance. If you'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.
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-87864016033207989212023-01-17T14:47:00.001+10:002023-01-17T14:47:22.315+10:00libinput and the custom pointer acceleration function<p>
After 8 months of work by Yinon Burgansky, libinput now has a new pointer acceleration profile: the "custom" profile. This profile allows users to tweak the exact response of their device based on their input speed.
</p>
<p>
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 <i>(x, y)</i> becomes <i>(Fx, Fy)</i>. How this is done is specific to the profile, libinput's existing profiles had either a flat factor or an adaptive factor that roughly resembles what Xorg used to have, see
the <a href="https://wayland.freedesktop.org/libinput/doc/latest/pointer-acceleration.html#pointer-acceleration-profiles">libinput documentation</a> 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.
<p>
<h2>Input speed to output speed</h2>
<p>
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 <i>(x, f(x))</i> that are linearly interpolated. Each point is defined as <b>input speed in device units/ms</b> to <b>output speed in device units/ms</b>. For example, to provide a flat acceleration equivalent, specify <i>[(0.0, 0.0), (1.0, 1.0)]</i>. With the linear interpolation this is of course a 45-degree function, and any incoming speed will result in the equivalent output speed.
</p>
<p>
Noteworthy: we are talking about the <b>speed</b> 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. "user moved 1 unit over 21ms"). This avoids some jumpyness at low speeds.
</p>
<p>
But <i>because</i> the curve is speed-based, it allows for some interesting features too: the curve <i>[(0.0, 1.0), (1.0, 1.0)]</i> is a horizontal function at 1.0. Which means that <i>any</i> 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'm not immediately sure of a real-world use case for this particular case (some accessibility needs maybe) but I'm sure it's a good prank to play on someone.
</p>
<p>
Because libinput is written in C, <a href="https://wayland.freedesktop.org/libinput/doc/latest/api/group__config.html#ga2e87262b38c8ac3fe6b4f9a60c5a69e4" target="_blank">the API</a> is not necessarily immediately obvious but: to configure you pass an array of (what will be) y-values and set the <i>step-size</i>. The curve then becomes: <i>[(0 * step-size, array[0]), (1 * step-size, array[1]), (2 * step-size, array[2]), ...]</i>. There are some limitations on the number of points but they're high enough that they should not matter.
</p>
<p>
Note that any curve is still <b>device-resolution dependent</b>, 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't change in response.
</p>
<h2>Motion types</h2>
<p>
Finally, there's another feature in this PR - the so-called "movement type" which must be set when defining a curve. Right now, we have two types, "fallback" and "motion". The "motion" type applies to, you guessed it, pointer motion. The only other type available is fallback which applies to everything <i>but</i> 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.
</p>
<h2>How to use this?</h2>
<p>
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 <a href="https://gitlab.freedesktop.org/xorg/driver/xf86-input-libinput/-/merge_requests/39" target="_blank">merge request</a> adds a few properties so that you can play with this using the <i>xinput</i> tool:
<pre>
# Set the flat-equivalent function described above
$ xinput set-prop "devname" "libinput Accel Custom Motion Points" 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 "devname" "libinput Accel Custom Motion Points" 1.0
# Now enable the custom profile
$ xinput set-prop "devname" "libinput Accel Profile Enabled" 0 0 1
</pre>
The above sets a custom pointer accel for the "motion" 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).
</p>
<p>
Happy playing around (and no longer filing bug reports if you don't like the default pointer acceleration ;)
</p>
<h2>Availability</h2>
<p>
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.
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com1tag:blogger.com,1999:blog-6112936277054198647.post-81862850076571421142023-01-06T12:15:00.003+10:002023-01-18T08:20:25.116+10:00X servers no longer allow byte-swapped clients (by default)<p>
In the beginning, there was the egg. Then <a href="https://en.wikipedia.org/wiki/Gulliver%27s_Travels">fictional people</a> started eating that from different ends, and the terms of "little endians" and "Big Endians" was born.
</p>
<p>
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't know, a strawberry plant.
</p>
<p>
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 "l" or "B" to inform the server of the client's byte order. Where the byte order doesn't match the X server's byte order, the client is a "swapped client" in X server terminology and all 16, 32, and 64-bit values must be "byte-swapped" into the server's byte order. All of those values in all requests, and then again back to the client's byte order in all outgoing replies and events. Forever, till a crash do them part.
</p>
<p>
If you get one of those wrong, the number is no longer correct. And it'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 <b>SProcFoo</b> implementations (X server naming scheme for <b>S</b>wapped<b> Proc</b>edure for request <b>Foo</b>) is so long that I'm too lazy to pull out the various security advisories and link to them. Just believe me, ok? <i>*jedi handwave*</i>
</p>
<p>
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's not do that?
</p>
<p>
I just <a href="https://gitlab.freedesktop.org/xorg/xserver/-/merge_requests/1029">merged a PR</a> 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 <i>"Prohibited client endianess, see the Xserver man page"</i>. [2] Thus, a whole class of future security issues avoided - yay!
</p>
<p>
For the use-cases where you <b>do</b> 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 <b>+byteswappedclients</b> commandline option. Alternatively, and this only applies for Xorg: add <b>Option "AllowByteSwappedClients" "on"</b> to the xorg.conf <b>ServerFlags</b> section. Both of these will change the default back to the original setting. Both are documented in the <b>Xserver(1)</b> and <b>xorg.conf(5)</b> man pages, respectively.
</p>
<p>
Now, there'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 <b>+byteswappedclients</b> to Xwayland. This is compositor-specific, bugs are filed for <a href="https://gitlab.gnome.org/GNOME/mutter/-/issues/2576">mutter</a> (merged for GNOME 44), <a href="https://invent.kde.org/plasma/kwin/-/issues/131">kwin</a> and <a href="https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3558">wlroots</a>. 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).
</p>
<p>
There's no specific plan yet which X releases this will end up in, primarily because the release cycle for X is...undefined. Probably <i>xserver-23.0</i> if and when that happens. It'll probably find its way into the <i>xwayland-23.0</i> 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 <a href="https://fedoraproject.org/w/index.php?title=Changes/XServerProhibitsByteSwappedClients">Fedora 38 change</a>.
</p>
<p>
<small>
[0] Also, it doesn't help that much of the X server's protocol handling code was written with the attitude of "surely the client wouldn't lie about that length value"<br/>
[1] little-endian client to Big Endian X server is so rare that it's barely worth talking about. But suffice to say, the exact same applies, just with little and big swapped around.<br/>
[2] That message is unceremoniously dumped to stderr, but that bit is unfortunately a libxcb issue.<br/>
</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com11tag:blogger.com,1999:blog-6112936277054198647.post-4906591787815875552022-12-13T13:24:00.000+10:002022-12-13T13:24:04.683+10:00libei - opening the portal doors <p>Time for another status update on <a href="https://who-t.blogspot.com/2021/08/libei-status-update.html">libei</a>, the transport layer for bouncing emulated input events between applications and Wayland compositors [1]. And this time it's all about portals and how we're about to use them for libei communication.
I've hinted at this in the <a href="https://who-t.blogspot.com/2022/03/libei-adding-support-for-passive.html">last post</a>, but of course you're forgiven if you forgot about this in the... uhm.. "interesting" year that was 2022. So, let's recap first:
</p>
<p>
Our basic premise is that we want to emulate and/or capture input events in the glorious new world that is Wayland (read: where applications can't do whatever they want, whenever they want). <a href="https://gitlab.freedesktop.org/libinput/libei/" target="_blank">libei</a> is a C library [0] that aims to provide this functionality.
libei supports "sender" and "receiver" contexts and that just specifies which way the events will flow. A sender context (e.g. xdotool) will send emulated input events to the compositor, a "receiver" context will - you'll never guess! - receive events from the compositor. If you have the InputLeap [2] use-case, the server-side will be a receiver context, the client side a sender context. But libei is really just the transport layer and hasn't had that many changes since the last post - most of the effort was spent on trying to figure out how to exchange the socket between different applications. And for that, we have portals!
</p>
<h2>RemoteDesktop</h2>
<p>
In particular, we have a <a href="https://github.com/flatpak/xdg-desktop-portal/pull/762">PR for the RemoteDesktop</a> portal to add that socket exchange. In particular, once a RemoteDesktop session starts your application can request an EIS socket and send input events over that. This socket supersedes the current <i>NotifyButton</i> and similar DBus calls and removes the need for the portal to stay in the middle - the application and compositor now talk directly to each other. The compositor/portal can still close the session at any time though, so all the benefits of a portal stay there. The big advantage of integrating this into RemoteDesktop is that the infrastructucture for that is already mostly in place - once your compositor adds the bits for the new ConnectToEIS method you get all the other pieces for free. In GNOME this includes a visual indication that your screen is currently being remote-controlled, same as from a real RemoteDesktop session.
</p>
<p>
Now, talking to the RemoteDesktop portal is nontrivial simply because using DBus is nontrivial, doubly so for the way how sessions and requests work in the portals. To make this easier, libei 0.4.1 now includes a new library "liboeffis" that enables your application to catch the DBus. This library has a <a href="https://libinput.pages.freedesktop.org/libei/group__oeffis.html">very small API</a> and can easily be integrated with your mainloop (it's very similar to libei). We have patches for Xwayland to use that and it's really trivial to use. And of course, with the other Xwayland work we already had this means we can really run xdotool through Xwayland to connect through the XDG Desktop Portal as a RemoteDesktop session and move the pointer around. Because, kids, remember, uhm, Unix is all about lots of separate pieces.
</p>
<h2>InputCapture</h2>
<p>
On to the second mode of libei - the receiver context. For this, we also use a portal but a brand new one: the <a href="https://github.com/flatpak/xdg-desktop-portal/pull/714" target="_blank">InputCapture portal</a>. The InputCapture portal is the one to use to decide <b>when</b> input events should be captured. The actual events are then sent over the EIS socket.
</p>
<p>
Right now, the InputCapture portal supports PointerBarriers - virtual lines on the screen edges that, once crossed, trigger input capture for a capability (e.g. pointer + keyboard). And an application's basic approach is to request a (logical) representation of the available desktop areas ("Zones") and then set up pointer barriers at the edge(s) of those Zones. Get the EIS connection, <i>Enable()</i> the session and voila - the compositor will (hopefully) send input events when the pointer crosses one of those barriers. Once that happens you'll get a DBus signal in InputCapture and the events will start flowing on the EIS socket. The portal itself doesn't need to sit in the middle, events go straight to the application. The portal can still close the session anytime though. And the compositor can decide to stop capturing events at any time.
</p>
<p>
There is actually zero Wayland-y code in all this, it's display-system acgnostic. So anyone with too much motivation could add this to the X server too. Because that's what the world needs...
</p>
<p>
The (currently) bad news is that this needs to be pulled into a lot of different repositories. And everything needs to get ready before it can be pulled into anything to make sure we don't add broken API to any of those components.
But thanks to a lot of work by Olivier Fourdan, we have this mostly working in InputLeap (tbh the remaining pieces are largely XKB related, not libei-related). Together with the client implementation (through RemoteDesktop) we can move pointers around like in the InputLeap of old (read: X11).
</p>
<p>Our current goal is for this to be ready for GNOME 45/Fedora 39.</p>
<p>
<small>
[0] eventually a protocol but we're not there yet<br/>
[1] It doesn't actually have to be a compositor but that's the prime use-case, so...<br/>
[2] or barrier or synergy. I'll stick with InputLeap for this post<br/>
</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-59412049974220686702022-08-11T16:50:00.004+10:002022-08-11T16:50:30.130+10:00The new XWAYLAND extension is available<p>As of xorgproto 2022.2, we have a new X11 protocol extension. First, you may rightly say "whaaaat? why add new extensions to the X protocol?" in a rather unnecessarily accusing way, followed up by "that's like adding lipstick to a dodo!". And that's not completely wrong, but nevertheless, we have a new protocol extension to the ... [checks calendar] almost 40 year old X protocol. And that extension is, ever creatively, named "XWAYLAND".
</p>
<p>
If you recall, Xwayland is a different X server than Xorg. It doesn't try to render directly to the hardware, instead it's a translation layer between the X protocol and the Wayland protocol so that X clients can continue to function on a Wayland compositor. The X application is generally unaware that it isn't running on Xorg and Xwayland (and the compositor) will do their best to accommodate for all the quirks that the application expects because it only speaks X. In a way, it's like calling a restaurant and ordering a burger because the person answering speaks American English. Without realising that you just called the local fancy French joint and now the chefs will have to make a burger for you, totally <a href="https://wiki.lspace.org/Avec">without avec</a>.
</p>
<p>
Anyway, sometimes it is necessary for a client (or a user) to know whether the X server is indeed Xwayland. Previously, this was done through heuristics: the <a href="http://who-t.blogspot.com/2020/05/xisxwayland-checks-for-xwayland-or-not.html">xisxwayland</a> tool checks for XRandR properties, the xinput tool checks for input device names, and so on. These heuristics are just that, though, so they can become unreliable as Xwayland gets closer to emulating Xorg or things just change. And properties in general are problematic since they could be set by other clients. To solve this, we now have a new extension.
</p>
<p>
The XWAYLAND extension doesn't actually do anything, it's the bare minimum required for an extension. It just needs to exist and clients only need to XQueryExtension or check for it in XListExtensions (the equivalent to <b>xdpyinfo | grep XWAYLAND</b>). Hence, no support for Xlib or libxcb is planned. So of all the nightmares you've had in the last 2 years, the one of misidentifying Xwayland will soon be in the past.
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-67547442816147981442022-03-04T14:30:00.002+10:002022-03-04T14:30:31.641+10:00libei - adding support for passive contexts<p>A quick reminder: <a href="https://who-t.blogspot.com/2020/08/libei-library-to-support-emulated-input.html" target="_blank">libei</a> is the <b>lib</b>rary for <b>e</b>mulated <b>i</b>nput. It comes as a pair of C libraries, <i>libei</i> for the client side and <i>libeis</i> for the server side.</p>
<p>
libei has been sitting mostly untouched since the last <a href="https://who-t.blogspot.com/2021/08/libei-status-update.html">status update</a>. There are two use-cases we need to solve for input emulation in Wayland - the ability to emulate input (think xdotool, or Synergy/Barrier/InputLeap client) and the ability to capture input (think Synergy/Barrier/InputLeap server). The latter effectively blocked development in libei [1], until that use-case was sorted there wasn't much point investing too much into libei - after all it may get thrown out as a bad idea. And epiphanies were as elusive like toilet paper and RATs, so nothing much get done. This changed about a week or two ago when the required lightbulb finally arrived, pre-lit from the factory.
</p>
<p>
So, the solution to the input capturing use-case is going to be a so-called <a href="https://gitlab.freedesktop.org/libinput/libei/-/merge_requests/80" target="_blank">"passive context" for libei</a>. In the traditional [2] "active context" approach for libei we have the EIS implementation in the compositor and a client using libei to connect to that. The compositor sets up a seat or more, then some devices within that seat that typically represent the available screens. libei then sends events through these devices, causing input to be appear in the compositor which moves the cursor around. In a typical and simple use-case you'd get a 1920x1080 absolute pointer device and a keyboard with a $layout keymap, libei then sends events to position the cursor and or happily type away on-screen.
</p>
<p>
In the "passive context" <i><deja-vu></i> approach for libei we have the EIS implementation in the compositor and a client using libei to connect to that. The compositor sets up a seat or more, then some devices within that seat <i></deja-vu></i> that typically represent <b>the physical devices</b> connected to the host computer. libei then <b>receives</b> events from these devices, causing input to be generated <b>in the libei client</b>. In a typical and simple use-case you'd get a <b>relative pointer device</b> and a keyboard device with a $layout keymap, <b>the compositor</b> then sends events matching the relative input of the connected mouse or touchpad.
</p>
<p>The two notable differences are thus: events flow from EIS to libei and the devices don't represent the screen but rather the physical [3] input devices.</p>
<p>This changes libei from a library for emulated input to an input event transport layer between two processes. On a much higher level than e.g. evdev or HID and with more contextual information (seats, devices are logically abstracted, etc.). And of course, the EIS implementation is always in control of the events, regardless which direction they flow. A compositor can implement an event filter or designate key to break the connection to the libei client. In pseudocode, the compositor's input event processing function will look like this:
<pre>
function handle_input_events():
real_events = libinput.get_events()
for e in real_events:
if input_capture_active:
send_event_to_passive_libei_client(e)
else:
process_event(e)
emulated_events = eis.get_events_from_active_clients()
for e in emulated_events:
process_event(e)
</pre>
Not shown here are the various appropriate filters and conversions in between (e.g. all relative events from libinput devices would likely be sent through the single relative device exposed on the EIS context). Again, the compositor is in control so it would be trivial to implement e.g. capturing of the touchpad only but not the mouse.
</p>
<p>
In the current design, a libei context can only be active or passive, not both. The EIS context is both, it's up to the implementation to disconnect active or passive clients if it doesn't support those.
</p>
<p>
Notably, the above only caters for the transport of input events, it doesn't actually make any decision on <i>when</i> to capture events. This handled by the <a href="https://github.com/flatpak/xdg-desktop-portal/pull/714">CaptureInput XDG Desktop Portal</a> [4]. The idea here is that an application like Synergy/Barrier/InputLeap server connects to the CaptureInput portal and requests a CaptureInput session. In that session it can define pointer barriers (left edge, right edge, etc.) and, in the future, maybe other triggers. In return it gets a libei socket that it can initialize a libei context from. When the compositor decides that the pointer barrier has been crossed, it re-routes the input events through the EIS context so they pop out in the application. Synergy/Barrier/InputLeap then converts that to the global position, passes it to the right remote Synergy/Barrier/InputLeap client and replays it there through an active libei context where it feeds into the local compositor.
</p>
<p>
Because the management of when to capture input is handled by the portal and the respective backends, it can be natively integrated into the UI. Because the actual input events are a direct flow between compositor and application, the latency should be minimal. Because it's a high-level event library, you don't need to care about hardware-specific details (unlike, say, <a href="https://who-t.blogspot.com/2017/04/inputfd-protocol-for-direct-access-to.html">the inputfd proposal from 2017</a>). Because the negotiation of when to capture input is through the portal, the application itself can run inside a sandbox. And because libei only handles the transport layer, compositors that don't want to support sandboxes can set up their own negotiation protocol.
</p>
<p>
So overall, right now this seems like a workable solution.
</p>
<p>
<small>
[1] "blocked" is probably overstating it a bit but no-one else tried to push it forward, so..<br/>
[2] "traditional" is probably overstating it for a project that's barely out of alpha development<br/>
[3] "physical" is probably overstating it since it's likely to be a logical representation of the types of inputs, e.g. one relative device for all mice/touchpads/trackpoints<br/>
[4] "handled by" is probably overstating it since at the time of writing the portal is merely a draft of an XML file<br/>
</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-73786946980163313692022-02-15T15:24:00.002+10:002022-02-15T15:24:30.465+10:00The xf86-input-wacom driver hits 1.0<p>
After roughly 20 years and counting up to 0.40 in release numbers, I've decided to call the next version of the <a href="https://github.com/linuxwacom/xf86-input-wacom/">xf86-input-wacom driver</a> the 1.0 release. [1]
This cycle has seen a bulk of development (>180 patches) which is roughly as much as the last 12 releases together. None of these patches actually added user-visible features, so let's talk about technical dept and what turned out to be an interesting way of reducing it.
</p>
<p>
The wacom driver's git history goes back to 2002 and the current batch of maintainers (Ping, Jason and I) have all been working on it for one to two decades. It used to be a Wacom-only driver but with the improvements made to the kernel over the years the driver should work with most tablets that have a kernel driver, albeit some of the more quirky niche features will be more limited (but your non-Wacom devices probably don't have those features anyway).
</p>
<p>
The one constant was always: the driver was extremely difficult to test, something common to all X input drivers. Development is a cycle of restarting the X server a billion times, testing is mostly plugging hardware in and moving things around in the hope that you can spot the bugs. On a driver that doesn't move much, this isn't necessarily a problem. Until <a href="https://github.com/linuxwacom/xf86-input-wacom/issues/186#issuecomment-948101940">a bug</a> comes along, that requires some core rework of the event handling - in the <a href="https://lore.kernel.org/lkml/20220203143226.4023622-1-benjamin.tissoires@redhat.com/T/">kernel</a>, <a href="https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/724">libinput</a> and, yes, the wacom driver.
</p>
<p>
After years of libinput development, I wasn't really in the mood for the whole "plug every tablet in and test it, for every commit". In a rather caffeine-driven development cycle [2], the driver was separated into two logical entities: the core driver and the "frontend". The default frontend is the X11 one which is now a relatively thin layer around the core driver parts, primarily to translate events into the X Server's API. So, not unlike libinput + xf86-input-libinput in terms of architecture. In ascii-art:
<pre>
|
+--------------------+ | big giant
/dev/input/event0->| core driver | x11 |->| X server
+--------------------+ | process
|
</pre>
</p>
<p>
Now, that logical separation means we can have another frontend which <a href="https://github.com/linuxwacom/xf86-input-wacom/blob/master/src/gwacom/wacom-device.h">I implemented as a relatively light GObject wrapper</a> and is now a library creatively called <b>libgwacom</b>:
<pre>
+-----------------------+ |
/dev/input/event0->| core driver | gwacom |--| tools or test suites
+-----------------------+ |
</pre>
This isn't a public library or API and it's very much focused on the needs of the X driver so there are some peculiarities in there. What it allows us though is a new wacom-record tool that can hook onto event nodes and <b>print</b> the events as they come out of the driver. So instead of having to restart X and move and click things, you get this:
<pre>
$ ./builddir/wacom-record
wacom-record:
version: 0.99.2
git: xf86-input-wacom-0.99.2-17-g404dfd5a
device:
path: /dev/input/event6
name: "Wacom Intuos Pro M Pen"
events:
- source: 0
event: new-device
name: "Wacom Intuos Pro M Pen"
type: stylus
capabilities:
keys: true
is-absolute: true
is-direct-touch: false
ntouches: 0
naxes: 6
axes:
- {type: x , range: [ 0, 44800], resolution: 200000}
- {type: y , range: [ 0, 29600], resolution: 200000}
- {type: pressure , range: [ 0, 65536], resolution: 0}
- {type: tilt_x , range: [ -64, 63], resolution: 57}
- {type: tilt_y , range: [ -64, 63], resolution: 57}
- {type: wheel , range: [ -900, 899], resolution: 0}
...
- source: 0
mode: absolute
event: motion
mask: [ "x", "y", "pressure", "tilt-x", "tilt-y", "wheel" ]
axes: { x: 28066, y: 17643, pressure: 0, tilt: [ -4, 56], rotation: 0, throttle: 0, wheel: -108, rings: [ 0, 0]
</pre>
This is YAML which means we can process the output for comparison or just to search for things.
</p>
<p>
A tool to quickly analyse data makes for faster development iterations but it's still a far cry from reliable regression testing (and writing a test suite is a daunting task at best). But one nice thing about GObject is that it's accessible from other languages, including Python. So our test suite can be in Python, using pytest and all its capabilities, plus all the advantages Python has over C. Most of driver testing comes down to: create a uinput device, set up the driver with some options, push events through that device and verify they come out of the driver in the right sequence and format. I don't need C for that. So there's <a href="https://github.com/linuxwacom/xf86-input-wacom/pull/241">pull request</a> sitting out there doing exactly that - adding a pytest test suite for a 20-year old X driver written in C. That this is a) possible and b) a lot less work than expected got me quite unreasonably excited. If you do have to maintain an old C library, maybe consider whether's possible doing the same because there's nothing like the warm fuzzy feeling a green tick on a CI pipeline gives you.
</p>
<p><small>
[1] As scholars of version numbers know, they make as much sense as your stereotypical uncle's facebook opinion, so why not. <br/>
[2] The Colombian GDP probably went up a bit<br/>
</small></p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com1tag:blogger.com,1999:blog-6112936277054198647.post-27751466829707686532021-09-23T15:26:00.003+10:002021-09-23T15:26:39.292+10:00What's new in XI 2.4 - touchpad gestures<p>After a nine year hiatus, a new version of the X Input Protocol is out. Credit for the work goes to Povilas Kanapickas, who also implemented support for XI 2.4 in the various pieces of the stack [0]. So let's have a look.</p>
<p>
X has had touch events since XI 2.2 (2012) but those were only really useful for direct touch devices (read: touchscreens). There were accommodations for indirect touch devices like touchpads but they were never used. The synaptics driver set the required bits for a while but it was dropped in 2015 because ... it was complicated to make use of and no-one seemed to actually use it anyway. Meanwhile, the rest of the world moved on and touchpad gestures are now prevalent. They've been standard in MacOS for ages, in Windows for almost ages and - with recent GNOME releases - now feature prominently on the Linux desktop as well. They have been part of libinput and the Wayland protocol for years (and even recently gained <a href="http://who-t.blogspot.com/2021/07/libinput-and-hold-gestures.html" target="_blank">a new set of "hold" gestures</a>). Meanwhile, X was left behind in the dust or mud, depending on your local climate.
</p>
<p>
XI 2.4 fixes this, it adds pinch and swipe gestures to the XI2 protocol and makes those available to supporting clients [2]. Notably here is that the interpretation of gestures is left to the driver [1]. The server takes the gestures and does the required state handling but otherwise has no decision into what constitutes a gesture. This is of course no different to e.g. 2-finger scrolling on a touchpad where the server just receives scroll events and passes them on accordingly.
</p>
<p>
XI 2.4 gesture events are quite similar to touch events in that they are processed as a sequence of begin/update/end with both types having their own event types. So the events you will receive are e.g. <i>XIGesturePinchBegin</i> or <i>XIGestureSwipeUpdate</i>. As with touch events, a client must select for all three (begin/update/end) on a window. Only one gesture can exist at any time, so if you are a multi-tasking octopus prepare to be disappointed.
</p>
<p>
Because gestures are tied to an indirect-touch device, the location they apply at is wherever the cursor is currently positioned. In that, they work similar to button presses, and passive grabs apply as expected too. So long-term the window manager will likely want a passive grab on the root window for swipe gestures while applications will implement pinch-to-zoom as you'd expect.
</p>
<p>
In terms of API there are no suprises. libXi 1.8 is the version to implement the new features and there we have a new <i>XIGestureClassInfo</i> returned by <i>XIQueryDevice</i> and of course the two events: <i>XIGesturePinchEvent</i> and <i>XIGestureSwipeEvent</i>. Grabbing is done via e.g. <i>XIGrabSwipeGestureBegin</i>, so for those of you with XI2 experience this will all look familiar. For those of you without - it's probably no longer worth investing time into becoming an XI2 expert.
</p>
<p>
Overall, it's a nice addition to the protocol and it will help getting the X server slightly closer to Wayland for a widely-used feature. Once GTK, mutter and all the other pieces in the stack are in place, it will just work for any (GTK) application that supports gestures under Wayland already. The same will be true for Qt I expect.
</p>
<p>
X server 21.1 will be out in a few weeks, xf86-input-libinput 1.2.0 is already out and so are xorgproto 2021.5 and libXi 1.8.
</p>
<p>
<small>
[0] In addition to taking on the <a href="https://lists.x.org/archives/xorg-announce/2021-September/003111.html" target="_blank">Xorg release</a>, so clearly there are no limits here<br/>
[1] More specifically: it's done by libinput since neither xf86-input-evdev nor xf86-input-synaptics will ever see gestures being implemented<br/>
[2] Hold gestures missed out on the various deadlines<br/>
</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-29451615067868086982021-09-22T13:16:00.001+10:002021-09-23T09:00:42.095+10:00An Xorg release without Xwayland<p>Xorg is <a href="https://lists.x.org/archives/xorg/2021-September/060773.html" target="_blank">about to released</a>.</p>
<p>And it's a release without Xwayland.</p>
</p>And... wait, what?</p>
</p>Let's unwind this a bit, and ideally you should come away with a better
understanding of Xorg vs Xwayland, and possibly even Wayland itself.</p>
<p>Heads up: if you are familiar with X, the below is simplified to the point
it hurts. Sorry about that, but as an X developer you're probably good at coping with pain.</p>
<p>Let's go back to the 1980s, when fashion was weird and there were still
reasons to be optimistic about the future. Because this is a thought
exercise, we go back with full hindsight 20/20 vision and, ideally, the
winning Lotto numbers in case we have some time for some self-indulgence.</p>
<p>If we were to implement an X server from scratch, we'd come away with a set of
components. libxprotocol that handles the actual protocol wire format parsing
and provides a C api to access that (quite like libxcb, actually). That one
will just be the protocol-to-code conversion layer.</p>
<p>We'd have a libxserver component which handles all the state management required
for an X server to actually behave like an X server (nothing in the X protocol
require an X server to display anything). That library has a few entry
points for abstract input events (pointer and keyboard, because this is the
80s after all) and a few exit points for rendered output.</p>
<p>libxserver uses libxprotocol but that's an implementation detail, we can
ignore the protocol for the rest of the post.</p>
<p>Let's create a github organisation and host those two libraries. We now have:
<i>http://github.com/x/libxserver</i> and <i>http://github.com/x/libxprotocol</i> [1].<p>
<p>Now, to actually implement a working functional X server, our new project
would link against libxserver hook into this library's API points. For input,
you'd use libinput and pass those events through, for output you'd use the
modesetting driver that knows how to scream at the hardware until something
finally shows up. This is somewhere between outrageously simplified and
unacceptably wrong but it'll do for this post.
</p>
<p>
Your X server has to handle a lot of the hardware-specifics but other than
that it's a wrapper around libxserver which does the work of ... well, being
an X server.
</p>
<p>
Our stack looks like this:
<pre>
+------------------------+
| xserver [libxserver]|--------[ X client ]
| |
|[libinput] [modesetting]|
+------------------------+
| kernel |
+------------------------+
</pre>
Hooray, we have re-implemented Xorg. Or rather, XFree86 because we're 20 years
from all the pent-up frustratrion that caused the Xorg fork. Let's host this project
on<i> http://github.com/x/xorg</i>
</p>
<p>
Now, let's say instead of physical display devices, we want to render into an
framebuffer, and we have no input devices.
<pre>
+------------------------+
| xserver [libxserver]|--------[ X client ]
| |
| [write()] |
+------------------------+
| some buffer |
+------------------------+
</pre>
This is basically Xvfb or, if you are writing out PostScript, Xprint. Let's
host those on github too, we're accumulating quite a set of projects here.
</p>
<p>
Now, let's say those buffers are allocated elsewhere and we're just rendering
to them. And those buffer are passed to us via an IPC protocol, like...
Wayland!
<pre>
+------------------------+
| xserver [libxserver]|--------[ X client ]
| |
|input events [render]|
+------------------------+
| |
+------------------------+
| Wayland compositor |
+------------------------+
</pre>
And voila, we have Xwayland. If you swap out the protocol you can have
Xquartz (X on Macos) or Xwin (X on Windows) or Xnext/Xephyr (X on X) or Xvnc
(X over VNC). The principle is always the same.
</p>
<p>
Fun fact: the Wayland compositor doesn't need to run on the hardware, you can
play display server matryoshka until you run out of turtles.
</p>
<p>
In our glorious revisioned past all these are distinct projects, re-using
libxserver and some external libraries where needed. Depending on the projects
things may be very simple or get very complex, it depends on how we render
things.
</p>
<p>
But in the end, we have several independent projects all providing us with an
X server process - the specific X bits are done in libxserver though. We can
release Xwayland without having to release Xorg or Xvfb.
</p>
<p>
libxserver won't need a lot of releases, the behaviour is largely specified
by the protocol requirements and once you're done implementing it, it'll be
quite a slow-moving project.
</p>
<p>
Ok, now, fast forward to 2021, lose some hindsight, hope, and attitude
and - oh, we have exactly the above structure. Except that it's not spread
across multiple independent repos on github, it's all sitting in the same git
directory: our Xorg, Xwayland, Xvfb, etc. are all sitting in hw/$name, and
libxserver is basically the rest of the repo.
</p>
<p>
A traditional X server release was a tag in that git directory. An
XWayland-only release is basically an rm -rf hw/*-but-not-xwayland followed by a tag, an Xorg-only
release is basically an rm -rf hw/*-but-not-xfree86 [2].
</p>
<p>
In theory, we could've moved all these out into separate projects a while ago
but the benefits are small and no-one has the time for that anyway.
</p>
<p>
So there you have it - you can have Xorg-only or XWayland-only releases
without the world coming to an end.
</p>
<p>
Now, for the "Xorg is dead" claims - it's very likely that the current
release will be the last Xorg release. [3] There is little interest in an X server
that runs on hardware, or rather: there's little interest in the effort
required to push out releases. Povilas did a great job in getting this one out
but again, it's likely this is the last release. [4]
</p>
<p>
Xwayland - very different, it'll hang around for a long time because it's
"just" a protocol translation layer. And of course the interest is there, so
we have volunteers to do the releases.
</p>
<p>
So basically: expecting Xwayland releases, be surprised (but not confused) by
Xorg releases.
</p>
<p>
<small>
[1] Github of course doesn't exist yet because we're in the 80s. Time-travelling is complicated.<br/>
[2] Historical directory name, just accept it.<br/>
[3] Just like the previous release... <br/>
[4] At least until the next volunteer steps ups. Turns out the problem "no-one
wants to work on this" is easily fixed by "me! me! I want to work on this".
A concept that is apparently quite hard to understand in the peanut gallery.<br/>
</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com3tag:blogger.com,1999:blog-6112936277054198647.post-40361470539499050242021-08-31T17:50:00.000+10:002021-08-31T17:50:40.976+10:00libinput and high-resolution wheel scrolling<p>Gut Ding braucht Weile. Almost three years ago, we added <a href="https://who-t.blogspot.com/2018/12/high-resolution-wheel-scrolling-on.html">high-resolution wheel scrolling</a> to the kernel (v5.0). The desktop stack however was first lagging and eventually left behind (except for an update a year ago or so, see <a href="https://who-t.blogspot.com/2020/04/high-resolution-wheel-scrolling-in.html">here</a>). However, I'm happy to announce that thanks to JosΓ© ExpΓ³sito's efforts, we now pushed it across the line. So - in a socially distanced manner and masked up to your eyebrows - gather round children, for it is storytime.
</p>
<h2>Historical History</h2>
<p>
In the beginning, there was the wheel detent. Or rather there were 24 of them, dividing a 360 degree [1] movement of a wheel into a neat set of 15 clicks. libinput exposed those wheel clicks as part of the "pointer axis" namespace and you could get the click count with <i>libinput_event_pointer_get_axis_discrete()</i> (announced <a href="https://who-t.blogspot.com/2015/01/providing-physical-movement-of-wheel.html">here</a>). The degree value is exposed as <i>libinput_event_pointer_get_axis_value()</i>. Other scroll backends (finger-scrolling or button-based scrolling) expose the pixel-precise value via that same function.
</p>
<p>
In a "recent" Microsoft Windows version (Vista!), MS added the ability for wheels to trigger more than 24 clicks per rotation. The MS Windows API now treats one "traditional" wheel click as a value of 120, anything finer-grained will be a fraction thereof. You may have a mouse that triggers quarter-wheel clicks, each sending a value of 30. This makes for smoother scrolling and is supported(-ish) by a lot of mice introduced in the last 10 years [2]. Obviously, three small scrolls are nicer than one large scroll, so the UX is less bad than before.
</p>
<p>
Now it's time for libinput to catch up with Windows Vista! For $reasons, the existing pointer axis API could get changed to accommodate for the high-res values, so a new API was added for scroll events. Read on for the details, you will believe what happens next.
</p>
<h2>Out with the old, in with the new</h2>
<p>
As of libinput 1.19, libinput has three new events: <b>LIBINPUT_EVENT_POINTER_SCROLL_WHEEL</b>, <b>LIBINPUT_EVENT_POINTER_SCROLL_FINGER</b>, and <b>LIBINPUT_EVENT_POINTER_SCROLL_CONTINUOUS</b>. These events reflect, perhaps unsuprisingly, scroll movements of a wheel, a finger or along a continuous axis (e.g. button scrolling). And they <i>replace</i> the old event LIBINPUT_EVENT_POINTER_AXIS. Those familiar with libinput will notice that the new event names encode the <a href="https://who-t.blogspot.com/2015/03/libinput-scroll-sources.html">scroll source</a> in the event name now. This makes them slightly more flexible and saves callers an extra call.
</p>
<p>
In terms of actual API, the new events have two new functions: <b>libinput_event_pointer_get_scroll_value()</b>. For the FINGER and CONTINUOUS events, the value returned is in "pixels" [3]. For the new WHEEL events, the value is in degrees. IOW this is a drop-in replacement for the old <i>libinput_event_pointer_get_axis_value()</i> function. The second call is <b>libinput_event_pointer_get_scroll_value_v120()</b> which, for WHEEL events, returns the 120-based logical units the kernel uses as well. <b>libinput_event_pointer_has_axis()</b> returns true if the given axis has a value, just as before. With those three calls you now get the data for the new events.
</p>
<h2>Backwards compatibility</h2>
<p>
To ensure backwards compatibility, libinput generates both old and new events so the rule for callers is: if you want to support the new events, just ignore the old ones completely. libinput also guarantees new events even on pre-5.0 kernels. This makes the old and new code easy to ifdef out, and once you get past the immediate event handling the code paths are virtually identical.
</p>
<h2>When, oh when?</h2>
<p>
These changes have been merged into the libinput main branch and will be part of libinput 1.19. Which is due to be released over the next month or so, so feel free to work backwards from that for your favourite distribution.
</p>
<p>Having said that, libinput is merely the lowest block in the Jenga tower that is the desktop stack. JosΓ© linked to the various MRs in <a href="https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/652#note_1046230">the upstream libinput MR</a>, so if you're on your seat's edge waiting for e.g. GTK to get this, well, there's an MR for that.
</p>
<p>
<small>
[1] That's degrees of an angle, not Fahrenheit<br/>
[2] As usual, on a significant number of those you'll need to know whatever proprietary protocol the vendor deemed to be important IP. Older MS mice stand out here because they use straight HID.<br/>
[3] libinput doesn't really have a concept of pixels, but it has a normalized pixel that movements are defined as. Most callers take that as real pixels except for the high-resolution displays where it's appropriately scaled. <br/>
</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-40125545580273748972021-08-31T16:29:00.002+10:002021-09-01T13:23:48.560+10:00Flatpak portals - how do they work?<p>
I've been working on portals recently and one of the issues for me was that the documentation just didn't quite hit the sweet spot. At least the bits I found were either too high-level or too implementation-specific. So here's a set of notes on how a portal works, in the hope that this is actually correct.
</p>
<p>
First, Portals are supposed to be a way for sandboxed applications (flatpaks) to trigger functionality they don't have direct access too. The prime example: opening a file without the application having access to $HOME. This is done by the applications talking to portals instead of doing the functionality themselves.
</p>
<p>
There is really only one portal process: <b>/usr/libexec/xdg-desktop-portal</b>, started as a systemd user service. That process owns a DBus bus name (<b>org.freedesktop.portal.Desktop</b>) and an object on that name (<b>/org/freedesktop/portal/desktop</b>). You can see that bus name and object with D-Feet, from DBus' POV there's nothing special about it. What makes it the portal is simply that the application running inside the sandbox can talk to that DBus name and thus call the various methods. Obviously the xdg-desktop-portal needs to run outside the sandbox to do its things.
</p>
<p>
There are multiple portal interfaces, all available on that one object. Those interfaces have names like <b>org.freedesktop.portal.FileChooser</b> (to open/save files). The xdg-desktop-portal implements those interfaces and thus handles any method calls on those interfaces. So where an application is sandboxed, it doesn't implement the functionality itself, it instead calls e.g. the <b>OpenFile()</b> method on the org.freedesktop.portal.FileChooser interface. Then it gets an fd back and can read the content of that file without needing full access to the file system.
</p>
<p>
Some interfaces are fully handled within xdg-desktop-portal. For example, the Camera portal checks a few things internally, pops up a dialog for the user to confirm access to if needed [1] but otherwise there's nothing else involved with this specific method call.
</p>
<p>
Other interfaces have a backend "implementation" DBus interface. For example, the <b>org.freedesktop.portal.FileChooser</b> interface has a <b>org.freedesktop.impl.portal.FileChooser</b> (notice the "impl") counterpart.
xdg-desktop-portal does <b>not</b> implement those impl.portals. xdg-desktop-portal instead routes the DBus calls to the respective "impl.portal". Your sandboxed application calls OpenFile(), xdg-desktop-portal now calls <b>OpenFile()</b> on
<b>org.freedesktop.impl.portal.FileChooser</b>. That interface returns a value, xdg-desktop-portal extracts it and returns it back to the application in respones to the original <b>OpenFile()</b> call.
</p>
<p>
What provides those impl.portals doesn't matter to xdg-desktop-portal, and this is where things are hot-swappable. <strike>GTK and Qt both provide (some of) those impl portals,</strike> There are GTK and Qt-specific portals with <a href="http://github.com/flatpak/xdg-desktop-portal-gtk" target="_blank">xdg-desktop-portal-gtk</a> and<a href="https://github.com/KDE/xdg-desktop-portal-kde" target="_blank"> xdg-desktop-portal-kde </a>but another one is provided by GNOME Shell directly. You can check the files in<b> /usr/share/xdg-desktop-portal/portals/</b> and see which impl portal is provided on which bus name. The reason those impl.portals exist is so they can be native to the desktop environment - regardless what application you're running and with a generic xdg-desktop-portal, you see the native file chooser dialog for your desktop environment.
</p>
<p>
So the full call sequence is:
<ul>
<li>At startup, xdg-desktop-portal parses the /usr/libexec/xdg-desktop-portal/*.portal files to know which impl.portal interface is provided on which bus name</li>
<li>The application calls OpenFile() on the org.freedesktop.portal.FileChooser interface on the object path /org/freedesktop/portal/desktop. It can do so because the bus name this object sits on is not restricted by the sandbox</li>
<li>xdg-desktop-portal receives that call. This is portal with an impl.portal so xdg-desktop-portal calls OpenFile() on the bus name that provides the org.freedesktop.impl.portal.FileChooser interface (as previously established by reading the *.portal files)</li>
<li>Assuming xdg-desktop-portal-gtk provides that portal at the moment, that process now pops up a GTK FileChooser dialog that runs outside the sandbox. User selects a file</li>
<li>xdg-desktop-portal-gtk sends back the fd for the file to the xdg-desktop-portal, and the impl.portal parts are done</li>
<li>xdg-desktop-portal receives that fd and sends it back as reply to the OpenFile() method in the normal portal</li>
<li>The application receives the fd and can read the file now</li>
</ul>
A few details here aren't fully correct, but it's correct enough to understand the sequence - the exact details depend on the method call anyway.
</p>
<p>
Finally: because of DBus restrictions, the various methods in the portal interfaces don't just reply with values. Instead, the xdg-desktop-portal creates a new <b>org.freedesktop.portal.Request</b> object and returns the object path for that. Once that's done the method is complete from DBus' POV. When the actual return value arrives (e.g. the fd), that value is passed via a signal on that Request object, which is then destroyed. This roundabout way is done for purely technical reasons, regular DBus methods would time out while the user picks a file path.
</p>
<p>Anyway. Maybe this helps someone understanding how the portal bits fit together.</p>
<p>
<small>
[1] it does so using another portal but let's ignore that<br/>
[2] not really hot-swappable though. You need to restart xdg-desktop-portal but not your host. So luke-warm-swappable only<br/>
</small>
</p>
<p>
<small>Edit Sep 01: clarify that it's not GTK/Qt providing the portals, but xdg-desktop-portal-gtk and -kde</small>
</p>
Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com3tag:blogger.com,1999:blog-6112936277054198647.post-9184995276592364312021-08-25T15:29:00.001+10:002021-08-25T15:29:29.467+10:00libei - a status update<p>A year ago, I first announced <a href="https://who-t.blogspot.com/2020/08/libei-library-to-support-emulated-input.html"> libei - a library to support emulated input</a>. After an initial spurt of development, it was left mostly untouched until a few weeks ago. Since then, another flurry of changes have been added, including some initial integration into GNOME's mutter. So, let's see what has changed.
</p>
<h2>A Recap</h2>
<p>
First, a short recap of what libei is: it's a transport layer for emulated input events to allow for any application to control the pointer, type, etc. But, unlike the XTEST extension in X, libei allows the compositor to be in control over clients, the devices they can emulate and the input events as well. So it's safer than XTEST but also a lot more flexible. libei already supports touch and smooth scrolling events, something XTest doesn't have or is struggling with.
</p>
<p>Terminology refresher: libei is the client library (used by an application wanting to emulate input), EIS is the Emulated Input Server, i.e. the part that typically runs in the compositor.</p>
<h2>Server-side Devices</h2>
<p> So what has changed recently: first, the whole approach has flipped on its head - now a libei client connects to the EIS implementation and "binds" to the seats the EIS implementation provides. The EIS implementation then provides input devices to the client. In the simplest case, that's just a relative pointer but we have capabilities for absolute pointers, keyboards and touch as well. Plans for the future is to add gestures and tablet support too. Possibly joysticks, but I haven't really thought about that in detail yet.
</p>
<p>
So basically, the initial conversation with an EIS implementation goes like this:
<ul>
<li>Client: Hello, I am $NAME</li>
<li>Server: Hello, I have "seat0" and "seat1"</li>
<li>Client: Bind to "seat0" for pointer, keyboard and touch</li>
<li>Server: Here is a pointer device</li>
<li>Server: Here is a keyboard device</li>
<li>Client: Send relative motion event 10/2 through the pointer device</li>
</ul>
Notice how the touch device is missing? The capabilities the client binds to are just what the client wants, the server doesn't need to actually give the client a device for that capability.
</p>
<p>
One of the design choices for libei is that devices are effectively static. If something changes on the EIS side, the device is removed and a new device is created with the new data. This applies for example to regions and keymaps (see below), so libei clients need to be able to re-create their internal states whenever the screen or the keymap changes.
</p>
<h2>Device Regions</h2>
<p>
Devices can now have <i>regions</i> attached to them, also provided by the EIS implementation. These regions define areas reachable by the device and are required for clients such as <a href="https://github.com/debauchee/barrier">Barrier</a>. On a dual-monitor setup you may have one device with two regions or two devices with one region (representing one monitor), it depends on the EIS implementation. But either way, as libei client you will know that there is an area and you will know how to reach any given pixel on that area. Since the EIS implementation decides the regions, it's possible to have areas that are unreachable by emulated input (though I'm struggling a bit for a real-world use-case).
</p>
<p>
So basically, the conversation with an EIS implementation goes like this:
<ul>
<li>Client: Hello, I am $NAME</li>
<li>Server: Hello, I have "seat0" and "seat1"</li>
<li>Client: Bind to "seat0" for absolute pointer</li>
<li>Server: Here is an abs pointer device with regions 1920x1080@0,0, 1080x1920@1920,0</li>
<li>Server: Here is an abs pointer device with regions 1920x1080@0,0</li>
<li>Server: Here is an abs pointer device with regions 1080x1920@1920,0</li>
<li>Client: Send abs position 100/100 through the second device</li>
</ul>
Notice how we have three absolute devices? A client emulating a tablet that is mapped to a screen could just use the third device. As with everything, the server decides what devices are created and the clients have to figure out what they want to do and how to do it.
</p>
<p>Perhaps unsurprisingly, the use of regions make libei clients windowing-system independent. The <a href="https://github.com/whot/barrier/tree/wip/ei">Barrier EI support WIP</a> no longer has any Wayland-specific code in it. In theory, we could implement EIS in the X server and libei clients would work against that unmodified.</p>
<h2>Keymap handling</h2>
<p>
The keymap handling has been changed so the keymap too is provided by the EIS implementation now, effectively in the same way as the Wayland compositor provides the keymap to Wayland clients. This means a client knows what keycodes to send, it can handle the state to keep track of things, etc. Using Barrier as an example again - if you want to generate an "a", you need to look up the keymap to figure out which keycode generates an A, then you can send that through libei to actually press the key.
</p>
<p>
Admittedly, this is quite messy. XKB (and specifically libxkbcommon) does not make it easy to go from a keysym to a key code. The existing Barrier X code is full of corner-cases with XKB already, I espect those to be necessary for the EI support as well.
</p>
<h2>Scrolling</h2>
<p>Scroll events have four types: pixel-based scrolling, discrete scrolling, and scroll stop/cancel events. The first should be obvious, discrete scrolling is for mouse wheels. It uses the same 120-based API that Windows (and the <a href="http://who-t.blogspot.com/2020/04/high-resolution-wheel-scrolling-in.html">kernel</a>) use, so it's compatible with high-resolution wheel mice. The scroll stop event notifies an EIS implementation that the scroll interaction has stopped (e.g. lifting fingers off) which in turn may start kinetic scrolling - just like the libinput/Wayland scroll stop events. The scroll cancel event notifies the EIS implementation that scrolling <i>really</i> has stopped and no kinetic scrolling should be triggered. There's no equivalent in libinput/Wayland for this yet but it helps to get the hook in place.
</p>
<h2>Emulation "Transactions"</h2>
<p>
This has fairly little functional effect, but interactions with an EIS server are now sandwiched in a start/stop emulating pair. While this doesn't matter for one-shot tools like xdotool, it does matter for things like Barrier which can send the start emulating event when the pointer enters the local window. This again allows the EIS implementation to provide some visual feedback to the user. To correct the example from above, the sequence is actually:
<ul>
<li>...</li>
<li>Server: Here is a pointer device</li>
<li>Client: Start emulating</li>
<li>Client: Send relative motion event 10/2 through the pointer device</li>
<li>Client: Send relative motion event 1/4 through the pointer device</li>
<li>Client: Stop emulating</li>
</ul>
</p>
<h2>Properties</h2>
<p>
Finally, there is now a generic property API, something copied from PipeWire. Properties are simple key/value string pairs and cover those things that aren't in the immediate API. One example here: the portal can set things like "ei.application.appid" to the Flatpak's appid. Properties can be locked down and only libei itself can set properties before the initial connection. This makes them reliable enough for the EIS implementation to make decisions based on their values. Just like with PipeWire, the list of useful properties will grow over time. it's too early to tell what is really needed.
</p>
<h2>Repositories</h2>
<p>
Now, for the actual demo bits: I've added enough support to Barrier, XWayland, Mutter and GNOME Shell that I can control a GNOME on Wayland session through Barrier (note: the controlling host still needs to run X since we don't have the ability to capture input events under Wayland yet). The keymap handling in Barrier is nasty but it's enough to show that it can work.</p>
<p>GNOME Shell has a rudimentary UI, again just to show what works:
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmUvTtMYu9jpiyQXRk7UfJDKdPLOIlZnCHj-lKLcdMtrmF99Qsl0xZvE8_l9to36-89ukCq9XpONE-trxyGCx-44Yd4FkMsAjLrlh-lX0jUmA3DwrgDudt2vjr3CF0SiMfSvH6pqFWdSMa/s283/gnome-shell-eis-status-bar.png" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" width="320" data-original-height="218" data-original-width="283" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmUvTtMYu9jpiyQXRk7UfJDKdPLOIlZnCHj-lKLcdMtrmF99Qsl0xZvE8_l9to36-89ukCq9XpONE-trxyGCx-44Yd4FkMsAjLrlh-lX0jUmA3DwrgDudt2vjr3CF0SiMfSvH6pqFWdSMa/s320/gnome-shell-eis-status-bar.png"/></a></div>
The status icon shows <i>...</i> if libei clients are connected, it changes to <i>!!!</i> while the clients are emulating events. Clients are listed by name and can be disconnected at will. I am not a designer, this is just a PoC to test the hooks.
</p>
<p>Note how xdotool is listed in this screenshot: that tool is unmodified, it's the XWayland libei implementation that allows it to work and show up correctly
</p>
<p>
The various repositories are in the "wip/ei" branch of:
<ul>
<li><a href="https://gitlab.freedesktop.org/whot/xserver/-/commits/wip/ei">XWayland</a></li>
<li><a href="https://github.com/whot/barrier/tree/wip/ei">Barrier</a></li>
<li><a href="https://gitlab.gnome.org/whot/mutter/-/commits/wip/ei">Mutter</a>
<li><a href="https://gitlab.gnome.org/whot/gnome-shell/-/commits/wip/ei">GNOME Shell</a></li>
</ul>
And of course <a href="https://gitlab.freedesktop.org/libinput/libei/">libei</a> itself.
</p>
<p>
Where to go from here? The last weeks were driven by rapid development, so there's plenty of test cases to be written to make sure the new code actually works as intended. That's easy enough. Looking at the Flatpak integration is another big ticket item, once the portal details are sorted all the pieces are (at least theoretically) in place. That aside, improving the integrations into the various systems above is obviously what's needed to get this working OOTB on the various distributions. Right now it's all very much in alpha stage and I could use help with all of those (unless you're happy to wait another year or so...). Do ping me if you're interested to work on any of this.
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-16362071143528558742021-07-28T13:22:00.000+10:002021-07-28T13:22:21.573+10:00It's templates all the way down - part 4<p><a href="https://who-t.blogspot.com/2020/03/its-templates-all-way-down.html" target="_blank">Part 1</a>, <a href="https://who-t.blogspot.com/2020/06/its-templates-all-way-down-part-2.html" target="_blank">Part 2</a>, <a href="https://who-t.blogspot.com/2020/12/its-templates-all-way-down-part-3.html" target="_blank">Part 3</a></p>
<p>
After getting thouroughly nerd-sniped a few weeks back, we now have FreeBSD support through qemu in the <a href="https://freedesktop.pages.freedesktop.org/ci-templates/" target="_blank">freedesktop.org ci-templates</a>. This is possible through the qemu image generation we have had for quite a while now. So let's see how we can easily add a FreeBSD VM (or other distributions) to our gitlab CI pipeline:
<pre style="background-color: #ddddff">
.freebsd:
variables:
FDO_DISTRIBUTION_VERSION: '13.0'
FDO_DISTRIBUTION_TAG: 'freebsd.0' # some value for humans to read
build-image:
extends:
- .freebsd
- .fdo.qemu-build@freebsd
variables:
FDO_DISTRIBUTION_PACKAGES: "curl wget"
</pre>
Now, so far this may all seem quite familiar. And indeed, this is almost exactly the same process as for normal containers (see <a href="https://who-t.blogspot.com/2020/03/its-templates-all-way-down.html" target="_blank">Part 1</a>), the only difference is the <b>.fdo.qemu-build</b> base template. Using this template means we build an image babushka: our desired BSD image is actual a QEMU RAW image sitting inside another generic container image. That latter image only exists to start the QEMU image and set up the environment if need be, you don't need to care what distribution it runs out (Fedora for now).
</p>
<p>
Because of the nesting, we need to handle this accordingly in our <b>script:</b> tag for the actual test job - we need to start the image and make sure our jobs are actually built within. The templates set up an ssh alias "vm" for this and the <b>vmctl</b> script helps to do things on the vm:
<pre style="background-color: #ddddff">
test-build:
extends:
- .freebsd
- .fdo.distribution-image@freebsd
script:
# start our QEMU image
- /app/vmctl start
# copy our current working directory to the VM
# (this is a yaml multiline command to work around the colon)
- |
scp -r $PWD vm:
# Run the build commands on the VM and if they succeed, create a .success file
- /app/vmctl exec "cd $CI_PROJECT_NAME; meson builddir; ninja -C builddir" && touch .success || true
# Copy results back to our run container so we can include them in artifacts:
- |
scp -r vm:$CI_PROJECT_NAME/builddir .
# kill the VM
- /app/vmctl stop
# Now that we have cleaned up: if our build job before
# failed, exit with an error
- [[ -e .success ]] || exit 1
</pre>
Now, there's a bit to unpack but with the comments above it should be fairly obvious what is happening. We start the VM, copy our working directory over and then run a command on the VM before cleaning up. The reason we use <i>touch .success</i> is simple: it allows us to copy things out and clean up before actually failing the job.</p>
<p>
Obviously, if you want to build any other distribution you just swap the freebsd out for fedora or whatever - the process is the same. libinput has been using fedora qemu images for ages now.
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-57303182347181437082021-07-27T15:58:00.001+10:002021-07-27T15:58:07.722+10:00libinput and hold gestures<p>
Thanks to the work done by <https://twitter.com/jose__exposito>Josè Expòsito</a>, libinput 1.19 will ship with a new type of gesture: <a href="https://wayland.freedesktop.org/libinput/doc/latest/gestures.html#hold-gestures" target="_blank">Hold Gestures</a>. So far libinput supported swipe (moving multiple fingers in the same direction) and pinch (moving fingers towards each other or away from each other). These gestures are well-known, commonly used, and familiar to most users. For example, GNOME 40 recently has increased its use of touchpad gestures to switch between workspaces, etc. Swipe and pinch gestures require movement, it was not possible (for callers) to detect fingers on the touchpad that don't move.
</p>
<p>
This gap is now filled by Hold gestures. These are triggered when a user puts fingers down on the touchpad, <i>without</i> moving the fingers. This allows for some new interactions and we had two specific ones in mind: hold-to-click, a common interaction on older touchscreen interfaces where holding a finger in place eventually triggers the context menu. On a touchpad, a three-finger hold could zoom in, or do dictionary lookups, or kill a kitten. Whatever matches your user interface most, I guess.
</p>
<p>The second interaction was the ability to stop kinetic scrolling. <a href="https://wayland.freedesktop.org/libinput/doc/latest/faqs.html#kinetic-scrolling-does-not-work" target="_blank">libinput does not actually provide kinetic scrolling</a>, it merely provides the information needed in the client to do it there: specifically, it tells the caller when a finger was lifted off a touchpad at the end of a scroll movement. It's up to the caller (usually: the toolkit) to implement the kinetic scrolling effects. One missing piece was that while libinput provided information about lifting the fingers, it didn't provide information about putting fingers down again later - a common way to stop scrolling on other systems.
</p>
<p>
Hold gestures are intended to address this: a hold gesture triggered after a flick with two fingers can now be used by callers (read: toolkits) to stop scrolling.
</p>
<p>
Now, one important thing about hold gestures is that they will generate a lot of false positives, so be careful how you implement them. The vast majority of interactions with the touchpad will trigger some movement - once that movement hits a certain threshold the hold gesture will be cancelled and libinput sends out the movement events. Those events may be tiny (depending on touchpad sensitivity) so getting the balance right for the aforementioned hold-to-click gesture is up to the caller.
</p>
<p>
As usual, the required bits to get hold gestures into the wayland protocol are either in the works, mid-flight or merge-ready so expect this to hit the various repositories over the medium-term future.
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-25295030769584429592021-02-18T11:57:00.003+10:002021-02-18T11:57:21.821+10:00A pre-supplied "custom" keyboard layout for X11<p>Last year I wrote about how to create a <a href="https://who-t.blogspot.com/2020/09/user-specific-xkb-configuration-putting.html">user-specific XKB layout</a>, followed by a post explaining that this <a href="https://who-t.blogspot.com/2020/09/no-user-specific-xkb-configuration-in-x.html">won't work in X</a>. But there's a pandemic going on, which is presumably the only reason people haven't all switched to Wayland yet. So it was time to figure out a workaround for those still running X.</p>
<p><a href="https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/merge_requests/189">This Merge Request</a> (scheduled for xkeyboard-config 2.33) adds a <i>"custom"</i> layout to the <b>evdev.xml </b>and <b>base.xml </b>files. These XML files are parsed by the various GUI tools to display the selection of available layouts. An entry in there will thus show up in the GUI tool.</p>
<p>Our rulesets, i.e. the files that convert a layout/variant configuration into the components to actually load already have wildcard matching [1]. So the <i>custom</i> layout will resolve to the <i>symbols/custom</i> file in your XKB data dir - usually <i>/usr/share/X11/xkb/symbols/custom</i>.</p><p>This file is <b>not</b> provided by xkeyboard-config. It can be created by the user though and whatever configuration is in there will be the "custom" keyboard layout. Because xkeyboard-config does not supply this file, it will not get overwritten on update.</p>
<p>From XKB's POV it is just another layout and it thus uses the same syntax. For example, to override the +/* key on the German keyboard layout with a key that produces a/b/c/d on the various Shift/Alt combinations, use this:
<pre style="background-color: #ddddff">
default
xkb_symbols "basic" {
include "de(basic)"
key <AD12> { [ a, b, c, d ] };
};
</pre>
This example includes the "basic" section from the symbols/de file (i.e. the default German layout), then overrides the 12th alphanumeric key from left in the 4th row from bottom (D) with the given symbols. I'll leave it up to the reader to come up with a less useful example.
</p>
<p>There are a few drawbacks:
<ul style="text-align: left;">
<li>If the file is missing and the user selects the custom layout, the results are... undefined. For run-time configuration like GNOME it doesn't really matter - the layout compilation fails and you end up with the one the device already had (i.e. the default one built into X, usually the US layout).</li>
<li>If the file is missing and the custom layout is selected in the xorg.conf, the results are... undefined. I tested it and ended up with the US layout but that seems more by accident than design. My recommendation is to not do that.</li>
<li>No variants are available in the XML files, so the only accessible section is the one marked<i> default</i>.</li>
<li>If a commandline tool uses a variant of custom, the GUI will not reflect this. If the GUI goes boom, that's a bug in the GUI.</li>
</ul>
<p>So overall, it's a hack[2]. But it's a hack that fixes real user issues and given we're talking about X, I doubt anyone notices another hack anyway. </p>
<p><small>
[1] If you don't care about GUIs, <i>setxkbmap -layout custom -variant foobar</i> has been possible for years.<br />
[2] Sticking with the UNIX principle, it's a hack that fixes the issue at hand, is badly integrated, and weird to configure.<br/>
</small></p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-30968670884188437342021-01-22T10:58:00.000+10:002021-01-22T10:58:04.483+10:00Auto-updating XKB for new kernel keycodes<p>Your XKB keymap contains two important parts. One is the mapping from the hardware scancode to some internal representation, for example:<br /></p><p></p><pre> <AB10> = 61; </pre><p></p><p>Which basically means <b>A</b>lphanumeric key in row <b>B</b> (from bottom), <b>10</b>th key from the left. In other words: the /? key on a US keyboard.<br /></p><p></p><p>The second part is mapping that internal representation to a keysym, for example:</p><pre> key <AB10> { [ slash, question ] }; </pre><p>This is the actual layout mapping - once in place this key really produces a slash or question mark (on level2, i.e. when Shift is down).</p><p>This two-part approach exists so either part can be swapped without affecting the other. Swap the second part to an exclamation mark and paragraph symbol and you have the French version of this key, swap it to dash/underscore and you have the German version of the key - all without having to change the keycode.<br /></p><p>Back in the golden days of everyone-does-what-they-feel-like, keyboard manufacturers (presumably happily so) changed the key codes and we needed model-specific keycodes in XKB. The XkbModel configuration is a leftover from these trying times.<br /></p><p>The Linux kernel's evdev API has largely done away with this. It provides a standardised set of keycodes, defined in<i> linux/input-event-codes.h</i>, and ensures, with the help of udev [0], that all keyboards actually conform to that. An evdev XKB keycode is a simple "kernel keycode + 8" [1] and that applies to all keyboards. On top of that, the kernel uses semantic definitions for the keys as they'd be in the US layout. KEY_Q is the key that would, behold!, produce a Q. Or an A in the French layout because they just have to be different, don't they? Either way, with evdev the Xkb Model configuration largely points to nothing and only wastes a few cycles with string parsing.</p><p>The second part, the keysym mapping, uses two approaches. One is to use a named #define like the "slash", "question" outlined above (see <i>X11/keysymdef.h</i> for the defines). The other is to use unicode directly like this example from the Devangari layout:<br /></p><pre> key <AB10> { [ U092f, U095f, slash, question ] };</pre><p>As you can see, mix and match is available too. Using Unicode code points of course makes the layouts less immediately readable but on the other hand we don't need to #define the whole of Unicode. So from a maintenance perspective it's a win.<br /></p><p>However, there's a third type of key that we care about: functional keys. Those are the multimedia (historically: "internet") keys that most devices have these days. Volume up, touchpad on/off, cycle display connectors, etc. Those keys are special in that they don't have a Unicode representation and they are always mapped to the same fixed functionality. Even Dvorak users want their volume keys to do what it says on the key.<br /></p><p>Because they have no Unicode code points, those keys are defined, historically, in XF86keysyms.h:</p><pre> #define XF86XK_MonBrightnessUp 0x1008FF02 /* Monitor/panel brightness */<br /></pre><p></p><p>And mapping a key like this looks like this [2]:<br /></p><pre> key <I21> { [ XF86Calculator ] };</pre><p>The only drawback: every key needs to be added manually. This has been done for some, but not for others. And some keys were added with different names than what the kernel uses [3].<br /></p><p>So we're in this weird situation where we have a flexible keymap system but the kernel already tells us what a key does anyway and we don't want to change that. Virtually all keys added in the last decade or so falls into that group of keys, but to actually make use of them requires a #define in xorgproto and an update to the keycodes and symbols in xkeyboard-config. That again introduces discrepancies and we end up in the situation where we're at right now: some keys don't work until someone files a bug, and then the users still need to wait for several components to be released and those releases trickle into the distributions.</p><p></p><p>10 years ago would've been a good time to make this more efficient. The situation wasn't that urgent then,
most of the kernel keycodes added are >255 which means
they cannot be used in X anyway. [4] The second best time to do it is now. What we need is basically a pass-through from kernel code to symbol and that's currently sitting in various MRs:</p><p>- <a href="https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/merge_requests/177">xkeyboard-config</a> can generate the keycodes/evdev file based on the list of kernel keycodes, so all kernel keycodes are mapped to internal representations by default</p><p>- <a href="https://gitlab.freedesktop.org/xorg/proto/xorgproto/-/merge_requests/23" target="_blank">xorgproto</a> has reserved a range within the XF86 keysym reserved range for pass-through mappings, i.e. any <i>KEY_FOO</i> define from the kernel is mapped to <i>XF86XK_Foo</i> with a specific value [5]. The #define format is fixed so it can be parsed.</p><p>- <a href="https://gitlab.freedesktop.org/xkeyboard-config/xkeyboard-config/-/merge_requests/172">xkeyboard-config</a> parses theses XF86 keysyms and sets up a keysym mapping in the default keymap.</p><p>This is semi-automatic, i.e. there are helper scripts that detect changes and notify us, hooked into the CI, but the actual work must be done manually. These keysyms immediately become set-in-stone API so we don't want some unsupervised script to go wild on them.</p><p>There's a huge backlog of keys to be added (dating to kernels pre-v3.18) and I'll go through them one-by-one over the next weeks to make sure they're correct. But eventually they'll be done and we have a full keymap for all kernel keys to be immediately available in the XKB layout.<br /></p><p>The last part of all of this is a calendar reminder for me to do this after every new kernel release. Let's hope this crucial part isn't the first to fail.<br /></p><p><span style="font-size: x-small;">[0] 60-keyboard.hwdb has a mere ~1800 lines!<br />[1] Historical reasons, you don't want to know. *jedi wave*<br />[2] the XK_ part of the key name is dropped, implementation detail.<br />[3] This can also happen when a kernel define is renamed/aliased but we cannot easily do so for this header.<br />[4] X has an 8 bit keycode limit and that won't change until someone develops XKB2 with support for 32-bit keycodes, i.e. never.</span><br /><span style="font-size: x-small;"><span style="font-size: x-small;">[5] The actual value is an implementation detail and no client must care<br /></span></span></p><p><br /></p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com0tag:blogger.com,1999:blog-6112936277054198647.post-25309907921256336252021-01-13T21:28:00.001+10:002021-01-13T21:28:02.589+10:00Parsing HID Unit Items<p>
This post explains how to parse the HID <b>Unit</b> Global Item as explained by the <a href="https://www.usb.org/sites/default/files/hid1_11.pdf" target="_blank">HID Specification</a>, page 37. The table there is quite confusing and it took me a while to fully understand it (Benjamin Tissoires was really the one who cracked it). I couldn't find any better explanation online which means either I'm incredibly dense and everyone's figured it out or no-one has posted a better explanation. On the off-chance it's the latter [1], here are the instructions on how to parse this item.
</p>
<p>
We know a <a href="https://who-t.blogspot.com/2018/12/understanding-hid-report-descriptors.html" target="_blank">HID Report Descriptor</a> consists of a number of items that describe the content of each HID Report (read: an event from a device). These Items include things like Logical Minimum/Maximum for axis ranges, etc. A HID Unit item specifies the physical unit to apply. For example, a Report Descriptor may specify that X and Y axes are in mm which can be quite useful for all the obvious reasons.
</p>
<p>
Like most HID items, a HID Unit Item consists of a one-byte item tag and 1, 2 or 4 byte payload. The <b>Unit</b> item in the Report Descriptor itself has the binary value <b>0110 01nn</b> where the <b>nn</b> is either 1, 2, or 3 indicating 1, 2 or 4 bytes of payload, respectively. That's standard HID.
</p>
<p>
The payload is divided into nibbles (4-bit units) and goes from LSB to MSB. The lowest-order 4 bits (<i>first byte & 0xf</i>) define the unit <b>System</b> to apply: one of <b>SI Linear</b>, <b>SI Rotation</b>, <b>English Linear</b> or <b>English Rotation</b> (well, or <b>None/Reserved</b>). The rest of the nibbles are in this order: "length", "mass", "time", "temperature", "current", "luminous intensity".
In something resembling code this means:
<pre style="background-color: #ddddff">
system = value & 0xf
length_exponent = (value & 0xf0) >> 4
mass_exponent = (value & 0xf00) >> 8
time_exponent = (value & 0xf000) >> 12
...
</pre>
The <b>System</b> defines which unit is used for length (e.g. SILinear means length is in cm). The actual <i>value</i> of each nibble is the <i>exponent</i> for the unit in use [2]. In something resembling code:
<pre style="background-color: #ddddff">
switch (system)
case SILinear:
print("length is in cm^{length_exponent}");
break;
case SIRotation:
print("length is in rad^{length_exponent}");
break;
case EnglishLinear:
print("length is in in^{length_exponent}");
break;
case EnglishRotation:
print("length is in deg^{length_exponent}");
break;
case None:
case Reserved"
print("boo!");
break;
</pre>
</p>
<p>
For example, the value <b>0x321</b> means "SI Linear" (0x1) so the remaining nibbles represent, in ascending nibble order: Centimeters, Grams, Seconds, Kelvin, Ampere, Candela. The length nibble has a value of 0x2 so it's square cm, the mass nibble has a value of 0x3 so it is cubic grams (well, it's just an example, so...). This means that any report containing this item comes in cmΒ²gΒ³. As a more realistic example: 0xF011 would be cm/s.
</p>
<p>
If we changed the lowest nibble to English Rotation (0x4), i.e. our value is now <b>0x324</b>, the units represent: Degrees, Slug, Seconds, F, Ampere, Candela [3]. The length nibble 0x2 means square degrees, the mass nibble is cubic slugs. As a more realistic example, 0xF014 would be degrees/s.
</p>
<p>
Any nibble with value 0 means the unit isn't in use, so the example from the spec with value <b>0x00F0D121</b> is SI linear, units cmΒ² g sβ»Β³ Aβ»ΒΉ, which is... Voltage! Of course you knew that and totally didn't have to double-check with wikipedia.
</p>
<p>
Because bits are expensive and the base units are of course either too big or too small or otherwise not quite right, HID also provides a <b>Unit Exponent</b> item. The <b>Unit Exponent</b> item (a separate item to <b>Unit</b> in the Report Descriptor) then describes the exponent to be applied to the actual value in the report. For example, a <b>Unit Eponent</b> of -3 means 10β»Β³ to be applied to the value. If the report descriptor specifies an item of <b>Unit</b> 0x00F0D121 (i.e. V) and <b>Unit Exponent</b> -3, the value of this item is mV (milliVolt), <b>Unit Exponent</b> of 3 would be kV (kiloVolt).
</p>
<p>
Now, in hindsight all this is pretty obvious and maybe even sensible. It'd have been nice if the spec would've explained it a bit clearer but then I would have nothing to write about, so I guess overall I call it a draw.
</p>
<p>
<small>[1] This whole adventure was started because there's a touchpad out there that measures touch pressure in radians, so at least one other person out there struggled with the docs...<br>
[2] The nibble value is twos complement (i.e. it's a signed 4-bit integer). Values 0x1-0x7 are exponents 1 to 7, values 0x8-0xf are exponents -8 to -1.<br>
[3] English Linear should've trolled everyone and use Centimetres instead of Centimeters in SI Linear.<br>
</small>
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com2tag:blogger.com,1999:blog-6112936277054198647.post-60887240593808449502020-12-04T14:00:00.000+10:002020-12-04T14:00:03.665+10:00It's templates all the way down - part 3<p>In <a href="https://who-t.blogspot.com/2020/03/its-templates-all-way-down.html" target="_blank">Part 1</a> I've shown you how to create your own distribution image using the freedesktop.org CI templates. In <a href="https://who-t.blogspot.com/2020/06/its-templates-all-way-down-part-2.html" target="_blank">Part 2</a>, I've shown you how to truly build nested images. In this part, I'll talk about the <a href="https://freedesktop.pages.freedesktop.org/ci-templates/ci-fairy.html" target="_blank"><b>ci-fairy</b> tool</a> that is part of the same repository of ci-templates.</p>
<p>
When you're building a CI pipeline, there are some tasks that most projects need in some way or another. The <b>ci-fairy</b> tool is a grab-bag of solutions for these. Some of those solutions are for a pipeline itself, others are for running locally. So let's go through the various commands available.
</p>
<h2>Using ci-fairy in a pipeline</h2>
<p>It's as simple as including the template in your <b>.gitlab-ci.yml</b> file.
<pre style="background-color: #ddddff">
include:
- 'https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/master/templates/ci-fairy.yml'
</pre>
Of course, if you want to track a specific sha instead of following master, just sub that sha there. freedesktop.org projects can include ci-fairy like this:
<pre style="background-color: #ddddff">
include:
- project: 'freedesktop/ci-templates'
ref: master
file: '/templates/ci-fairy.yml'
</pre>
Once that's done, you have access to a <b>.fdo.ci-fairy</b> job template that you can <b>extends</b> from. This will download an <a href="https://quay.io/repository/freedesktop.org/ci-templates?tab=tags" target="_blank">image from quay.io</a> that is capable of git, python, bash and obviously ci-fairy. This image is a fixed one and referenced by a unique sha so even if where we keep working on ci-fairy upstream you should never see regression, updating requires you to explicitly update the sha of the included ci-fairy template. Obviously, if you're using master like above you'll always get the latest.
</p>
<p>
Due to how the ci-templates work, it's good to set the <b>FDO_UPSTREAM_REPO</b> variable with the upstream project name. This means ci-fairy will be able to find the equivalent origin/master branch, where that's not available in the merge request. Note, this is not your personal fork but the upstream one, e.g. "freedesktop/ci-templates" if you are working on the ci-templates itself.
</p>
<h3>Checking commit messages</h3>
<p>
ci-fairy has a command to check commits for a few basic expectations in commit messages. This currently includes things like enforcing a 80 char subject line length, that there is an empty line after the subject line, that no fixup or squash commits are in the history, etc. If you have complex requirements you need to write your own but for most projects this job ensures that there are no obvious errors in the git commit log:
<pre style="background-color: #ddddff">
check-commit:
extends:
- .fdo.ci-fairy
script:
- ci-fairy check-commits --signed-off-by
except:
- master@upstream/project
</pre>
Since you don't ever want this to fail on an already merged commit, exclude this job the master branch of the upstream project - the MRs should've caught this already anyway.
</p>
<h3>Checking merge requests</h3>
<p>
To rebase a contributors merge request, the contributor must tick the checkbox to <a href="https://docs.gitlab.com/ee/user/project/merge_requests/allow_collaboration.html" target="_blank">Allow commits from members who can merge to the target branch</a>. The default value is off which is frustrating (<a href="https://gitlab.com/gitlab-org/gitlab/-/issues/23308" target="_blank">gitlab is working on it though</a>) and causes unnecessary delays in processing merge requests. ci-fairy has command to check for this value on an MR and fail - contributors ideally pay attention to the pipeline and fix this accordingly.
<pre style="background-color: #ddddff">
check-merge-request:
extends:
- .fdo.ci-fairy
script:
- ci-fairy check-merge-request --require-allow-collaboration
allow_failure: true
</pre>
As a tip: run this job towards the end of the pipeline to give collaborators a chance to file an MR before this job fails.
</p>
<h2>Using ci-fairy locally</h2>
<p>
The two examples above are the most useful ones for CI pipelines, but ci-fairy also has some useful local commands. For that you'll have to install it, but that's as simple as
<pre style="background-color: #ddddff">
$ pip3 install git+http://gitlab.freedesktop.org/freedesktop/ci-templates
</pre>
A big focus on ci-fairy for local commands is that it should, usually, be able to work without any specific configuration if you run it in the repository itself.
</p>
<h3>Linting</h3>
<p>
Just hacked on the CI config?
<pre style="background-color: #ddddff">
$ ci-fairy lint
</pre>
and done, you get the same error back that the online linter for your project would return.
</p>
<h3>Pipeline checks</h3>
<p>
Just pushed to the repo?
<pre style="background-color: #ddddff">
$ ci-fairy wait-for-pipeline
Pipeline https://gitlab.freedesktop.org/username/project/-/pipelines/238586
status: success | 7/7 | created: 0 | pending: 0 | running: 0 | failed: 0 | success: 7 ....
</pre>
The command is self-explanatory, I think.
</p>
<h2>Summary</h2>
<p>
There are a few other parts to ci-fairy including templating and even minio handling. I recommend looking at e.g. the <a href="https://gitlab.freedesktop.org/libinput/libinput/-/blob/master/.gitlab-ci.yml" target="_blank">libinput CI pipeline</a> which uses much of ci-fairy's functionality. And the <a href="about:invalid#zSoyz" target="_blank">online documentation for ci-fairy</a>, who knows, there may be something useful in there for you.
</p>
<p>
The useful contribution of ci-fairy is primarily that it tries to detect the settings for each project automatically, regardless of whether it's run inside a MR pipeline or just as part of a normal pipeline. So the same commands will work without custom configuration on a per-project basis. And for many things it works without API tokens, so the setup costs are just the pip install.
</p>
<p>
If you have recurring jobs, <a href="https://gitlab.freedesktop.org/whot/ci-templates/issues" target="_blank">let us know</a>, we're always looking to add more useful functionality to this little tool.
</p>Peter Huttererhttp://www.blogger.com/profile/17204066043271384535noreply@blogger.com1