Brutkey

blinry
@blinry@chaos.social

In Firefox, it seems like all mouse events are mashed together, and are seen as coming from the same device.

That means that both cursors can click – if the other one "holds still". Otherwise, I guess Firefox is very confused by a click on a link while the mouse is not in it!
πŸ˜†πŸ˜†

Selections feel strange – the last-moving cursor will determine the selection.

Also notice how, if one cursor hovers a link,
both turn into hand icons!


blinry
@blinry@chaos.social

Of course, we had to try a drawing application next!

Here's
@tldraw@mas.to (in Firefox)! rainbow_heart_eyes

Collaborative drawing at it's best!
πŸ’šπŸ’š

blinry
@blinry@chaos.social

Finally, I tried attaching an additional keyboard and assigned them to a different "seat"!

That worked really well! In Weston, each "seat" has its own keyboard focus, so you can actually work side-by-side with two mice + two keyboards independently!

Also!!! The two seats have their own (independent) clipboards!!!! Whatttt!
🀯🀯

I totally didn't expect this. But multi-seat as a concept seems deeply integrated into libinput +
#Wayland! Now it's up to GUI toolkits and compositors to support it!

blinry
@blinry@chaos.social

Tried it again, and the independent clipboards still seem a bit glitchy after all… :(

An issue asking for proper support in GTK was closed five years ago, for example…
https://gitlab.gnome.org/GNOME/gtk/-/issues/1574

blinry
@blinry@chaos.social

Side note: I think more window managers should support this feature!

😈😈 Sat 🐱🐱
@tfed@infosec.exchange

@blinry@chaos.social also Right Mouse Click + Mouse Scrolling should control the Opacity to fade the windows or control the windows size shrug_akko

but i want opacity
blob_grinning_sweat

blinry
@blinry@chaos.social

Very useful when your very long line of code doesn't fit on your screen, for example! :P

(Shout-out to
@xssfox@cloudisland.nz, who first did this on X.org! https://sprocketfox.io/xssfox/2021/12/02/xrandr/)

blinry
@blinry@chaos.social

Okay, here's how to set this up!

You need to create a udev rule for the "second" input device that sets ENV{WL_SEAT} to a string other than "default", and then start Weston from a virtual console. (At least, starting it from another Wayland session didn't work for me.) That's it!

The WL_SEAT property is what Wayland refers to as a "logical seat". Assign the same seat name to a mouse and a keyboard to make them work together! The default seat is "default".

Detailed steps:

blinry
@blinry@chaos.social

Okay, here's how to set this up!

You need to create a udev rule for the "second" input device that sets ENV{WL_SEAT} to a string other than "default", and then start Weston from a virtual console. (At least, starting it from another Wayland session didn't work for me.) That's it!

The WL_SEAT property is what Wayland refers to as a "logical seat". Assign the same seat name to a mouse and a keyboard to make them work together! The default seat is "default".

Detailed steps:

blinry
@blinry@chaos.social

1. Use sudo libinput list-devices to find the device file (like "/dev/input/event12")
2. Use
udevadm info -a /dev/input/event12 to find the parent device with a catchy ATTRS{name}.
3. Create a file /run/udev/rules.d/00-multiseat.rules like this:

ATTRS{name}=="Name of your mouse" ENV{WL_SEAT}="second"

(Note: This is a single line)

4. Run
sudo udevadm trigger to apply the new rules.

You can check again with
sudo libinput list-devices. The device's "Seat" should now say "seat0, second"!

blinry
@blinry@chaos.social

1. Use sudo libinput list-devices to find the device file (like "/dev/input/event12")
2. Use
udevadm info -a /dev/input/event12 to find the parent device with a catchy ATTRS{name}.
3. Create a file /run/udev/rules.d/00-multiseat.rules like this:

ATTRS{name}=="Name of your mouse" ENV{WL_SEAT}="second"

(Note: This is a single line)

4. Run
sudo udevadm trigger to apply the new rules.

You can check again with
sudo libinput list-devices. The device's "Seat" should now say "seat0, second"!

blinry
@blinry@chaos.social

You could try this script (requires zenity & possibly more tools? Please read before running!) https://github.com/n3rdopolis/rebeccablackos/blob/master/rebeccablackos_files/usr/bin/configureseats

(Doesn't work on
#NixOS, where /etc/udev is read-only. πŸ’€πŸ’€)

I'd love to have a little command line helper tool to help set this up, for an arbitrary number of mice! :D

blinry
@blinry@chaos.social

You could try this script (requires zenity & possibly more tools? Please read before running!) https://github.com/n3rdopolis/rebeccablackos/blob/master/rebeccablackos_files/usr/bin/configureseats

(Doesn't work on
#NixOS, where /etc/udev is read-only. πŸ’€πŸ’€)

I'd love to have a little command line helper tool to help set this up, for an arbitrary number of mice! :D

blinry
@blinry@chaos.social

This was a fun afternoon! Thanks for following along.

Let me know which other programs I should try with multiple mice! :D

blinry
@blinry@chaos.social

This was a fun afternoon! Thanks for following along.

Let me know which other programs I should try with multiple mice! :D

blinry
@blinry@chaos.social

Looks like SDL (the multimedia library) very recently merged support for multi-seat input! https://www.phoronix.com/news/SDL-Merges-Wayland-Multi-Seat

So… many-mouse games should be possible on Wayland? :3

blinry
@blinry@chaos.social

Looks like SDL (the multimedia library) very recently merged support for multi-seat input! https://www.phoronix.com/news/SDL-Merges-Wayland-Multi-Seat

So… many-mouse games should be possible on Wayland? :3

blinry
@blinry@chaos.social

Still fascinated by the idea of applications with multi-mouse input (see above thread)…

So I thought I'd give it a try! The first step was to hack my Wayland compositor
#niri to track multiple mouse positions, and forward them to client applications. When receiving an event from libinput, you can tell which device it originated from!

Never really done any Wayland programming, so I'm pretty excited that this works! Also, niri is written in Rust, so it was quite a pleasant experience!

blinry
@blinry@chaos.social

Still fascinated by the idea of applications with multi-mouse input (see above thread)…

So I thought I'd give it a try! The first step was to hack my Wayland compositor
#niri to track multiple mouse positions, and forward them to client applications. When receiving an event from libinput, you can tell which device it originated from!

Never really done any Wayland programming, so I'm pretty excited that this works! Also, niri is written in Rust, so it was quite a pleasant experience!

blinry
@blinry@chaos.social

Next, I tried to write some multi-mouse client applications!

My first attempt was a Rust program written using the smithay_client_toolkit. Events callbacks come with a reference to the "pointer" that caused it. In the logs to the left, you can see that I can in fact differentiate the two mouse devices there. Very neat, and a perfectly fine proof of concept.

But this library is super low-level. I wanted something more convenient for actually trying to write some fun demos.

blinry
@blinry@chaos.social

Next, I tried to write some multi-mouse client applications!

My first attempt was a Rust program written using the smithay_client_toolkit. Events callbacks come with a reference to the "pointer" that caused it. In the logs to the left, you can see that I can in fact differentiate the two mouse devices there. Very neat, and a perfectly fine proof of concept.

But this library is super low-level. I wanted something more convenient for actually trying to write some fun demos.

blinry
@blinry@chaos.social

Next, I thought: Oh I know, I'll write a GTK application! So I fumbled my way through setting one up in Rust, and on adding a custom "widget" that can receive motion events.

Problem is: I know next to nothing about GTK. Do I really need to do:

let wayland_seat: WaylandSeat = seat.downcast::<WaylandSeat>().unwrap();
let wl_seat_ptr = gdk_wayland_seat_get_wl_seat(wayland_seat.to_glib_none().0);

to differentiate the event's seat? At least, this worked as well! But it would
really slow me down.

blinry
@blinry@chaos.social

Next, I thought: Oh I know, I'll write a GTK application! So I fumbled my way through setting one up in Rust, and on adding a custom "widget" that can receive motion events.

Problem is: I know next to nothing about GTK. Do I really need to do:

let wayland_seat: WaylandSeat = seat.downcast::<WaylandSeat>().unwrap();
let wl_seat_ptr = gdk_wayland_seat_get_wl_seat(wayland_seat.to_glib_none().0);

to differentiate the event's seat? At least, this worked as well! But it would
really slow me down.

blinry
@blinry@chaos.social

I always had a good time writing games in the LΓ–VE engine! <3 So I forked LΓ–VE, and found an easy way to return the mouse device ID when users receive a "mousemoved" callback in Lua!

But… the ID was always 0 – for both devices! :(

LΓ–VE is built on top of the graphics library SDL, which I knew had introduced some multi-seat features on Wayland recently. So maybe SDL was the culprit?

blinry
@blinry@chaos.social

I always had a good time writing games in the LΓ–VE engine! <3 So I forked LΓ–VE, and found an easy way to return the mouse device ID when users receive a "mousemoved" callback in Lua!

But… the ID was always 0 – for both devices! :(

LΓ–VE is built on top of the graphics library SDL, which I knew had introduced some multi-seat features on Wayland recently. So maybe SDL was the culprit?

blinry
@blinry@chaos.social

So I tried it in pure #SDL3! I thought I'd never have to code something in C again, but here we are.

And indeed: The device IDs are always 0 there, as well – making it impossible to differentiate my two mice.

I haven't figured out why yet; might need some deep-dive into the SDL code to find out? Or maybe I'm not passing the events in properly from "above" (my hacked niri compositor). But given that the other approaches worked, it's maybe something in the SDL pipeline…

blinry
@blinry@chaos.social

So I tried it in pure #SDL3! I thought I'd never have to code something in C again, but here we are.

And indeed: The device IDs are always 0 there, as well – making it impossible to differentiate my two mice.

I haven't figured out why yet; might need some deep-dive into the SDL code to find out? Or maybe I'm not passing the events in properly from "above" (my hacked niri compositor). But given that the other approaches worked, it's maybe something in the SDL pipeline…

blinry
@blinry@chaos.social

I figured it out! Mouse IDs for events are only available in "relative mode" for some reason; which you can enable using SDL_SetWindowRelativeMouseMode(true).

Using love.mouse.setRelativeMode(true), it also kinda works in LΓ–VE! \o/ Still have to "constrain" the other mouse in the window.

But this should be good enough to have some fun with this tomorrow! :3

blinry
@blinry@chaos.social

I figured it out! Mouse IDs for events are only available in "relative mode" for some reason; which you can enable using SDL_SetWindowRelativeMouseMode(true).

Using love.mouse.setRelativeMode(true), it also kinda works in LΓ–VE! \o/ Still have to "constrain" the other mouse in the window.

But this should be good enough to have some fun with this tomorrow! :3