Merge branch 'master' of https://codeberg.org/river/river
This commit is contained in:
commit
2061ae2c4c
@ -28,15 +28,17 @@ sources:
|
|||||||
tasks:
|
tasks:
|
||||||
- install_deps: |
|
- install_deps: |
|
||||||
cd wayland
|
cd wayland
|
||||||
git checkout 1.22.0
|
git checkout 1.23.0
|
||||||
meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
|
meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
|
||||||
sudo ninja -C build install
|
sudo ninja -C build install
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
cd wlroots
|
cd wlroots
|
||||||
git checkout 0.17.2
|
git checkout 0.18.0
|
||||||
meson setup build --auto-features=enabled -Drenderers=gles2 -Dexamples=false \
|
meson setup build --auto-features=enabled -Drenderers=gles2 \
|
||||||
-Dwerror=false -Db_ndebug=false -Dxcb-errors=disabled --prefix /usr
|
-Dcolor-management=disabled -Dlibliftoff=disabled \
|
||||||
|
-Dexamples=false -Dwerror=false -Db_ndebug=false \
|
||||||
|
-Dxcb-errors=disabled --prefix /usr
|
||||||
sudo ninja -C build/ install
|
sudo ninja -C build/ install
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
@ -26,15 +26,17 @@ sources:
|
|||||||
tasks:
|
tasks:
|
||||||
- install_deps: |
|
- install_deps: |
|
||||||
cd wayland
|
cd wayland
|
||||||
git checkout 1.22.0
|
git checkout 1.23.0
|
||||||
meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
|
meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
|
||||||
sudo ninja -C build install
|
sudo ninja -C build install
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
cd wlroots
|
cd wlroots
|
||||||
git checkout 0.17.2
|
git checkout 0.18.0
|
||||||
meson setup build --auto-features=enabled -Drenderers=gles2 -Dexamples=false \
|
meson setup build --auto-features=enabled -Drenderers=gles2 \
|
||||||
-Dwerror=false -Db_ndebug=false --prefix /usr
|
-Dcolor-management=disabled -Dlibliftoff=disabled \
|
||||||
|
-Dexamples=false -Dwerror=false -Db_ndebug=false \
|
||||||
|
-Dxcb-errors=disabled --prefix /usr
|
||||||
sudo ninja -C build/ install
|
sudo ninja -C build/ install
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
@ -31,15 +31,17 @@ sources:
|
|||||||
tasks:
|
tasks:
|
||||||
- install_deps: |
|
- install_deps: |
|
||||||
cd wayland
|
cd wayland
|
||||||
git checkout 1.22.0
|
git checkout 1.23.0
|
||||||
meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
|
meson setup build -Ddocumentation=false -Dtests=false --prefix /usr
|
||||||
sudo ninja -C build install
|
sudo ninja -C build install
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
cd wlroots
|
cd wlroots
|
||||||
git checkout 0.17.2
|
git checkout 0.18.0
|
||||||
meson setup build --auto-features=enabled -Drenderers=gles2 -Dexamples=false \
|
meson setup build --auto-features=enabled -Drenderers=gles2 \
|
||||||
-Dwerror=false -Db_ndebug=false --prefix /usr
|
-Dcolor-management=disabled -Dlibliftoff=disabled \
|
||||||
|
-Dexamples=false -Dwerror=false -Db_ndebug=false \
|
||||||
|
-Dxcb-errors=disabled --prefix /usr
|
||||||
sudo ninja -C build/ install
|
sudo ninja -C build/ install
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
River is a dynamic tiling Wayland compositor with flexible runtime
|
River is a dynamic tiling Wayland compositor with flexible runtime
|
||||||
configuration.
|
configuration.
|
||||||
|
|
||||||
Check [packaging status](https://repology.org/project/river/versions) —
|
Check [packaging status](https://repology.org/project/river-compositor/versions) —
|
||||||
Join us at [#river](https://web.libera.chat/?channels=#river) on irc.libera.chat —
|
Join us at [#river](https://web.libera.chat/?channels=#river) on irc.libera.chat —
|
||||||
Read our man pages, [wiki](https://codeberg.org/river/wiki), and
|
Read our man pages, [wiki](https://codeberg.org/river/wiki), and
|
||||||
[Code of Conduct](CODE_OF_CONDUCT.md)
|
[Code of Conduct](CODE_OF_CONDUCT.md)
|
||||||
@ -60,7 +60,7 @@ distribution.
|
|||||||
- [zig](https://ziglang.org/download/) 0.13
|
- [zig](https://ziglang.org/download/) 0.13
|
||||||
- wayland
|
- wayland
|
||||||
- wayland-protocols
|
- wayland-protocols
|
||||||
- [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.17.2
|
- [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.18
|
||||||
- xkbcommon
|
- xkbcommon
|
||||||
- libevdev
|
- libevdev
|
||||||
- pixman
|
- pixman
|
||||||
@ -72,6 +72,7 @@ Then run, for example:
|
|||||||
zig build -Doptimize=ReleaseSafe --prefix ~/.local install
|
zig build -Doptimize=ReleaseSafe --prefix ~/.local install
|
||||||
```
|
```
|
||||||
To enable Xwayland support pass the `-Dxwayland` option as well.
|
To enable Xwayland support pass the `-Dxwayland` option as well.
|
||||||
|
Run `zig build -h` to see a list of all options.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -92,11 +92,12 @@ pub fn build(b: *Build) !void {
|
|||||||
const scanner = Scanner.create(b, .{});
|
const scanner = Scanner.create(b, .{});
|
||||||
|
|
||||||
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
|
scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
|
||||||
|
scanner.addSystemProtocol("stable/tablet/tablet-v2.xml");
|
||||||
scanner.addSystemProtocol("staging/cursor-shape/cursor-shape-v1.xml");
|
scanner.addSystemProtocol("staging/cursor-shape/cursor-shape-v1.xml");
|
||||||
scanner.addSystemProtocol("staging/ext-session-lock/ext-session-lock-v1.xml");
|
scanner.addSystemProtocol("staging/ext-session-lock/ext-session-lock-v1.xml");
|
||||||
|
scanner.addSystemProtocol("staging/tearing-control/tearing-control-v1.xml");
|
||||||
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
|
scanner.addSystemProtocol("unstable/pointer-constraints/pointer-constraints-unstable-v1.xml");
|
||||||
scanner.addSystemProtocol("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml");
|
scanner.addSystemProtocol("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml");
|
||||||
scanner.addSystemProtocol("unstable/tablet/tablet-unstable-v2.xml");
|
|
||||||
scanner.addSystemProtocol("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml");
|
scanner.addSystemProtocol("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml");
|
||||||
|
|
||||||
scanner.addCustomProtocol("protocol/river-control-unstable-v1.xml");
|
scanner.addCustomProtocol("protocol/river-control-unstable-v1.xml");
|
||||||
@ -124,6 +125,7 @@ pub fn build(b: *Build) !void {
|
|||||||
scanner.generate("zxdg_decoration_manager_v1", 1);
|
scanner.generate("zxdg_decoration_manager_v1", 1);
|
||||||
scanner.generate("ext_session_lock_manager_v1", 1);
|
scanner.generate("ext_session_lock_manager_v1", 1);
|
||||||
scanner.generate("wp_cursor_shape_manager_v1", 1);
|
scanner.generate("wp_cursor_shape_manager_v1", 1);
|
||||||
|
scanner.generate("wp_tearing_control_manager_v1", 1);
|
||||||
|
|
||||||
scanner.generate("zriver_control_v1", 1);
|
scanner.generate("zriver_control_v1", 1);
|
||||||
scanner.generate("zriver_status_manager_v1", 4);
|
scanner.generate("zriver_status_manager_v1", 4);
|
||||||
@ -146,7 +148,7 @@ pub fn build(b: *Build) !void {
|
|||||||
// exposed to the wlroots module for @cImport() to work. This seems to be
|
// exposed to the wlroots module for @cImport() to work. This seems to be
|
||||||
// the best way to do so with the current std.Build API.
|
// the best way to do so with the current std.Build API.
|
||||||
wlroots.resolved_target = target;
|
wlroots.resolved_target = target;
|
||||||
wlroots.linkSystemLibrary("wlroots", .{});
|
wlroots.linkSystemLibrary("wlroots-0.18", .{});
|
||||||
|
|
||||||
const flags = b.createModule(.{ .root_source_file = b.path("common/flags.zig") });
|
const flags = b.createModule(.{ .root_source_file = b.path("common/flags.zig") });
|
||||||
const globber = b.createModule(.{ .root_source_file = b.path("common/globber.zig") });
|
const globber = b.createModule(.{ .root_source_file = b.path("common/globber.zig") });
|
||||||
@ -167,7 +169,7 @@ pub fn build(b: *Build) !void {
|
|||||||
river.linkSystemLibrary("libevdev");
|
river.linkSystemLibrary("libevdev");
|
||||||
river.linkSystemLibrary("libinput");
|
river.linkSystemLibrary("libinput");
|
||||||
river.linkSystemLibrary("wayland-server");
|
river.linkSystemLibrary("wayland-server");
|
||||||
river.linkSystemLibrary("wlroots");
|
river.linkSystemLibrary("wlroots-0.18");
|
||||||
river.linkSystemLibrary("xkbcommon");
|
river.linkSystemLibrary("xkbcommon");
|
||||||
river.linkSystemLibrary("pixman-1");
|
river.linkSystemLibrary("pixman-1");
|
||||||
|
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
.hash = "1220687c8c47a48ba285d26a05600f8700d37fc637e223ced3aa8324f3650bf52242",
|
.hash = "1220687c8c47a48ba285d26a05600f8700d37fc637e223ced3aa8324f3650bf52242",
|
||||||
},
|
},
|
||||||
.@"zig-wlroots" = .{
|
.@"zig-wlroots" = .{
|
||||||
.url = "https://codeberg.org/ifreund/zig-wlroots/archive/084736cd92364b5fa7d8161611d085ce272fa707.tar.gz",
|
.url = "https://codeberg.org/ifreund/zig-wlroots/archive/e486223799648d27e8b91c5fe0ea4c088b74b707.tar.gz",
|
||||||
.hash = "12208383c1cf42e9b932b90f68cd4f378582cf966355a6377fd8f913852e7bc2d7c6",
|
.hash = "1220aeb3317e16c38583839961c9d695fa60d23a3d506c8275fb0e8fa9849844f2f7",
|
||||||
},
|
},
|
||||||
.@"zig-xkbcommon" = .{
|
.@"zig-xkbcommon" = .{
|
||||||
.url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.2.0.tar.gz",
|
.url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.2.0.tar.gz",
|
||||||
|
@ -99,6 +99,7 @@ function __riverctl_completion ()
|
|||||||
tap-button-map \
|
tap-button-map \
|
||||||
scroll-method \
|
scroll-method \
|
||||||
scroll-button \
|
scroll-button \
|
||||||
|
scroll-button-lock \
|
||||||
map-to-output"
|
map-to-output"
|
||||||
COMPREPLY=($(compgen -W "${OPTS}" -- "${COMP_WORDS[3]}"))
|
COMPREPLY=($(compgen -W "${OPTS}" -- "${COMP_WORDS[3]}"))
|
||||||
elif [ "${COMP_WORDS[1]}" == "hide-cursor" ]
|
elif [ "${COMP_WORDS[1]}" == "hide-cursor" ]
|
||||||
@ -117,7 +118,7 @@ function __riverctl_completion ()
|
|||||||
"events") OPTS="enabled disabled disabled-on-external-mouse" ;;
|
"events") OPTS="enabled disabled disabled-on-external-mouse" ;;
|
||||||
"accel-profile") OPTS="none flat adaptive" ;;
|
"accel-profile") OPTS="none flat adaptive" ;;
|
||||||
"click-method") OPTS="none button-areas clickfinger" ;;
|
"click-method") OPTS="none button-areas clickfinger" ;;
|
||||||
"drag"|"drag-lock"|"disable-while-typing"|"middle-emulation"|"left-handed"|"tap") OPTS="enabled disabled" ;;
|
"drag"|"drag-lock"|"disable-while-typing"|"middle-emulation"|"left-handed"|"tap"|"scroll-button-lock") OPTS="enabled disabled" ;;
|
||||||
"tap-button-map") OPTS="left-right-middle left-middle-right" ;;
|
"tap-button-map") OPTS="left-right-middle left-middle-right" ;;
|
||||||
"scroll-method") OPTS="none two-finger edge button" ;;
|
"scroll-method") OPTS="none two-finger edge button" ;;
|
||||||
*) return ;;
|
*) return ;;
|
||||||
|
@ -119,10 +119,11 @@ complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_
|
|||||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'tap-button-map' -d 'Configure the button mapping for tapping'
|
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'tap-button-map' -d 'Configure the button mapping for tapping'
|
||||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'scroll-method' -d 'Set the scroll method'
|
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'scroll-method' -d 'Set the scroll method'
|
||||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'scroll-button' -d 'Set the scroll button'
|
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'scroll-button' -d 'Set the scroll button'
|
||||||
|
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'scroll-button-lock' -d 'Enable or disable the scroll button lock functionality'
|
||||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'map-to-output' -d 'Map to a given output'
|
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 3' -a 'map-to-output' -d 'Map to a given output'
|
||||||
|
|
||||||
# Subcommands for the subcommands of 'input'
|
# Subcommands for the subcommands of 'input'
|
||||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 4; and __fish_seen_subcommand_from drag drag-lock disable-while-typing disable-while-trackpointing middle-emulation natural-scroll left-handed tap' -a 'enabled disabled'
|
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 4; and __fish_seen_subcommand_from drag drag-lock disable-while-typing disable-while-trackpointing middle-emulation natural-scroll left-handed tap scroll-button-lock' -a 'enabled disabled'
|
||||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 4; and __fish_seen_subcommand_from events' -a 'enabled disabled disabled-on-external-mouse'
|
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 4; and __fish_seen_subcommand_from events' -a 'enabled disabled disabled-on-external-mouse'
|
||||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 4; and __fish_seen_subcommand_from accel-profile' -a 'none flat adaptive'
|
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 4; and __fish_seen_subcommand_from accel-profile' -a 'none flat adaptive'
|
||||||
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 4; and __fish_seen_subcommand_from click-method' -a 'none button-areas clickfinger'
|
complete -c riverctl -n '__fish_seen_subcommand_from input; and __fish_riverctl_complete_arg 4; and __fish_seen_subcommand_from click-method' -a 'none button-areas clickfinger'
|
||||||
|
@ -126,6 +126,7 @@ _riverctl()
|
|||||||
'tap-button-map:Configure the button mapping for tapping'
|
'tap-button-map:Configure the button mapping for tapping'
|
||||||
'scroll-method:Set the scroll method'
|
'scroll-method:Set the scroll method'
|
||||||
'scroll-button:Set the scroll button'
|
'scroll-button:Set the scroll button'
|
||||||
|
'scroll-button-lock:Enable or disable the scroll button lock functionality'
|
||||||
'map-to-output:Map to a given output'
|
'map-to-output:Map to a given output'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -135,7 +136,7 @@ _riverctl()
|
|||||||
case "$line[2]" in
|
case "$line[2]" in
|
||||||
events) _alternative 'input-cmds:args:(enabled disabled disabled-on-external-mouse)' ;;
|
events) _alternative 'input-cmds:args:(enabled disabled disabled-on-external-mouse)' ;;
|
||||||
accel-profile) _alternative 'input-cmds:args:(none flat adaptive)' ;;
|
accel-profile) _alternative 'input-cmds:args:(none flat adaptive)' ;;
|
||||||
click-method) _alternative 'input-cmds:args:(none button-area clickfinger)' ;;
|
click-method) _alternative 'input-cmds:args:(none button-areas clickfinger)' ;;
|
||||||
drag) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
drag) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
||||||
drag-lock) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
drag-lock) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
||||||
disable-while-typing) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
disable-while-typing) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
||||||
@ -144,6 +145,7 @@ _riverctl()
|
|||||||
natural-scroll) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
natural-scroll) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
||||||
left-handed) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
left-handed) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
||||||
tap) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
tap) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
||||||
|
scroll-button-lock) _alternative 'input-cmds:args:(enabled disabled)' ;;
|
||||||
tap-button-map) _alternative 'input-cmds:args:(left-right-middle left-middle-right)' ;;
|
tap-button-map) _alternative 'input-cmds:args:(left-right-middle left-middle-right)' ;;
|
||||||
scroll-method) _alternative 'input-cmds:args:(none two-finger edge button)' ;;
|
scroll-method) _alternative 'input-cmds:args:(none two-finger edge button)' ;;
|
||||||
*) return 0 ;;
|
*) return 0 ;;
|
||||||
|
@ -307,11 +307,16 @@ matches everything while _\*\*_ and the empty string are invalid.
|
|||||||
- *fullscreen*: Make the view fullscreen. Applies only to new views.
|
- *fullscreen*: Make the view fullscreen. Applies only to new views.
|
||||||
- *no-fullscreen*: Don't make the view fullscreen. Applies only to
|
- *no-fullscreen*: Don't make the view fullscreen. Applies only to
|
||||||
new views.
|
new views.
|
||||||
|
- *tearing*: Allow the view to tear when fullscreen regardless of the
|
||||||
|
view's preference. Applies to new and existing views.
|
||||||
|
- *no-tearing*: Disable tearing for the view regardless of the view's
|
||||||
|
preference. Applies to new and existing views.
|
||||||
|
|
||||||
Both *float* and *no-float* rules are added to the same list,
|
Both *float* and *no-float* rules are added to the same list,
|
||||||
which means that adding a *no-float* rule with the same arguments
|
which means that adding a *no-float* rule with the same arguments
|
||||||
as a *float* rule will overwrite it. The same holds for *ssd* and
|
as a *float* rule will overwrite it. The same holds for *ssd* and
|
||||||
*csd*, *fullscreen* and *no-fullscreen* rules.
|
*csd*, *fullscreen* and *no-fullscreen*, *tearing* and
|
||||||
|
*no-tearing* rules.
|
||||||
|
|
||||||
If multiple rules in a list match a given view the most specific
|
If multiple rules in a list match a given view the most specific
|
||||||
rule will be applied. For example with the following rules
|
rule will be applied. For example with the following rules
|
||||||
@ -364,6 +369,10 @@ matches everything while _\*\*_ and the empty string are invalid.
|
|||||||
Set the attach mode of the currently focused output, overriding the value of
|
Set the attach mode of the currently focused output, overriding the value of
|
||||||
default-attach-mode if any.
|
default-attach-mode if any.
|
||||||
|
|
||||||
|
*allow-tearing* *enabled*|*disabled*
|
||||||
|
Allow fullscreen views to tear if requested by the view. See also the
|
||||||
|
*tearing* rule to force enable tearing for specific views.
|
||||||
|
|
||||||
*background-color* _0xRRGGBB_|_0xRRGGBBAA_
|
*background-color* _0xRRGGBB_|_0xRRGGBBAA_
|
||||||
Set the background color.
|
Set the background color.
|
||||||
|
|
||||||
@ -465,8 +474,8 @@ matches everything while _\*\*_ and the empty string are invalid.
|
|||||||
|
|
||||||
The _input_ command can be used to create a configuration rule for an input
|
The _input_ command can be used to create a configuration rule for an input
|
||||||
device identified by its _name_.
|
device identified by its _name_.
|
||||||
The _name_ of an input device consists of its type, its numerical vendor id,
|
The _name_ of an input device consists of its type, its decimal vendor id,
|
||||||
its numerical product id and finally its self-advertised name, separated by -.
|
its decimal product id and finally its self-advertised name, separated by -.
|
||||||
Simple globbing patterns are supported, see the rules section for further
|
Simple globbing patterns are supported, see the rules section for further
|
||||||
information on globs.
|
information on globs.
|
||||||
|
|
||||||
@ -536,6 +545,11 @@ However note that not every input device supports every property.
|
|||||||
Set the scroll button of an input device. _button_ is the name of a Linux
|
Set the scroll button of an input device. _button_ is the name of a Linux
|
||||||
input event code.
|
input event code.
|
||||||
|
|
||||||
|
*input* _name_ *scroll-button-lock* *enabled*|*disabled*
|
||||||
|
Enable or disable the scroll button lock functionality of the input device. If
|
||||||
|
active, the button does not need to be held down. One press makes the button
|
||||||
|
considered to be held down, and a second press releases the button.
|
||||||
|
|
||||||
*input* _name_ *map-to-output* _output_|*disabled*
|
*input* _name_ *map-to-output* _output_|*disabled*
|
||||||
Maps the input to a given output. This is valid even if the output isn't
|
Maps the input to a given output. This is valid even if the output isn't
|
||||||
currently active and will lead to the device being mapped once it is
|
currently active and will lead to the device being mapped once it is
|
||||||
|
@ -68,6 +68,9 @@ pub const Dimensions = struct {
|
|||||||
height: u31,
|
height: u31,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Whether to allow tearing page flips when fullscreen if a view requests it.
|
||||||
|
allow_tearing: bool = false,
|
||||||
|
|
||||||
/// Color of background in RGBA with premultiplied alpha (alpha should only affect nested sessions)
|
/// Color of background in RGBA with premultiplied alpha (alpha should only affect nested sessions)
|
||||||
background_color: [4]f32 = [_]f32{ 0.0, 0.16862745, 0.21176471, 1.0 }, // Solarized base03
|
background_color: [4]f32 = [_]f32{ 0.0, 0.16862745, 0.21176471, 1.0 }, // Solarized base03
|
||||||
|
|
||||||
@ -98,6 +101,7 @@ rules: struct {
|
|||||||
position: RuleList(Position) = .{},
|
position: RuleList(Position) = .{},
|
||||||
dimensions: RuleList(Dimensions) = .{},
|
dimensions: RuleList(Dimensions) = .{},
|
||||||
fullscreen: RuleList(bool) = .{},
|
fullscreen: RuleList(bool) = .{},
|
||||||
|
tearing: RuleList(bool) = .{},
|
||||||
} = .{},
|
} = .{},
|
||||||
|
|
||||||
/// The selected focus_follows_cursor mode
|
/// The selected focus_follows_cursor mode
|
||||||
|
@ -324,6 +324,7 @@ fn handleAxis(listener: *wl.Listener(*wlr.Pointer.event.Axis), event: *wlr.Point
|
|||||||
math.maxInt(i32) / 2,
|
math.maxInt(i32) / 2,
|
||||||
)),
|
)),
|
||||||
event.source,
|
event.source,
|
||||||
|
event.relative_direction,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +569,7 @@ fn handleTouchUp(
|
|||||||
cursor.seat.handleActivity();
|
cursor.seat.handleActivity();
|
||||||
|
|
||||||
if (cursor.touch_points.remove(event.touch_id)) {
|
if (cursor.touch_points.remove(event.touch_id)) {
|
||||||
cursor.seat.wlr_seat.touchNotifyUp(event.time_msec, event.touch_id);
|
_ = cursor.seat.wlr_seat.touchNotifyUp(event.time_msec, event.touch_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,32 +583,9 @@ fn handleTouchCancel(
|
|||||||
|
|
||||||
cursor.touch_points.clearRetainingCapacity();
|
cursor.touch_points.clearRetainingCapacity();
|
||||||
|
|
||||||
// We can't call touchNotifyCancel() from inside the loop over touch points as it also loops
|
const wlr_seat = cursor.seat.wlr_seat;
|
||||||
// over touch points and may destroy multiple touch points in a single call.
|
while (wlr_seat.touch_state.touch_points.first()) |touch_point| {
|
||||||
//
|
wlr_seat.touchNotifyCancel(touch_point.client);
|
||||||
// What we should do here is `while (touch_points.first()) |point| cancel()` but since the
|
|
||||||
// surface may be null we can't rely on the fact tha all touch points will be destroyed
|
|
||||||
// and risk an infinite loop if the surface of any wlr_touch_point is null.
|
|
||||||
//
|
|
||||||
// This is all really silly and totally unnecessary since all touchNotifyCancel() does with
|
|
||||||
// the surface argument is obtain a seat client and touch_point.seat_client is never null.
|
|
||||||
// TODO(wlroots) clean this up after the wlroots MR fixing this is merged:
|
|
||||||
// https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4613
|
|
||||||
|
|
||||||
// The upper bound of 32 comes from an implementation detail of libinput which uses
|
|
||||||
// a 32-bit integer as a map to keep track of touch points.
|
|
||||||
var surfaces: std.BoundedArray(*wlr.Surface, 32) = .{};
|
|
||||||
{
|
|
||||||
var it = cursor.seat.wlr_seat.touch_state.touch_points.iterator(.forward);
|
|
||||||
while (it.next()) |touch_point| {
|
|
||||||
if (touch_point.surface) |surface| {
|
|
||||||
surfaces.append(surface) catch break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (surfaces.slice()) |surface| {
|
|
||||||
cursor.seat.wlr_seat.touchNotifyCancel(surface);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,6 +215,18 @@ pub const ScrollButton = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const ScrollButtonLock = enum {
|
||||||
|
enabled,
|
||||||
|
disabled,
|
||||||
|
|
||||||
|
fn apply(scroll_button_lock: ScrollButtonLock, device: *c.libinput_device) void {
|
||||||
|
_ = c.libinput_device_config_scroll_set_button_lock(device, switch (scroll_button_lock) {
|
||||||
|
.enabled => c.LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_ENABLED,
|
||||||
|
.disabled => c.LIBINPUT_CONFIG_SCROLL_BUTTON_LOCK_DISABLED,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const MapToOutput = struct {
|
pub const MapToOutput = struct {
|
||||||
output_name: ?[]const u8,
|
output_name: ?[]const u8,
|
||||||
|
|
||||||
@ -232,7 +244,7 @@ pub const MapToOutput = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
switch (device.wlr_device.type) {
|
switch (device.wlr_device.type) {
|
||||||
.pointer, .touch, .tablet_tool => {
|
.pointer, .touch, .tablet => {
|
||||||
log.debug("mapping input '{s}' -> '{s}'", .{
|
log.debug("mapping input '{s}' -> '{s}'", .{
|
||||||
device.identifier,
|
device.identifier,
|
||||||
if (wlr_output) |o| o.name else "<no output>",
|
if (wlr_output) |o| o.name else "<no output>",
|
||||||
@ -240,14 +252,14 @@ pub const MapToOutput = struct {
|
|||||||
|
|
||||||
device.seat.cursor.wlr_cursor.mapInputToOutput(device.wlr_device, wlr_output);
|
device.seat.cursor.wlr_cursor.mapInputToOutput(device.wlr_device, wlr_output);
|
||||||
|
|
||||||
if (device.wlr_device.type == .tablet_tool) {
|
if (device.wlr_device.type == .tablet) {
|
||||||
const tablet: *Tablet = @fieldParentPtr("device", device);
|
const tablet: *Tablet = @fieldParentPtr("device", device);
|
||||||
tablet.output_mapping = wlr_output;
|
tablet.output_mapping = wlr_output;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// These devices do not support being mapped to outputs.
|
// These devices do not support being mapped to outputs.
|
||||||
.keyboard, .tablet_pad, .switch_device => {},
|
.keyboard, .tablet_pad, .@"switch" => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -279,6 +291,7 @@ tap: ?TapState = null,
|
|||||||
@"pointer-accel": ?PointerAccel = null,
|
@"pointer-accel": ?PointerAccel = null,
|
||||||
@"scroll-method": ?ScrollMethod = null,
|
@"scroll-method": ?ScrollMethod = null,
|
||||||
@"scroll-button": ?ScrollButton = null,
|
@"scroll-button": ?ScrollButton = null,
|
||||||
|
@"scroll-button-lock": ?ScrollButtonLock = null,
|
||||||
@"map-to-output": ?MapToOutput = null,
|
@"map-to-output": ?MapToOutput = null,
|
||||||
|
|
||||||
pub fn deinit(config: *InputConfig) void {
|
pub fn deinit(config: *InputConfig) void {
|
||||||
|
@ -24,6 +24,7 @@ const wl = @import("wayland").server.wl;
|
|||||||
|
|
||||||
const globber = @import("globber");
|
const globber = @import("globber");
|
||||||
|
|
||||||
|
const c = @import("c.zig");
|
||||||
const server = &@import("main.zig").server;
|
const server = &@import("main.zig").server;
|
||||||
const util = @import("util.zig");
|
const util = @import("util.zig");
|
||||||
|
|
||||||
@ -52,19 +53,21 @@ config: struct {
|
|||||||
link: wl.list.Link,
|
link: wl.list.Link,
|
||||||
|
|
||||||
pub fn init(device: *InputDevice, seat: *Seat, wlr_device: *wlr.InputDevice) !void {
|
pub fn init(device: *InputDevice, seat: *Seat, wlr_device: *wlr.InputDevice) !void {
|
||||||
const device_type: []const u8 = switch (wlr_device.type) {
|
var vendor: c_uint = 0;
|
||||||
.switch_device => "switch",
|
var product: c_uint = 0;
|
||||||
.tablet_tool => "tablet",
|
|
||||||
else => @tagName(wlr_device.type),
|
if (wlr_device.getLibinputDevice()) |d| {
|
||||||
};
|
vendor = c.libinput_device_get_id_vendor(@ptrCast(d));
|
||||||
|
product = c.libinput_device_get_id_product(@ptrCast(d));
|
||||||
|
}
|
||||||
|
|
||||||
const identifier = try std.fmt.allocPrint(
|
const identifier = try std.fmt.allocPrint(
|
||||||
util.gpa,
|
util.gpa,
|
||||||
"{s}-{}-{}-{s}",
|
"{s}-{}-{}-{s}",
|
||||||
.{
|
.{
|
||||||
device_type,
|
@tagName(wlr_device.type),
|
||||||
wlr_device.vendor,
|
vendor,
|
||||||
wlr_device.product,
|
product,
|
||||||
mem.trim(u8, mem.sliceTo(wlr_device.name orelse "unknown", 0), &ascii.whitespace),
|
mem.trim(u8, mem.sliceTo(wlr_device.name orelse "unknown", 0), &ascii.whitespace),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -139,11 +142,11 @@ fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice)
|
|||||||
device.deinit();
|
device.deinit();
|
||||||
util.gpa.destroy(device);
|
util.gpa.destroy(device);
|
||||||
},
|
},
|
||||||
.tablet_tool => {
|
.tablet => {
|
||||||
const tablet: *Tablet = @fieldParentPtr("device", device);
|
const tablet: *Tablet = @fieldParentPtr("device", device);
|
||||||
tablet.destroy();
|
tablet.destroy();
|
||||||
},
|
},
|
||||||
.switch_device => {
|
.@"switch" => {
|
||||||
const switch_device: *Switch = @fieldParentPtr("device", device);
|
const switch_device: *Switch = @fieldParentPtr("device", device);
|
||||||
switch_device.deinit();
|
switch_device.deinit();
|
||||||
util.gpa.destroy(switch_device);
|
util.gpa.destroy(switch_device);
|
||||||
|
@ -66,11 +66,6 @@ pub fn create(wlr_layer_surface: *wlr.LayerSurfaceV1) error{OutOfMemory}!void {
|
|||||||
wlr_layer_surface.surface.events.unmap.add(&layer_surface.unmap);
|
wlr_layer_surface.surface.events.unmap.add(&layer_surface.unmap);
|
||||||
wlr_layer_surface.surface.events.commit.add(&layer_surface.commit);
|
wlr_layer_surface.surface.events.commit.add(&layer_surface.commit);
|
||||||
wlr_layer_surface.events.new_popup.add(&layer_surface.new_popup);
|
wlr_layer_surface.events.new_popup.add(&layer_surface.new_popup);
|
||||||
|
|
||||||
// wlroots only informs us of the new surface after the first commit,
|
|
||||||
// so our listener does not get called for this first commit. However,
|
|
||||||
// we do want our listener called in order to send the initial configure.
|
|
||||||
handleCommit(&layer_surface.commit, wlr_layer_surface.surface);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn destroyPopups(layer_surface: *LayerSurface) void {
|
pub fn destroyPopups(layer_surface: *LayerSurface) void {
|
||||||
@ -100,11 +95,19 @@ fn handleDestroy(listener: *wl.Listener(*wlr.LayerSurfaceV1), _: *wlr.LayerSurfa
|
|||||||
|
|
||||||
fn handleMap(listener: *wl.Listener(void)) void {
|
fn handleMap(listener: *wl.Listener(void)) void {
|
||||||
const layer_surface: *LayerSurface = @fieldParentPtr("map", listener);
|
const layer_surface: *LayerSurface = @fieldParentPtr("map", listener);
|
||||||
|
const wlr_surface = layer_surface.wlr_layer_surface;
|
||||||
|
|
||||||
log.debug("layer surface '{s}' mapped", .{layer_surface.wlr_layer_surface.namespace});
|
log.debug("layer surface '{s}' mapped", .{wlr_surface.namespace});
|
||||||
|
|
||||||
layer_surface.output.arrangeLayers();
|
layer_surface.output.arrangeLayers();
|
||||||
handleKeyboardInteractiveExclusive(layer_surface.output);
|
|
||||||
|
const consider = wlr_surface.current.keyboard_interactive == .on_demand and
|
||||||
|
(wlr_surface.current.layer == .top or wlr_surface.current.layer == .overlay);
|
||||||
|
handleKeyboardInteractiveExclusive(
|
||||||
|
layer_surface.output,
|
||||||
|
if (consider) layer_surface else null,
|
||||||
|
);
|
||||||
|
|
||||||
server.root.applyPending();
|
server.root.applyPending();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +117,7 @@ fn handleUnmap(listener: *wl.Listener(void)) void {
|
|||||||
log.debug("layer surface '{s}' unmapped", .{layer_surface.wlr_layer_surface.namespace});
|
log.debug("layer surface '{s}' unmapped", .{layer_surface.wlr_layer_surface.namespace});
|
||||||
|
|
||||||
layer_surface.output.arrangeLayers();
|
layer_surface.output.arrangeLayers();
|
||||||
handleKeyboardInteractiveExclusive(layer_surface.output);
|
handleKeyboardInteractiveExclusive(layer_surface.output, null);
|
||||||
server.root.applyPending();
|
server.root.applyPending();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,18 +137,20 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
|
|||||||
@as(u32, @bitCast(wlr_layer_surface.current.committed)) != 0)
|
@as(u32, @bitCast(wlr_layer_surface.current.committed)) != 0)
|
||||||
{
|
{
|
||||||
layer_surface.output.arrangeLayers();
|
layer_surface.output.arrangeLayers();
|
||||||
handleKeyboardInteractiveExclusive(layer_surface.output);
|
handleKeyboardInteractiveExclusive(layer_surface.output, null);
|
||||||
server.root.applyPending();
|
server.root.applyPending();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Focus topmost keyboard-interactivity-exclusive layer surface above normal
|
||||||
|
/// content, or if none found, focus the surface given as `consider`.
|
||||||
/// Requires a call to Root.applyPending()
|
/// Requires a call to Root.applyPending()
|
||||||
fn handleKeyboardInteractiveExclusive(output: *Output) void {
|
fn handleKeyboardInteractiveExclusive(output: *Output, consider: ?*LayerSurface) void {
|
||||||
if (server.lock_manager.state != .unlocked) return;
|
if (server.lock_manager.state != .unlocked) return;
|
||||||
|
|
||||||
// Find the topmost layer surface in the top or overlay layers which
|
// Find the topmost layer surface (if any) in the top or overlay layers which
|
||||||
// requests keyboard interactivity if any.
|
// requests exclusive keyboard interactivity.
|
||||||
const topmost_surface = outer: for ([_]zwlr.LayerShellV1.Layer{ .overlay, .top }) |layer| {
|
const to_focus = outer: for ([_]zwlr.LayerShellV1.Layer{ .overlay, .top }) |layer| {
|
||||||
const tree = output.layerSurfaceTree(layer);
|
const tree = output.layerSurfaceTree(layer);
|
||||||
// Iterate in reverse to match rendering order.
|
// Iterate in reverse to match rendering order.
|
||||||
var it = tree.children.iterator(.reverse);
|
var it = tree.children.iterator(.reverse);
|
||||||
@ -161,17 +166,21 @@ fn handleKeyboardInteractiveExclusive(output: *Output) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else null;
|
} else consider;
|
||||||
|
|
||||||
|
if (to_focus) |s| {
|
||||||
|
assert(s.wlr_layer_surface.current.keyboard_interactive != .none);
|
||||||
|
}
|
||||||
|
|
||||||
var it = server.input_manager.seats.first;
|
var it = server.input_manager.seats.first;
|
||||||
while (it) |node| : (it = node.next) {
|
while (it) |node| : (it = node.next) {
|
||||||
const seat = &node.data;
|
const seat = &node.data;
|
||||||
|
|
||||||
if (seat.focused_output == output) {
|
if (seat.focused_output == output) {
|
||||||
if (topmost_surface) |to_focus| {
|
if (to_focus) |s| {
|
||||||
// If we found a surface on the output that requires focus, grab the focus of all
|
// If we found a surface on the output that requires focus, grab the focus of all
|
||||||
// seats that are focusing that output.
|
// seats that are focusing that output.
|
||||||
seat.setFocusRaw(.{ .layer = to_focus });
|
seat.setFocusRaw(.{ .layer = s });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -367,6 +367,8 @@ fn sendLayerConfigures(
|
|||||||
if (@as(?*SceneNodeData, @ptrFromInt(node.data))) |node_data| {
|
if (@as(?*SceneNodeData, @ptrFromInt(node.data))) |node_data| {
|
||||||
const layer_surface = node_data.data.layer_surface;
|
const layer_surface = node_data.data.layer_surface;
|
||||||
|
|
||||||
|
if (!layer_surface.wlr_layer_surface.initialized) continue;
|
||||||
|
|
||||||
const exclusive = layer_surface.wlr_layer_surface.current.exclusive_zone > 0;
|
const exclusive = layer_surface.wlr_layer_surface.current.exclusive_zone > 0;
|
||||||
if (exclusive != (mode == .exclusive)) {
|
if (exclusive != (mode == .exclusive)) {
|
||||||
continue;
|
continue;
|
||||||
@ -536,10 +538,12 @@ fn handleFrame(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn renderAndCommit(output: *Output, scene_output: *wlr.SceneOutput) !void {
|
fn renderAndCommit(output: *Output, scene_output: *wlr.SceneOutput) !void {
|
||||||
if (output.gamma_dirty) {
|
|
||||||
var state = wlr.Output.State.init();
|
var state = wlr.Output.State.init();
|
||||||
defer state.finish();
|
defer state.finish();
|
||||||
|
|
||||||
|
if (!scene_output.buildState(&state, null)) return error.CommitFailed;
|
||||||
|
|
||||||
|
if (output.gamma_dirty) {
|
||||||
const control = server.root.gamma_control_manager.getControl(output.wlr_output);
|
const control = server.root.gamma_control_manager.getControl(output.wlr_output);
|
||||||
if (!wlr.GammaControlV1.apply(control, &state)) return error.OutOfMemory;
|
if (!wlr.GammaControlV1.apply(control, &state)) return error.OutOfMemory;
|
||||||
|
|
||||||
@ -551,17 +555,23 @@ fn renderAndCommit(output: *Output, scene_output: *wlr.SceneOutput) !void {
|
|||||||
// has a null LUT. The wayland backend for example has this behavior.
|
// has a null LUT. The wayland backend for example has this behavior.
|
||||||
state.committed.gamma_lut = false;
|
state.committed.gamma_lut = false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!scene_output.buildState(&state, null)) return error.CommitFailed;
|
if (output.current.fullscreen) |fullscreen| {
|
||||||
|
if (fullscreen.allowTearing()) {
|
||||||
|
state.tearing_page_flip = true;
|
||||||
|
if (!output.wlr_output.testState(&state)) {
|
||||||
|
log.debug("tearing page flip test failed for {s}, retrying without tearing", .{
|
||||||
|
output.wlr_output.name,
|
||||||
|
});
|
||||||
|
state.tearing_page_flip = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!output.wlr_output.commitState(&state)) return error.CommitFailed;
|
if (!output.wlr_output.commitState(&state)) return error.CommitFailed;
|
||||||
|
|
||||||
// TODO(wlroots) remove this rotate() call when updating to wlroots 0.18
|
|
||||||
scene_output.damage_ring.rotate();
|
|
||||||
output.gamma_dirty = false;
|
output.gamma_dirty = false;
|
||||||
} else {
|
|
||||||
if (!scene_output.commit(null)) return error.CommitFailed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (server.lock_manager.state == .locked or
|
if (server.lock_manager.state == .locked or
|
||||||
(server.lock_manager.state == .waiting_for_lock_surfaces and output.locked_content.node.enabled) or
|
(server.lock_manager.state == .waiting_for_lock_surfaces and output.locked_content.node.enabled) or
|
||||||
|
@ -42,7 +42,7 @@ state: union(enum) {
|
|||||||
} = .inactive,
|
} = .inactive,
|
||||||
|
|
||||||
destroy: wl.Listener(*wlr.PointerConstraintV1) = wl.Listener(*wlr.PointerConstraintV1).init(handleDestroy),
|
destroy: wl.Listener(*wlr.PointerConstraintV1) = wl.Listener(*wlr.PointerConstraintV1).init(handleDestroy),
|
||||||
set_region: wl.Listener(void) = wl.Listener(void).init(handleSetRegion),
|
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
|
||||||
|
|
||||||
node_destroy: wl.Listener(void) = wl.Listener(void).init(handleNodeDestroy),
|
node_destroy: wl.Listener(void) = wl.Listener(void).init(handleNodeDestroy),
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ pub fn create(wlr_constraint: *wlr.PointerConstraintV1) error{OutOfMemory}!void
|
|||||||
wlr_constraint.data = @intFromPtr(constraint);
|
wlr_constraint.data = @intFromPtr(constraint);
|
||||||
|
|
||||||
wlr_constraint.events.destroy.add(&constraint.destroy);
|
wlr_constraint.events.destroy.add(&constraint.destroy);
|
||||||
wlr_constraint.events.set_region.add(&constraint.set_region);
|
wlr_constraint.surface.events.commit.add(&constraint.commit);
|
||||||
|
|
||||||
if (seat.wlr_seat.keyboard_state.focused_surface) |surface| {
|
if (seat.wlr_seat.keyboard_state.focused_surface) |surface| {
|
||||||
if (surface == wlr_constraint.surface) {
|
if (surface == wlr_constraint.surface) {
|
||||||
@ -169,7 +169,7 @@ pub fn deactivate(constraint: *PointerConstraint) void {
|
|||||||
fn warpToHintIfSet(constraint: *PointerConstraint) void {
|
fn warpToHintIfSet(constraint: *PointerConstraint) void {
|
||||||
const seat: *Seat = @ptrFromInt(constraint.wlr_constraint.seat.data);
|
const seat: *Seat = @ptrFromInt(constraint.wlr_constraint.seat.data);
|
||||||
|
|
||||||
if (constraint.wlr_constraint.current.committed.cursor_hint) {
|
if (constraint.wlr_constraint.current.cursor_hint.enabled) {
|
||||||
var lx: i32 = undefined;
|
var lx: i32 = undefined;
|
||||||
var ly: i32 = undefined;
|
var ly: i32 = undefined;
|
||||||
_ = constraint.state.active.node.coords(&lx, &ly);
|
_ = constraint.state.active.node.coords(&lx, &ly);
|
||||||
@ -201,7 +201,7 @@ fn handleDestroy(listener: *wl.Listener(*wlr.PointerConstraintV1), _: *wlr.Point
|
|||||||
}
|
}
|
||||||
|
|
||||||
constraint.destroy.link.remove();
|
constraint.destroy.link.remove();
|
||||||
constraint.set_region.link.remove();
|
constraint.commit.link.remove();
|
||||||
|
|
||||||
if (seat.cursor.constraint == constraint) {
|
if (seat.cursor.constraint == constraint) {
|
||||||
seat.cursor.constraint = null;
|
seat.cursor.constraint = null;
|
||||||
@ -210,8 +210,11 @@ fn handleDestroy(listener: *wl.Listener(*wlr.PointerConstraintV1), _: *wlr.Point
|
|||||||
util.gpa.destroy(constraint);
|
util.gpa.destroy(constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleSetRegion(listener: *wl.Listener(void)) void {
|
// It is necessary to listen for the commit event rather than the set_region
|
||||||
const constraint: *PointerConstraint = @fieldParentPtr("set_region", listener);
|
// event as the latter is not triggered by wlroots when the input region of
|
||||||
|
// the surface changes.
|
||||||
|
fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
|
||||||
|
const constraint: *PointerConstraint = @fieldParentPtr("commit", listener);
|
||||||
const seat: *Seat = @ptrFromInt(constraint.wlr_constraint.seat.data);
|
const seat: *Seat = @ptrFromInt(constraint.wlr_constraint.seat.data);
|
||||||
|
|
||||||
switch (constraint.state) {
|
switch (constraint.state) {
|
||||||
@ -219,7 +222,7 @@ fn handleSetRegion(listener: *wl.Listener(void)) void {
|
|||||||
const sx: i32 = @intFromFloat(state.sx);
|
const sx: i32 = @intFromFloat(state.sx);
|
||||||
const sy: i32 = @intFromFloat(state.sy);
|
const sy: i32 = @intFromFloat(state.sy);
|
||||||
if (!constraint.wlr_constraint.region.containsPoint(sx, sy, null)) {
|
if (!constraint.wlr_constraint.region.containsPoint(sx, sy, null)) {
|
||||||
log.info("deactivating pointer constraint, region change left pointer outside constraint", .{});
|
log.info("deactivating pointer constraint, (input) region change left pointer outside constraint", .{});
|
||||||
constraint.deactivate();
|
constraint.deactivate();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -117,7 +117,7 @@ transaction_timeout: *wl.EventSource,
|
|||||||
pending_state_dirty: bool = false,
|
pending_state_dirty: bool = false,
|
||||||
|
|
||||||
pub fn init(root: *Root) !void {
|
pub fn init(root: *Root) !void {
|
||||||
const output_layout = try wlr.OutputLayout.create();
|
const output_layout = try wlr.OutputLayout.create(server.wl_server);
|
||||||
errdefer output_layout.destroy();
|
errdefer output_layout.destroy();
|
||||||
|
|
||||||
const scene = try wlr.Scene.create();
|
const scene = try wlr.Scene.create();
|
||||||
@ -131,9 +131,6 @@ pub fn init(root: *Root) !void {
|
|||||||
const outputs = try interactive_content.createSceneTree();
|
const outputs = try interactive_content.createSceneTree();
|
||||||
const override_redirect = if (build_options.xwayland) try interactive_content.createSceneTree();
|
const override_redirect = if (build_options.xwayland) try interactive_content.createSceneTree();
|
||||||
|
|
||||||
const presentation = try wlr.Presentation.create(server.wl_server, server.backend);
|
|
||||||
scene.setPresentation(presentation);
|
|
||||||
|
|
||||||
const event_loop = server.wl_server.getEventLoop();
|
const event_loop = server.wl_server.getEventLoop();
|
||||||
const transaction_timeout = try event_loop.addTimer(*Root, handleTransactionTimeout, root);
|
const transaction_timeout = try event_loop.addTimer(*Root, handleTransactionTimeout, root);
|
||||||
errdefer transaction_timeout.remove();
|
errdefer transaction_timeout.remove();
|
||||||
@ -166,7 +163,7 @@ pub fn init(root: *Root) !void {
|
|||||||
.all_outputs = undefined,
|
.all_outputs = undefined,
|
||||||
.active_outputs = undefined,
|
.active_outputs = undefined,
|
||||||
|
|
||||||
.presentation = presentation,
|
.presentation = try wlr.Presentation.create(server.wl_server, server.backend),
|
||||||
.xdg_output_manager = try wlr.XdgOutputManagerV1.create(server.wl_server, output_layout),
|
.xdg_output_manager = try wlr.XdgOutputManagerV1.create(server.wl_server, output_layout),
|
||||||
.output_manager = try wlr.OutputManagerV1.create(server.wl_server),
|
.output_manager = try wlr.OutputManagerV1.create(server.wl_server),
|
||||||
.power_manager = try wlr.OutputPowerManagerV1.create(server.wl_server),
|
.power_manager = try wlr.OutputPowerManagerV1.create(server.wl_server),
|
||||||
@ -676,24 +673,12 @@ fn commitTransaction(root: *Root) void {
|
|||||||
while (focus_stack_it.next()) |view| {
|
while (focus_stack_it.next()) |view| {
|
||||||
assert(view.inflight.output == output);
|
assert(view.inflight.output == output);
|
||||||
|
|
||||||
if (view.current.output != view.inflight.output or
|
|
||||||
(output.current.fullscreen == view and output.inflight.fullscreen != view))
|
|
||||||
{
|
|
||||||
if (view.inflight.float) {
|
if (view.inflight.float) {
|
||||||
view.tree.node.reparent(output.layers.float);
|
view.tree.node.reparent(output.layers.float);
|
||||||
} else {
|
} else {
|
||||||
view.tree.node.reparent(output.layers.layout);
|
view.tree.node.reparent(output.layers.layout);
|
||||||
}
|
}
|
||||||
view.popup_tree.node.reparent(output.layers.popups);
|
view.popup_tree.node.reparent(output.layers.popups);
|
||||||
}
|
|
||||||
|
|
||||||
if (view.current.float != view.inflight.float) {
|
|
||||||
if (view.inflight.float) {
|
|
||||||
view.tree.node.reparent(output.layers.float);
|
|
||||||
} else {
|
|
||||||
view.tree.node.reparent(output.layers.layout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
view.commitTransaction();
|
view.commitTransaction();
|
||||||
|
|
||||||
@ -706,7 +691,6 @@ fn commitTransaction(root: *Root) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output.inflight.fullscreen != output.current.fullscreen) {
|
|
||||||
if (output.inflight.fullscreen) |view| {
|
if (output.inflight.fullscreen) |view| {
|
||||||
assert(view.inflight.output == output);
|
assert(view.inflight.output == output);
|
||||||
assert(view.current.output == output);
|
assert(view.current.output == output);
|
||||||
@ -714,7 +698,6 @@ fn commitTransaction(root: *Root) void {
|
|||||||
}
|
}
|
||||||
output.current.fullscreen = output.inflight.fullscreen;
|
output.current.fullscreen = output.inflight.fullscreen;
|
||||||
output.layers.fullscreen.node.setEnabled(output.current.fullscreen != null);
|
output.layers.fullscreen.node.setEnabled(output.current.fullscreen != null);
|
||||||
}
|
|
||||||
|
|
||||||
output.status.handleTransactionCommit(output);
|
output.status.handleTransactionCommit(output);
|
||||||
}
|
}
|
||||||
|
@ -511,11 +511,11 @@ fn tryAddDevice(seat: *Seat, wlr_device: *wlr.InputDevice) !void {
|
|||||||
|
|
||||||
seat.cursor.wlr_cursor.attachInputDevice(wlr_device);
|
seat.cursor.wlr_cursor.attachInputDevice(wlr_device);
|
||||||
},
|
},
|
||||||
.tablet_tool => {
|
.tablet => {
|
||||||
try Tablet.create(seat, wlr_device);
|
try Tablet.create(seat, wlr_device);
|
||||||
seat.cursor.wlr_cursor.attachInputDevice(wlr_device);
|
seat.cursor.wlr_cursor.attachInputDevice(wlr_device);
|
||||||
},
|
},
|
||||||
.switch_device => {
|
.@"switch" => {
|
||||||
const switch_device = try util.gpa.create(Switch);
|
const switch_device = try util.gpa.create(Switch);
|
||||||
errdefer util.gpa.destroy(switch_device);
|
errdefer util.gpa.destroy(switch_device);
|
||||||
|
|
||||||
@ -538,7 +538,7 @@ pub fn updateCapabilities(seat: *Seat) void {
|
|||||||
switch (device.wlr_device.type) {
|
switch (device.wlr_device.type) {
|
||||||
.keyboard => capabilities.keyboard = true,
|
.keyboard => capabilities.keyboard = true,
|
||||||
.touch => capabilities.touch = true,
|
.touch => capabilities.touch = true,
|
||||||
.pointer, .switch_device, .tablet_tool => {},
|
.pointer, .@"switch", .tablet => {},
|
||||||
.tablet_pad => unreachable,
|
.tablet_pad => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
108
river/Server.zig
108
river/Server.zig
@ -38,6 +38,7 @@ const Root = @import("Root.zig");
|
|||||||
const Seat = @import("Seat.zig");
|
const Seat = @import("Seat.zig");
|
||||||
const SceneNodeData = @import("SceneNodeData.zig");
|
const SceneNodeData = @import("SceneNodeData.zig");
|
||||||
const StatusManager = @import("StatusManager.zig");
|
const StatusManager = @import("StatusManager.zig");
|
||||||
|
const TabletTool = @import("TabletTool.zig");
|
||||||
const XdgDecoration = @import("XdgDecoration.zig");
|
const XdgDecoration = @import("XdgDecoration.zig");
|
||||||
const XdgToplevel = @import("XdgToplevel.zig");
|
const XdgToplevel = @import("XdgToplevel.zig");
|
||||||
const XwaylandOverrideRedirect = @import("XwaylandOverrideRedirect.zig");
|
const XwaylandOverrideRedirect = @import("XwaylandOverrideRedirect.zig");
|
||||||
@ -83,6 +84,8 @@ screencopy_manager: *wlr.ScreencopyManagerV1,
|
|||||||
|
|
||||||
foreign_toplevel_manager: *wlr.ForeignToplevelManagerV1,
|
foreign_toplevel_manager: *wlr.ForeignToplevelManagerV1,
|
||||||
|
|
||||||
|
tearing_control_manager: *wlr.TearingControlManagerV1,
|
||||||
|
|
||||||
input_manager: InputManager,
|
input_manager: InputManager,
|
||||||
root: Root,
|
root: Root,
|
||||||
config: Config,
|
config: Config,
|
||||||
@ -96,8 +99,10 @@ xwayland: if (build_options.xwayland) ?*wlr.Xwayland else void = if (build_optio
|
|||||||
new_xwayland_surface: if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface) else void =
|
new_xwayland_surface: if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface) else void =
|
||||||
if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface).init(handleNewXwaylandSurface),
|
if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface).init(handleNewXwaylandSurface),
|
||||||
|
|
||||||
new_xdg_surface: wl.Listener(*wlr.XdgSurface) =
|
renderer_lost: wl.Listener(void) = wl.Listener(void).init(handleRendererLost),
|
||||||
wl.Listener(*wlr.XdgSurface).init(handleNewXdgSurface),
|
|
||||||
|
new_xdg_toplevel: wl.Listener(*wlr.XdgToplevel) =
|
||||||
|
wl.Listener(*wlr.XdgToplevel).init(handleNewXdgToplevel),
|
||||||
new_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) =
|
new_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) =
|
||||||
wl.Listener(*wlr.XdgToplevelDecorationV1).init(handleNewToplevelDecoration),
|
wl.Listener(*wlr.XdgToplevelDecorationV1).init(handleNewToplevelDecoration),
|
||||||
new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1) =
|
new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1) =
|
||||||
@ -113,14 +118,14 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
|
|||||||
// This keeps the code simpler and more readable.
|
// This keeps the code simpler and more readable.
|
||||||
|
|
||||||
const wl_server = try wl.Server.create();
|
const wl_server = try wl.Server.create();
|
||||||
|
const loop = wl_server.getEventLoop();
|
||||||
|
|
||||||
var session: ?*wlr.Session = undefined;
|
var session: ?*wlr.Session = undefined;
|
||||||
const backend = try wlr.Backend.autocreate(wl_server, &session);
|
const backend = try wlr.Backend.autocreate(loop, &session);
|
||||||
const renderer = try wlr.Renderer.autocreate(backend);
|
const renderer = try wlr.Renderer.autocreate(backend);
|
||||||
|
|
||||||
const compositor = try wlr.Compositor.create(wl_server, 6, renderer);
|
const compositor = try wlr.Compositor.create(wl_server, 6, renderer);
|
||||||
|
|
||||||
const loop = wl_server.getEventLoop();
|
|
||||||
server.* = .{
|
server.* = .{
|
||||||
.wl_server = wl_server,
|
.wl_server = wl_server,
|
||||||
.sigint_source = try loop.addSignal(*wl.Server, posix.SIG.INT, terminate, wl_server),
|
.sigint_source = try loop.addSignal(*wl.Server, posix.SIG.INT, terminate, wl_server),
|
||||||
@ -156,6 +161,8 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
|
|||||||
|
|
||||||
.foreign_toplevel_manager = try wlr.ForeignToplevelManagerV1.create(wl_server),
|
.foreign_toplevel_manager = try wlr.ForeignToplevelManagerV1.create(wl_server),
|
||||||
|
|
||||||
|
.tearing_control_manager = try wlr.TearingControlManagerV1.create(wl_server, 1),
|
||||||
|
|
||||||
.config = try Config.init(),
|
.config = try Config.init(),
|
||||||
|
|
||||||
.root = undefined,
|
.root = undefined,
|
||||||
@ -167,7 +174,7 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
|
|||||||
.lock_manager = undefined,
|
.lock_manager = undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (renderer.getDmabufFormats() != null and renderer.getDrmFd() >= 0) {
|
if (renderer.getTextureFormats(@intFromEnum(wlr.BufferCap.dmabuf)) != null) {
|
||||||
// wl_drm is a legacy interface and all clients should switch to linux_dmabuf.
|
// wl_drm is a legacy interface and all clients should switch to linux_dmabuf.
|
||||||
// However, enough widely used clients still rely on wl_drm that the pragmatic option
|
// However, enough widely used clients still rely on wl_drm that the pragmatic option
|
||||||
// is to keep it around for the near future.
|
// is to keep it around for the near future.
|
||||||
@ -190,7 +197,8 @@ pub fn init(server: *Server, runtime_xwayland: bool) !void {
|
|||||||
try server.idle_inhibit_manager.init();
|
try server.idle_inhibit_manager.init();
|
||||||
try server.lock_manager.init();
|
try server.lock_manager.init();
|
||||||
|
|
||||||
server.xdg_shell.events.new_surface.add(&server.new_xdg_surface);
|
server.renderer.events.lost.add(&server.renderer_lost);
|
||||||
|
server.xdg_shell.events.new_toplevel.add(&server.new_xdg_toplevel);
|
||||||
server.xdg_decoration_manager.events.new_toplevel_decoration.add(&server.new_toplevel_decoration);
|
server.xdg_decoration_manager.events.new_toplevel_decoration.add(&server.new_toplevel_decoration);
|
||||||
server.layer_shell.events.new_surface.add(&server.new_layer_surface);
|
server.layer_shell.events.new_surface.add(&server.new_layer_surface);
|
||||||
server.xdg_activation.events.request_activate.add(&server.request_activate);
|
server.xdg_activation.events.request_activate.add(&server.request_activate);
|
||||||
@ -204,7 +212,8 @@ pub fn deinit(server: *Server) void {
|
|||||||
server.sigint_source.remove();
|
server.sigint_source.remove();
|
||||||
server.sigterm_source.remove();
|
server.sigterm_source.remove();
|
||||||
|
|
||||||
server.new_xdg_surface.link.remove();
|
server.renderer_lost.link.remove();
|
||||||
|
server.new_xdg_toplevel.link.remove();
|
||||||
server.new_toplevel_decoration.link.remove();
|
server.new_toplevel_decoration.link.remove();
|
||||||
server.new_layer_surface.link.remove();
|
server.new_layer_surface.link.remove();
|
||||||
server.request_activate.link.remove();
|
server.request_activate.link.remove();
|
||||||
@ -277,14 +286,6 @@ fn globalFilter(client: *const wl.Client, global: *const wl.Global, server: *Ser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hackGlobal(ptr: *anyopaque) *wl.Global {
|
|
||||||
// TODO(wlroots) MR that eliminates the need for this hack:
|
|
||||||
// https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4612
|
|
||||||
if (wlr.version.major != 0 or wlr.version.minor != 17) @compileError("FIXME");
|
|
||||||
|
|
||||||
return @as(*extern struct { global: *wl.Global }, @alignCast(@ptrCast(ptr))).global;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the global is allowlisted for security contexts
|
/// Returns true if the global is allowlisted for security contexts
|
||||||
fn allowlist(server: *Server, global: *const wl.Global) bool {
|
fn allowlist(server: *Server, global: *const wl.Global) bool {
|
||||||
if (server.drm) |drm| if (global == drm.global) return true;
|
if (server.drm) |drm| if (global == drm.global) return true;
|
||||||
@ -300,8 +301,8 @@ fn allowlist(server: *Server, global: *const wl.Global) bool {
|
|||||||
// with an assertion failure.
|
// with an assertion failure.
|
||||||
return global.getInterface() == wl.Output.getInterface() or
|
return global.getInterface() == wl.Output.getInterface() or
|
||||||
global.getInterface() == wl.Seat.getInterface() or
|
global.getInterface() == wl.Seat.getInterface() or
|
||||||
global == hackGlobal(server.shm) or
|
global == server.shm.global or
|
||||||
global == hackGlobal(server.single_pixel_buffer_manager) or
|
global == server.single_pixel_buffer_manager.global or
|
||||||
global == server.viewporter.global or
|
global == server.viewporter.global or
|
||||||
global == server.fractional_scale_manager.global or
|
global == server.fractional_scale_manager.global or
|
||||||
global == server.compositor.global or
|
global == server.compositor.global or
|
||||||
@ -319,7 +320,8 @@ fn allowlist(server: *Server, global: *const wl.Global) bool {
|
|||||||
global == server.input_manager.text_input_manager.global or
|
global == server.input_manager.text_input_manager.global or
|
||||||
global == server.input_manager.tablet_manager.global or
|
global == server.input_manager.tablet_manager.global or
|
||||||
global == server.input_manager.pointer_gestures.global or
|
global == server.input_manager.pointer_gestures.global or
|
||||||
global == server.idle_inhibit_manager.wlr_manager.global;
|
global == server.idle_inhibit_manager.wlr_manager.global or
|
||||||
|
global == server.tearing_control_manager.global;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the global is blocked for security contexts
|
/// Returns true if the global is blocked for security contexts
|
||||||
@ -336,7 +338,7 @@ fn blocklist(server: *Server, global: *const wl.Global) bool {
|
|||||||
global == server.root.output_manager.global or
|
global == server.root.output_manager.global or
|
||||||
global == server.root.power_manager.global or
|
global == server.root.power_manager.global or
|
||||||
global == server.root.gamma_control_manager.global or
|
global == server.root.gamma_control_manager.global or
|
||||||
global == hackGlobal(server.input_manager.idle_notifier) or
|
global == server.input_manager.idle_notifier.global or
|
||||||
global == server.input_manager.virtual_pointer_manager.global or
|
global == server.input_manager.virtual_pointer_manager.global or
|
||||||
global == server.input_manager.virtual_keyboard_manager.global or
|
global == server.input_manager.virtual_keyboard_manager.global or
|
||||||
global == server.input_manager.input_method_manager.global or
|
global == server.input_manager.input_method_manager.global or
|
||||||
@ -349,17 +351,55 @@ fn terminate(_: c_int, wl_server: *wl.Server) c_int {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleNewXdgSurface(_: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
|
fn handleRendererLost(listener: *wl.Listener(void)) void {
|
||||||
if (xdg_surface.role == .popup) {
|
const server: *Server = @fieldParentPtr("renderer_lost", listener);
|
||||||
log.debug("new xdg_popup", .{});
|
|
||||||
return;
|
log.info("recovering from GPU reset", .{});
|
||||||
|
|
||||||
|
// There's not much that can be done if creating a new renderer or allocator fails.
|
||||||
|
// With luck there might be another GPU reset after which we try again and succeed.
|
||||||
|
|
||||||
|
server.recoverFromGpuReset() catch |err| switch (err) {
|
||||||
|
error.RendererCreateFailed => log.err("failed to create new renderer after GPU reset", .{}),
|
||||||
|
error.AllocatorCreateFailed => log.err("failed to create new allocator after GPU reset", .{}),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn recoverFromGpuReset(server: *Server) !void {
|
||||||
|
const new_renderer = try wlr.Renderer.autocreate(server.backend);
|
||||||
|
errdefer new_renderer.destroy();
|
||||||
|
|
||||||
|
const new_allocator = try wlr.Allocator.autocreate(server.backend, new_renderer);
|
||||||
|
errdefer comptime unreachable; // no failure allowed after this point
|
||||||
|
|
||||||
|
server.renderer_lost.link.remove();
|
||||||
|
new_renderer.events.lost.add(&server.renderer_lost);
|
||||||
|
|
||||||
|
server.compositor.setRenderer(new_renderer);
|
||||||
|
|
||||||
|
{
|
||||||
|
var it = server.root.all_outputs.iterator(.forward);
|
||||||
|
while (it.next()) |output| {
|
||||||
|
// This should never fail here as failure with this combination of
|
||||||
|
// renderer, allocator, and backend should have prevented creating
|
||||||
|
// the output in the first place.
|
||||||
|
_ = output.wlr_output.initRender(new_allocator, new_renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server.renderer.destroy();
|
||||||
|
server.renderer = new_renderer;
|
||||||
|
|
||||||
|
server.allocator.destroy();
|
||||||
|
server.allocator = new_allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handleNewXdgToplevel(_: *wl.Listener(*wlr.XdgToplevel), xdg_toplevel: *wlr.XdgToplevel) void {
|
||||||
log.debug("new xdg_toplevel", .{});
|
log.debug("new xdg_toplevel", .{});
|
||||||
|
|
||||||
XdgToplevel.create(xdg_surface.role_data.toplevel.?) catch {
|
XdgToplevel.create(xdg_toplevel) catch {
|
||||||
log.err("out of memory", .{});
|
log.err("out of memory", .{});
|
||||||
xdg_surface.resource.postNoMemory();
|
xdg_toplevel.resource.postNoMemory();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -450,17 +490,27 @@ fn handleRequestSetCursorShape(
|
|||||||
_: *wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape),
|
_: *wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape),
|
||||||
event: *wlr.CursorShapeManagerV1.event.RequestSetShape,
|
event: *wlr.CursorShapeManagerV1.event.RequestSetShape,
|
||||||
) void {
|
) void {
|
||||||
// Ignore requests to set a tablet tool's cursor shape for now
|
const seat: *Seat = @ptrFromInt(event.seat_client.seat.data);
|
||||||
// TODO(wlroots): https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/3821
|
|
||||||
if (event.device_type == .tablet_tool) return;
|
if (event.tablet_tool) |wp_tool| {
|
||||||
|
assert(event.device_type == .tablet_tool);
|
||||||
|
|
||||||
|
const tool = TabletTool.get(event.seat_client.seat, wp_tool.wlr_tool) catch return;
|
||||||
|
|
||||||
|
if (tool.allowSetCursor(event.seat_client, event.serial)) {
|
||||||
|
const name = wlr.CursorShapeManagerV1.shapeName(event.shape);
|
||||||
|
tool.wlr_cursor.setXcursor(seat.cursor.xcursor_manager, name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert(event.device_type == .pointer);
|
||||||
|
|
||||||
const focused_client = event.seat_client.seat.pointer_state.focused_client;
|
const focused_client = event.seat_client.seat.pointer_state.focused_client;
|
||||||
|
|
||||||
// This can be sent by any client, so we check to make sure this one is
|
// This can be sent by any client, so we check to make sure this one is
|
||||||
// actually has pointer focus first.
|
// actually has pointer focus first.
|
||||||
if (focused_client == event.seat_client) {
|
if (focused_client == event.seat_client) {
|
||||||
const seat: *Seat = @ptrFromInt(event.seat_client.seat.data);
|
|
||||||
const name = wlr.CursorShapeManagerV1.shapeName(event.shape);
|
const name = wlr.CursorShapeManagerV1.shapeName(event.shape);
|
||||||
seat.cursor.setXcursor(name);
|
seat.cursor.setXcursor(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -33,7 +33,7 @@ wp_tablet: *wlr.TabletV2Tablet,
|
|||||||
output_mapping: ?*wlr.Output = null,
|
output_mapping: ?*wlr.Output = null,
|
||||||
|
|
||||||
pub fn create(seat: *Seat, wlr_device: *wlr.InputDevice) !void {
|
pub fn create(seat: *Seat, wlr_device: *wlr.InputDevice) !void {
|
||||||
assert(wlr_device.type == .tablet_tool);
|
assert(wlr_device.type == .tablet);
|
||||||
|
|
||||||
const tablet = try util.gpa.create(Tablet);
|
const tablet = try util.gpa.create(Tablet);
|
||||||
errdefer util.gpa.destroy(tablet);
|
errdefer util.gpa.destroy(tablet);
|
||||||
|
@ -102,25 +102,30 @@ fn handleDestroy(listener: *wl.Listener(*wlr.TabletTool), _: *wlr.TabletTool) vo
|
|||||||
util.gpa.destroy(tool);
|
util.gpa.destroy(tool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn allowSetCursor(tool: *TabletTool, seat_client: *wlr.Seat.Client, serial: u32) bool {
|
||||||
|
if (tool.wp_tool.focused_surface == null or
|
||||||
|
tool.wp_tool.focused_surface.?.resource.getClient() != seat_client.client)
|
||||||
|
{
|
||||||
|
log.debug("client tried to set cursor without focus", .{});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (serial != tool.wp_tool.proximity_serial) {
|
||||||
|
log.debug("focused client tried to set cursor with incorrect serial", .{});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
fn handleSetCursor(
|
fn handleSetCursor(
|
||||||
listener: *wl.Listener(*wlr.TabletV2TabletTool.event.SetCursor),
|
listener: *wl.Listener(*wlr.TabletV2TabletTool.event.SetCursor),
|
||||||
event: *wlr.TabletV2TabletTool.event.SetCursor,
|
event: *wlr.TabletV2TabletTool.event.SetCursor,
|
||||||
) void {
|
) void {
|
||||||
const tool: *TabletTool = @fieldParentPtr("set_cursor", listener);
|
const tool: *TabletTool = @fieldParentPtr("set_cursor", listener);
|
||||||
|
|
||||||
if (tool.wp_tool.focused_surface == null or
|
if (tool.allowSetCursor(event.seat_client, event.serial)) {
|
||||||
tool.wp_tool.focused_surface.?.resource.getClient() != event.seat_client.client)
|
|
||||||
{
|
|
||||||
log.debug("client tried to set cursor without focus", .{});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (event.serial != tool.wp_tool.proximity_serial) {
|
|
||||||
log.debug("focused client tried to set cursor with incorrect serial", .{});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tool.wlr_cursor.setSurface(event.surface, event.hotspot_x, event.hotspot_y);
|
tool.wlr_cursor.setSurface(event.surface, event.hotspot_x, event.hotspot_y);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn axis(tool: *TabletTool, tablet: *Tablet, event: *wlr.Tablet.event.Axis) void {
|
pub fn axis(tool: *TabletTool, tablet: *Tablet, event: *wlr.Tablet.event.Axis) void {
|
||||||
tool.wlr_cursor.attachInputDevice(tablet.device.wlr_device);
|
tool.wlr_cursor.attachInputDevice(tablet.device.wlr_device);
|
||||||
|
@ -23,6 +23,7 @@ const math = std.math;
|
|||||||
const posix = std.posix;
|
const posix = std.posix;
|
||||||
const wlr = @import("wlroots");
|
const wlr = @import("wlroots");
|
||||||
const wl = @import("wayland").server.wl;
|
const wl = @import("wayland").server.wl;
|
||||||
|
const wp = @import("wayland").server.wp;
|
||||||
|
|
||||||
const server = &@import("main.zig").server;
|
const server = &@import("main.zig").server;
|
||||||
const util = @import("util.zig");
|
const util = @import("util.zig");
|
||||||
@ -59,6 +60,12 @@ const AttachRelativeMode = enum {
|
|||||||
below,
|
below,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const TearingMode = enum {
|
||||||
|
no_tearing,
|
||||||
|
tearing,
|
||||||
|
window_hint,
|
||||||
|
};
|
||||||
|
|
||||||
pub const State = struct {
|
pub const State = struct {
|
||||||
/// The output the view is currently assigned to.
|
/// The output the view is currently assigned to.
|
||||||
/// May be null if there are no outputs or for newly created views.
|
/// May be null if there are no outputs or for newly created views.
|
||||||
@ -177,6 +184,8 @@ foreign_toplevel_handle: ForeignToplevelHandle = .{},
|
|||||||
/// Connector name of the output this view occupied before an evacuation.
|
/// Connector name of the output this view occupied before an evacuation.
|
||||||
output_before_evac: ?[]const u8 = null,
|
output_before_evac: ?[]const u8 = null,
|
||||||
|
|
||||||
|
tearing_mode: TearingMode = .window_hint,
|
||||||
|
|
||||||
pub fn create(impl: Impl) error{OutOfMemory}!*View {
|
pub fn create(impl: Impl) error{OutOfMemory}!*View {
|
||||||
assert(impl != .none);
|
assert(impl != .none);
|
||||||
|
|
||||||
@ -572,6 +581,22 @@ pub fn getAppId(view: View) ?[*:0]const u8 {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true if tearing should be allowed for the view.
|
||||||
|
pub fn allowTearing(view: *View) bool {
|
||||||
|
switch (view.tearing_mode) {
|
||||||
|
.no_tearing => return false,
|
||||||
|
.tearing => return true,
|
||||||
|
.window_hint => {
|
||||||
|
if (server.config.allow_tearing) {
|
||||||
|
if (view.rootSurface()) |root_surface| {
|
||||||
|
return server.tearing_control_manager.hintFromSurface(root_surface) == .@"async";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Clamp the width/height of the box to the constraints of the view
|
/// Clamp the width/height of the box to the constraints of the view
|
||||||
pub fn applyConstraints(view: *View, box: *wlr.Box) void {
|
pub fn applyConstraints(view: *View, box: *wlr.Box) void {
|
||||||
box.width = math.clamp(box.width, view.constraints.min_width, view.constraints.max_width);
|
box.width = math.clamp(box.width, view.constraints.min_width, view.constraints.max_width);
|
||||||
@ -640,6 +665,10 @@ pub fn map(view: *View) !void {
|
|||||||
view.pending.ssd = ssd;
|
view.pending.ssd = ssd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (server.config.rules.tearing.match(view)) |tearing| {
|
||||||
|
view.tearing_mode = if (tearing) .tearing else .no_tearing;
|
||||||
|
}
|
||||||
|
|
||||||
if (server.config.rules.dimensions.match(view)) |dimensions| {
|
if (server.config.rules.dimensions.match(view)) |dimensions| {
|
||||||
view.pending.box.width = dimensions.width;
|
view.pending.box.width = dimensions.width;
|
||||||
view.pending.box.height = dimensions.height;
|
view.pending.box.height = dimensions.height;
|
||||||
|
@ -42,14 +42,9 @@ pub fn init(wlr_decoration: *wlr.XdgToplevelDecorationV1) void {
|
|||||||
wlr_decoration.events.destroy.add(&decoration.destroy);
|
wlr_decoration.events.destroy.add(&decoration.destroy);
|
||||||
wlr_decoration.events.request_mode.add(&decoration.request_mode);
|
wlr_decoration.events.request_mode.add(&decoration.request_mode);
|
||||||
|
|
||||||
const ssd = server.config.rules.ssd.match(toplevel.view) orelse
|
if (toplevel.wlr_toplevel.base.initialized) {
|
||||||
(decoration.wlr_decoration.requested_mode != .client_side);
|
handleRequestMode(&decoration.request_mode, wlr_decoration);
|
||||||
|
}
|
||||||
// TODO(wlroots): make sure this is properly batched in a single configure
|
|
||||||
// with all other initial state when wlroots makes this possible.
|
|
||||||
_ = wlr_decoration.setMode(if (ssd) .server_side else .client_side);
|
|
||||||
|
|
||||||
toplevel.view.pending.ssd = ssd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(decoration: *XdgDecoration) void {
|
pub fn deinit(decoration: *XdgDecoration) void {
|
||||||
|
@ -54,7 +54,7 @@ pub fn create(
|
|||||||
.tree = try parent.createSceneXdgSurface(wlr_xdg_popup.base),
|
.tree = try parent.createSceneXdgSurface(wlr_xdg_popup.base),
|
||||||
};
|
};
|
||||||
|
|
||||||
wlr_xdg_popup.base.events.destroy.add(&xdg_popup.destroy);
|
wlr_xdg_popup.events.destroy.add(&xdg_popup.destroy);
|
||||||
wlr_xdg_popup.base.surface.events.commit.add(&xdg_popup.commit);
|
wlr_xdg_popup.base.surface.events.commit.add(&xdg_popup.commit);
|
||||||
wlr_xdg_popup.base.events.new_popup.add(&xdg_popup.new_popup);
|
wlr_xdg_popup.base.events.new_popup.add(&xdg_popup.new_popup);
|
||||||
wlr_xdg_popup.events.reposition.add(&xdg_popup.reposition);
|
wlr_xdg_popup.events.reposition.add(&xdg_popup.reposition);
|
||||||
|
@ -62,12 +62,12 @@ configure_state: union(enum) {
|
|||||||
destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy),
|
destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy),
|
||||||
map: wl.Listener(void) = wl.Listener(void).init(handleMap),
|
map: wl.Listener(void) = wl.Listener(void).init(handleMap),
|
||||||
unmap: wl.Listener(void) = wl.Listener(void).init(handleUnmap),
|
unmap: wl.Listener(void) = wl.Listener(void).init(handleUnmap),
|
||||||
|
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
|
||||||
new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
|
new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
|
||||||
|
|
||||||
// Listeners that are only active while the view is mapped
|
// Listeners that are only active while the view is mapped
|
||||||
ack_configure: wl.Listener(*wlr.XdgSurface.Configure) =
|
ack_configure: wl.Listener(*wlr.XdgSurface.Configure) =
|
||||||
wl.Listener(*wlr.XdgSurface.Configure).init(handleAckConfigure),
|
wl.Listener(*wlr.XdgSurface.Configure).init(handleAckConfigure),
|
||||||
commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit),
|
|
||||||
request_fullscreen: wl.Listener(void) = wl.Listener(void).init(handleRequestFullscreen),
|
request_fullscreen: wl.Listener(void) = wl.Listener(void).init(handleRequestFullscreen),
|
||||||
request_move: wl.Listener(*wlr.XdgToplevel.event.Move) =
|
request_move: wl.Listener(*wlr.XdgToplevel.event.Move) =
|
||||||
wl.Listener(*wlr.XdgToplevel.event.Move).init(handleRequestMove),
|
wl.Listener(*wlr.XdgToplevel.event.Move).init(handleRequestMove),
|
||||||
@ -104,11 +104,10 @@ pub fn create(wlr_toplevel: *wlr.XdgToplevel) error{OutOfMemory}!void {
|
|||||||
wlr_toplevel.base.surface.data = @intFromPtr(&view.tree.node);
|
wlr_toplevel.base.surface.data = @intFromPtr(&view.tree.node);
|
||||||
|
|
||||||
// Add listeners that are active over the toplevel's entire lifetime
|
// Add listeners that are active over the toplevel's entire lifetime
|
||||||
wlr_toplevel.base.events.destroy.add(&toplevel.destroy);
|
wlr_toplevel.events.destroy.add(&toplevel.destroy);
|
||||||
wlr_toplevel.base.surface.events.map.add(&toplevel.map);
|
wlr_toplevel.base.surface.events.map.add(&toplevel.map);
|
||||||
|
wlr_toplevel.base.surface.events.commit.add(&toplevel.commit);
|
||||||
wlr_toplevel.base.events.new_popup.add(&toplevel.new_popup);
|
wlr_toplevel.base.events.new_popup.add(&toplevel.new_popup);
|
||||||
|
|
||||||
_ = wlr_toplevel.setWmCapabilities(.{ .fullscreen = true });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a configure event, applying the inflight state of the view.
|
/// Send a configure event, applying the inflight state of the view.
|
||||||
@ -213,8 +212,10 @@ fn handleDestroy(listener: *wl.Listener(void)) void {
|
|||||||
toplevel.destroy.link.remove();
|
toplevel.destroy.link.remove();
|
||||||
toplevel.map.link.remove();
|
toplevel.map.link.remove();
|
||||||
toplevel.unmap.link.remove();
|
toplevel.unmap.link.remove();
|
||||||
|
toplevel.commit.link.remove();
|
||||||
|
toplevel.new_popup.link.remove();
|
||||||
|
|
||||||
// The wlr_surface may outlive the wlr_xdg_surface so we must clean up the user data.
|
// The wlr_surface may outlive the wlr_xdg_toplevel so we must clean up the user data.
|
||||||
toplevel.wlr_toplevel.base.surface.data = 0;
|
toplevel.wlr_toplevel.base.surface.data = 0;
|
||||||
|
|
||||||
const view = toplevel.view;
|
const view = toplevel.view;
|
||||||
@ -228,7 +229,6 @@ fn handleMap(listener: *wl.Listener(void)) void {
|
|||||||
|
|
||||||
// Add listeners that are only active while mapped
|
// Add listeners that are only active while mapped
|
||||||
toplevel.wlr_toplevel.base.events.ack_configure.add(&toplevel.ack_configure);
|
toplevel.wlr_toplevel.base.events.ack_configure.add(&toplevel.ack_configure);
|
||||||
toplevel.wlr_toplevel.base.surface.events.commit.add(&toplevel.commit);
|
|
||||||
toplevel.wlr_toplevel.events.request_fullscreen.add(&toplevel.request_fullscreen);
|
toplevel.wlr_toplevel.events.request_fullscreen.add(&toplevel.request_fullscreen);
|
||||||
toplevel.wlr_toplevel.events.request_move.add(&toplevel.request_move);
|
toplevel.wlr_toplevel.events.request_move.add(&toplevel.request_move);
|
||||||
toplevel.wlr_toplevel.events.request_resize.add(&toplevel.request_resize);
|
toplevel.wlr_toplevel.events.request_resize.add(&toplevel.request_resize);
|
||||||
@ -270,7 +270,6 @@ fn handleUnmap(listener: *wl.Listener(void)) void {
|
|||||||
|
|
||||||
// Remove listeners that are only active while mapped
|
// Remove listeners that are only active while mapped
|
||||||
toplevel.ack_configure.link.remove();
|
toplevel.ack_configure.link.remove();
|
||||||
toplevel.commit.link.remove();
|
|
||||||
toplevel.request_fullscreen.link.remove();
|
toplevel.request_fullscreen.link.remove();
|
||||||
toplevel.request_move.link.remove();
|
toplevel.request_move.link.remove();
|
||||||
toplevel.request_resize.link.remove();
|
toplevel.request_resize.link.remove();
|
||||||
@ -309,6 +308,23 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
|
|||||||
const toplevel: *XdgToplevel = @fieldParentPtr("commit", listener);
|
const toplevel: *XdgToplevel = @fieldParentPtr("commit", listener);
|
||||||
const view = toplevel.view;
|
const view = toplevel.view;
|
||||||
|
|
||||||
|
if (toplevel.wlr_toplevel.base.initial_commit) {
|
||||||
|
_ = toplevel.wlr_toplevel.setWmCapabilities(.{ .fullscreen = true });
|
||||||
|
|
||||||
|
if (toplevel.decoration) |decoration| {
|
||||||
|
const ssd = server.config.rules.ssd.match(toplevel.view) orelse
|
||||||
|
(decoration.wlr_decoration.requested_mode != .client_side);
|
||||||
|
_ = decoration.wlr_decoration.setMode(if (ssd) .server_side else .client_side);
|
||||||
|
toplevel.view.pending.ssd = ssd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!view.mapped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const state = &toplevel.wlr_toplevel.current;
|
const state = &toplevel.wlr_toplevel.current;
|
||||||
view.constraints = .{
|
view.constraints = .{
|
||||||
|
@ -41,6 +41,7 @@ const command_impls = std.StaticStringMap(
|
|||||||
).initComptime(
|
).initComptime(
|
||||||
.{
|
.{
|
||||||
// zig fmt: off
|
// zig fmt: off
|
||||||
|
.{ "allow-tearing", @import("command/config.zig").allowTearing },
|
||||||
.{ "attach-mode", @import("command/attach_mode.zig").defaultAttachMode },
|
.{ "attach-mode", @import("command/attach_mode.zig").defaultAttachMode },
|
||||||
.{ "background-color", @import("command/config.zig").backgroundColor },
|
.{ "background-color", @import("command/config.zig").backgroundColor },
|
||||||
.{ "border-color-focused", @import("command/config.zig").borderColorFocused },
|
.{ "border-color-focused", @import("command/config.zig").borderColorFocused },
|
||||||
|
@ -24,6 +24,20 @@ const Error = @import("../command.zig").Error;
|
|||||||
const Seat = @import("../Seat.zig");
|
const Seat = @import("../Seat.zig");
|
||||||
const Config = @import("../Config.zig");
|
const Config = @import("../Config.zig");
|
||||||
|
|
||||||
|
pub fn allowTearing(
|
||||||
|
_: *Seat,
|
||||||
|
args: []const [:0]const u8,
|
||||||
|
_: *?[]const u8,
|
||||||
|
) Error!void {
|
||||||
|
if (args.len < 2) return Error.NotEnoughArguments;
|
||||||
|
if (args.len > 2) return Error.TooManyArguments;
|
||||||
|
|
||||||
|
const arg = std.meta.stringToEnum(enum { enabled, disabled }, args[1]) orelse
|
||||||
|
return Error.UnknownOption;
|
||||||
|
|
||||||
|
server.config.allow_tearing = arg == .enabled;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn borderWidth(
|
pub fn borderWidth(
|
||||||
_: *Seat,
|
_: *Seat,
|
||||||
args: []const [:0]const u8,
|
args: []const [:0]const u8,
|
||||||
|
@ -38,6 +38,8 @@ const Action = enum {
|
|||||||
dimensions,
|
dimensions,
|
||||||
fullscreen,
|
fullscreen,
|
||||||
@"no-fullscreen",
|
@"no-fullscreen",
|
||||||
|
tearing,
|
||||||
|
@"no-tearing",
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void {
|
pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void {
|
||||||
@ -53,7 +55,7 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
|
|||||||
const action = std.meta.stringToEnum(Action, result.args[0]) orelse return Error.UnknownOption;
|
const action = std.meta.stringToEnum(Action, result.args[0]) orelse return Error.UnknownOption;
|
||||||
|
|
||||||
const positional_arguments_count: u8 = switch (action) {
|
const positional_arguments_count: u8 = switch (action) {
|
||||||
.float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen" => 1,
|
.float, .@"no-float", .ssd, .csd, .fullscreen, .@"no-fullscreen", .tearing, .@"no-tearing" => 1,
|
||||||
.tags, .output => 2,
|
.tags, .output => 2,
|
||||||
.position, .dimensions => 3,
|
.position, .dimensions => 3,
|
||||||
};
|
};
|
||||||
@ -83,6 +85,14 @@ pub fn ruleAdd(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
|
|||||||
apply_ssd_rules();
|
apply_ssd_rules();
|
||||||
server.root.applyPending();
|
server.root.applyPending();
|
||||||
},
|
},
|
||||||
|
.tearing, .@"no-tearing" => {
|
||||||
|
try server.config.rules.tearing.add(.{
|
||||||
|
.app_id_glob = app_id_glob,
|
||||||
|
.title_glob = title_glob,
|
||||||
|
.value = (action == .tearing),
|
||||||
|
});
|
||||||
|
apply_tearing_rules();
|
||||||
|
},
|
||||||
.tags => {
|
.tags => {
|
||||||
const tags = try fmt.parseInt(u32, result.args[1], 10);
|
const tags = try fmt.parseInt(u32, result.args[1], 10);
|
||||||
try server.config.rules.tags.add(.{
|
try server.config.rules.tags.add(.{
|
||||||
@ -177,6 +187,10 @@ pub fn ruleDel(_: *Seat, args: []const [:0]const u8, _: *?[]const u8) Error!void
|
|||||||
.fullscreen, .@"no-fullscreen" => {
|
.fullscreen, .@"no-fullscreen" => {
|
||||||
_ = server.config.rules.fullscreen.del(rule);
|
_ = server.config.rules.fullscreen.del(rule);
|
||||||
},
|
},
|
||||||
|
.tearing, .@"no-tearing" => {
|
||||||
|
_ = server.config.rules.tearing.del(rule);
|
||||||
|
apply_tearing_rules();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,6 +205,17 @@ fn apply_ssd_rules() void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn apply_tearing_rules() void {
|
||||||
|
var it = server.root.views.iterator(.forward);
|
||||||
|
while (it.next()) |view| {
|
||||||
|
if (view.destroying) continue;
|
||||||
|
|
||||||
|
if (server.config.rules.tearing.match(view)) |tearing| {
|
||||||
|
view.tearing_mode = if (tearing) .tearing else .no_tearing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!void {
|
pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!void {
|
||||||
if (args.len < 2) return error.NotEnoughArguments;
|
if (args.len < 2) return error.NotEnoughArguments;
|
||||||
if (args.len > 2) return error.TooManyArguments;
|
if (args.len > 2) return error.TooManyArguments;
|
||||||
@ -203,6 +228,7 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!
|
|||||||
position,
|
position,
|
||||||
dimensions,
|
dimensions,
|
||||||
fullscreen,
|
fullscreen,
|
||||||
|
tearing,
|
||||||
}, args[1]) orelse return Error.UnknownOption;
|
}, args[1]) orelse return Error.UnknownOption;
|
||||||
const max_glob_len = switch (rule_list) {
|
const max_glob_len = switch (rule_list) {
|
||||||
inline else => |list| @field(server.config.rules, @tagName(list)).getMaxGlobLen(),
|
inline else => |list| @field(server.config.rules, @tagName(list)).getMaxGlobLen(),
|
||||||
@ -218,12 +244,13 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!
|
|||||||
try writer.writeAll("action\n");
|
try writer.writeAll("action\n");
|
||||||
|
|
||||||
switch (rule_list) {
|
switch (rule_list) {
|
||||||
inline .float, .ssd, .output, .fullscreen => |list| {
|
inline .float, .ssd, .output, .fullscreen, .tearing => |list| {
|
||||||
const rules = switch (list) {
|
const rules = switch (list) {
|
||||||
.float => server.config.rules.float.rules.items,
|
.float => server.config.rules.float.rules.items,
|
||||||
.ssd => server.config.rules.ssd.rules.items,
|
.ssd => server.config.rules.ssd.rules.items,
|
||||||
.output => server.config.rules.output.rules.items,
|
.output => server.config.rules.output.rules.items,
|
||||||
.fullscreen => server.config.rules.fullscreen.rules.items,
|
.fullscreen => server.config.rules.fullscreen.rules.items,
|
||||||
|
.tearing => server.config.rules.tearing.rules.items,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
for (rules) |rule| {
|
for (rules) |rule| {
|
||||||
@ -234,6 +261,7 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!
|
|||||||
.ssd => if (rule.value) "ssd" else "csd",
|
.ssd => if (rule.value) "ssd" else "csd",
|
||||||
.output => rule.value,
|
.output => rule.value,
|
||||||
.fullscreen => if (rule.value) "fullscreen" else "no-fullscreen",
|
.fullscreen => if (rule.value) "fullscreen" else "no-fullscreen",
|
||||||
|
.tearing => if (rule.value) "tearing" else "no-tearing",
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
@ -31,12 +31,6 @@ const process = @import("process.zig");
|
|||||||
|
|
||||||
const Server = @import("Server.zig");
|
const Server = @import("Server.zig");
|
||||||
|
|
||||||
comptime {
|
|
||||||
if (wlr.version.major != 0 or wlr.version.minor != 17 or wlr.version.micro < 2) {
|
|
||||||
@compileError("river requires at least wlroots version 0.17.2 due to bugs in wlroots 0.17.0/0.17.1");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const usage: []const u8 =
|
const usage: []const u8 =
|
||||||
\\usage: river [options]
|
\\usage: river [options]
|
||||||
\\
|
\\
|
||||||
|
Loading…
Reference in New Issue
Block a user