diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 33cb225e..4954400f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -98,7 +98,7 @@ Some distributions carry labwc in their repositories or user repositories.
- @adcdam (Slackware)
- @bdantas (Tiny Core Linux)
- @Visone-Selektah (Venom Linux)
-- @tranzystorek-io (Void Linux)
+- @tranzystorekk (Void Linux)
kindly maintain the packages in their respective distro.
@@ -350,19 +350,32 @@ Base both bugfixes and new features on `master`.
# Native Language Support
+## Translators
+
+### Weblate Instance
+
+Translators can create an account at [LXQt Weblate](https://translate.lxqt-project.org/projects/labwc/labwc/)
+and use the web interface. Adding new languages should work, otherwise the
+administrators can be contacted. Suggestions for improving existing translations
+can be added without account.
+
+### Github Pull Request
+
Translators can add their `MY_LOCALE.po` files to the `po` directory
based on `po/labwc.pot` and issue a pull request. To do this they can
generate their `MY_LOCALE.po` file in a few steps:
-1. Edit the `po/LINGUAS` file to add their locale name by adding a space
- to the end of the field and typing the locale code.
-2. Copy the po/labwc.pot to po/MY_LOCALE.po
-3. Edit the newly generated MY_LOCALE.po file with some of their
+1. Edit the `po/LINGUAS` file to add their locale code in English
+ alphabetical order to the field of locale codes.
+2. Copy the `po/labwc.pot` to `po/MY_LOCALE.po`
+3. Edit the newly generated `MY_LOCALE.po` file with some of their
contact and locale details in the header of the file then add the
translation strings under each English string.
[See this tutorial for further guidance](https://www.labri.fr/perso/fleury/posts/programming/a-quick-gettext-tutorial.html)
+## Coders
+
Code contributors may need to update relevant files if their additions
affect UI elements (at the moment only `src/menu/menu.c`). In this case
the `po/labwc.pot` file needs to be updated so that translators can
diff --git a/NEWS.md b/NEWS.md
index 11c0cebd..dbafc05f 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -9,7 +9,7 @@ The format is based on [Keep a Changelog]
| Date | All Changes | wlroots version | lines-of-code |
|------------|---------------|-----------------|---------------|
-| 2024-01-23 | [unreleased] | 0.17.1 | |
+| 2024-03-01 | [0.7.1] | 0.17.1 | 18624 |
| 2023-12-22 | [0.7.0] | 0.17.1 | 16576 |
| 2023-11-25 | [0.6.6] | 0.16.2 | 15796 |
| 2023-09-23 | [0.6.5] | 0.16.2 | 14809 |
@@ -28,10 +28,33 @@ The format is based on [Keep a Changelog]
| 2021-03-05 | [0.1.0] | 0.12.0 | 4627 |
-## [unreleased]
+## [0.7.1]
### Added
+- Support libinput option sendEventsMode to allow enabling/disabling devices.
+ Co-Authored-By: @Sachin-Bhat
+
+```xml
+
+
+ yes|no|disabledOnExternalMouse
+
+
+```
+
+- Add click method libinput option. Written-by: @datMaffin
+
+```xml
+
+
+ none|buttonAreas|clickfinger
+
+
+```
+
+- Add `data/labwc.svg` & `data/labwc-symbolic.svg`, and specify icon name
+ in labwc.desktop to enable Display Managers to show an icons for labwc.
- Expose output configuration test to clients. For example, this enables
`wlr-randr --dryrun`
- Add window-edge resistance for interactive moves/resizes and support negative
@@ -90,6 +113,7 @@ The format is based on [Keep a Changelog]
- Add config option `` with supported values `center`,
`cursor` and `automatic`. The latter minimizes overlap with other windows
already on screen and is similar to Openbox's smart window placement.
+ The placement policies honour ``.
Written-by: @ahesford #1312
```xml
@@ -100,6 +124,13 @@ The format is based on [Keep a Changelog]
### Fixed
+- Delay popup-unconstrain until after first commit in response to a changed
+ wlroots 0.17 interface and to get rid of the error message below. Issue #1372
+
+ [types/xdg_shell/wlr_xdg_surface.c:169] A configure is scheduled for an uninitialized xdg_surface
+
+- Notify clients about configuration errors when changing output settings.
+ Issue #1528.
- Fix output configuration bug causing compositor crash when refresh rate is
zero. Issue #1458
- Fix disappearing cursor bug on view destruction. Issue #1393
@@ -124,6 +155,8 @@ The format is based on [Keep a Changelog]
### Changed
+- Make `MoveToCursor` honour ``. Issue #1494
+- Add `Roll Up/Down` client-menu entry for `ToggleShade`
- When a Wayland-native window is snapped to a screen edges or user-defined
region, labwc will notify the application that it is "tiled", allowing the
application to better adapt its rendering to constrained layouts. Windows
@@ -1043,7 +1076,8 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
ShowMenu
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
-[unreleased]: https://github.com/labwc/labwc/compare/0.7.0...HEAD
+[unreleased]: https://github.com/labwc/labwc/compare/0.7.1...HEAD
+[0.7.1]: https://github.com/labwc/labwc/compare/0.7.0...0.7.1
[0.7.0]: https://github.com/labwc/labwc/compare/0.6.6...0.7.0
[0.6.6]: https://github.com/labwc/labwc/compare/0.6.5...0.6.6
[0.6.5]: https://github.com/labwc/labwc/compare/0.6.4...0.6.5
diff --git a/README.md b/README.md
index f2c4c375..10339da7 100644
--- a/README.md
+++ b/README.md
@@ -127,8 +127,11 @@ High-level summary of items that Labwc supports:
The obligatory screenshot:
-
-
+
+
+
+
+ Screenshot description
## 2. Build and Installation
diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd
index 9d55f63f..facb2d68 100644
--- a/docs/labwc-actions.5.scd
+++ b/docs/labwc-actions.5.scd
@@ -163,7 +163,7 @@ Actions are used in menus and keyboard/mouse bindings.
to the center of the window. If the given output does not contain
any windows, the cursor is centered on the given output.
-**
+**
Moves active window to other output, unless the window state is
fullscreen.
@@ -172,6 +172,9 @@ Actions are used in menus and keyboard/mouse bindings.
be one of "left", "right", "up" or "down" to indicate that the window
should be moved to the next output in that direction (if one exists).
+ *wrap* [yes|no] When using the direction attribute, wrap around from
+ right-to-left or top-to-bottom, and vice versa. Default no.
+
**
Resizes active window size to width and height of the output when the
window size exceeds the output size.
diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd
index 13751813..57e76bdc 100644
--- a/docs/labwc-config.5.scd
+++ b/docs/labwc-config.5.scd
@@ -8,7 +8,7 @@ labwc - configuration files
Labwc uses openbox-3.6 specification for configuration and theming, but does not
support all options. The following files form the basis of the labwc
-configuration: rc.xml, menu.xml, autostart and environment.
+configuration: rc.xml, menu.xml, autostart, shutdown and environment.
No configuration files are needed to start and run labwc.
@@ -34,11 +34,8 @@ alternative.
The configuration directory location can be override with the -C command line
option.
-All configuration and theme files except autostart are re-loaded on receiving
-signal SIGHUP.
-
-The *autostart* file is executed as a shell script. This is the place for
-executing clients for handling background images, panels and similar.
+All configuration and theme files except autostart and shutdown are re-loaded on
+receiving signal SIGHUP.
The *environment* file is parsed as *variable=value* and sets environment
variables accordingly. It is recommended to specify keyboard layout settings and
@@ -48,6 +45,20 @@ sourced prior to running openbox.
Note: Tilde (~) and environment variables in the value are expanded, but
subshell syntax and apostrophes are ignored.
+The *autostart* file is executed as a shell script after labwc has read its
+configuration and set variables defined in the environment file. Additionally,
+the environment variables WAYLAND_DISPLAY and (when labwc is built with Xwayland
+support) DISPLAY will be defined. This is the place for executing clients for
+handling background images, panels and other tasks that should run automatically
+when labwc launches.
+
+The *shutdown* file is executed as a shell script when labwc is preparing to
+terminate itself. All environment variables, including WAYLAND_DISPLAY and
+DISPLAY, will be available to the script. However, because the script runs
+asynchronously with other termination tasks, the shutdown file should not assume
+that the display will be usable. This file is useful to perform any custom
+operations necessary to finalize a labwc session.
+
The *menu.xml* file defines the context/root-menus and is described in
labwc-menu(5).
@@ -511,7 +522,7 @@ extending outward from the snapped edge.
```
**
- A touch configuration can be bound to a specifc device. If device
+ A touch configuration can be bound to a specific device. If device
name is left empty, the touch configuration applies to all touch
devices or functions as a fallback. Multiple touch configurations
can exist.
@@ -592,6 +603,8 @@ extending outward from the snapped edge.
+
+
```
@@ -667,6 +680,36 @@ extending outward from the snapped edge.
any motion events while a keyboard is typing, and for a short while
after as well.
+** [none|buttonAreas|clickfinger]
+ Configure the method by which physical clicks on a touchpad are mapped to
+ mouse-button events.
+
+ The click methods available are:
+ - *buttonAreas* - The bottom of the touchpad is divided into distinct
+ regions corresponding to left, middle and right buttons; clicking within
+ the region will trigger the corresponding event. Clicking the main area
+ further up produces a left button event.
+ - *clickfinger* - Clicking with one, two or three finger(s) will produce
+ left, right or middle button event without regard to the location of a
+ click.
+ - *none* - Physical clicks will not produce button events.
+
+ The default method depends on the touchpad hardware.
+
+** [yes|no|disabledOnExternalMouse]
+ Optionally enable or disable sending any device events.
+
+ The options available are:
+ - *yes* - Events are sent as usual
+ - *no* - No events are sent from this device
+ - *disabledOnExternalMouse* - This device does not send events if an
+ external mouse has been detected.
+
+ It is possible to prevent events from a device in the config and then do
+ a Reconfigure to temporarily enable / disable specific devices.
+
+ By default, this setting is not configured.
+
## WINDOW RULES
Two types of window rules are supported, actions and properties. They are
diff --git a/docs/labwc.1.scd b/docs/labwc.1.scd
index ca9f1ae9..64eb1a98 100644
--- a/docs/labwc.1.scd
+++ b/docs/labwc.1.scd
@@ -2,7 +2,7 @@ labwc(1)
# NAME
-labwc - a wayland stacking compositor
+labwc - a Wayland stacking compositor
# SYNOPSIS
@@ -16,8 +16,10 @@ It is light-weight and independent with a focus on simply stacking windows
well and rendering some window decorations. Where practicable it uses clients
for wall-paper, panels, screenshots and so on.
+# SIGNALS
+
The compositor will exit or reload its configuration upon receiving SIGTERM
-and SIGHUP respectively. For example:
+and SIGHUP respectively. For example:
```
kill -s $LABWC_PID
@@ -40,7 +42,7 @@ the `--exit` and `--reconfigure` options use.
Enable full logging, including debug information
*-e, --exit*
- Exit the compositor
+ Exit the compositor by sending SIGTERM to `$LABWC_PID`
*-h, --help*
Show help message and quit
@@ -49,7 +51,7 @@ the `--exit` and `--reconfigure` options use.
Merge user config/theme files in all XDG Base Directories
*-r, --reconfigure*
- Reload the compositor configuration
+ Reload the compositor configuration by sending SIGHUP to `$LABWC_PID`
*-s, --startup*
Run command on startup
@@ -60,6 +62,48 @@ the `--exit` and `--reconfigure` options use.
*-V, --verbose*
Enable more verbose logging
+# SESSION MANAGEMENT
+
+To enable the use of graphical clients launched via D-Bus or systemd servie
+activation, labwc can update both activation environments on launch. Provided
+that labwc is aware of an active D-Bus user session (*i.e.*, the environment
+variable `DBUS_SESSION_BUS_ADDRESS` is defined), the compositor will invoke the
+commands
+
+```
+dbus-update-activation-environment
+systemctl --user import-environment
+```
+
+(when available) to notify D-Bus and systemd with the values of the following
+environment variables:
+
+```
+WAYLAND_DISPLAY
+DISPLAY
+XDG_CURRENT_DESKTOP
+XDG_SESSION_TYPE
+XCURSOR_SIZE
+XCURSOR_THEME
+LABWC_PWD
+```
+
+This behavior is enabled by default whenever labwc uses the "DRM" wlroots
+backend (which implies that labwc is the primary compositor on the console).
+When other backends are employed (for example, when labwc runs nested in another
+Wayland compositor or an X11 server), updates to the activation environment are
+disabled by default. Updates to the activation environment can be forced by
+setting the environment variable `LABWC_UPDATE_ACTIVATION_ENV` to one of the
+truthy values `1`, `true`, `yes` or `on`; or suppressed by setting the variable
+to one of the falsy values `0`, `false`, `no` or `off`.
+
+Whenever labwc updates the activation environment on launch, it will also
+attempt to clear the activation environment on exit. For D-Bus, which does not
+provide a means for properly un-setting variables in the activation environment,
+this is accomplished by setting the session variables to empty strings. For
+systemd, the command `systemctl --user unset-environment` will be invoked to
+actually remove the variables from the activation environment.
+
# SEE ALSO
-labwc-config(5), labwc-theme(5), labwc-actions(5)
+labwc-actions(5), labwc-config(5), labwc-menu(5), labwc-theme(5)
diff --git a/docs/rc.xml b/docs/rc.xml
index 3bf2d116..edcca868 100644
--- a/docs/rc.xml
+++ b/docs/rc.xml
@@ -7,10 +7,6 @@
-
- 10
-
-
8
diff --git a/docs/rc.xml.all b/docs/rc.xml.all
index 423bcb74..c3586ae0 100644
--- a/docs/rc.xml.all
+++ b/docs/rc.xml.all
@@ -465,13 +465,15 @@
non-touch, default or the name of a device. You can obtain device names by
running *libinput list-devices* as root or member of the input group.
- Tap is set to *yes* be default. All others are left blank in order to use
+ Tap is set to *yes* by default. All others are left blank in order to use
device defaults.
All values are [yes|no] except for:
- pointerSpeed [-1.0 to 1.0]
- accelProfile [flat|adaptive]
- tapButtonMap [lrm|lmr]
+ - clickMethod [none|buttonAreas|clickfinger]
+ - sendEventsMode [yes|no|disabledOnExternalMouse]
-->
@@ -485,6 +487,8 @@
+
+
diff --git a/include/common/string-helpers.h b/include/common/string-helpers.h
index 4e204302..6d0ec905 100644
--- a/include/common/string-helpers.h
+++ b/include/common/string-helpers.h
@@ -43,4 +43,23 @@ void string_truncate_at_pattern(char *buf, const char *pattern);
*/
char *strdup_printf(const char *fmt, ...);
+/**
+ * str_join - format and join an array of strings with a separator
+ * @parts: NULL-terminated array of string parts to be joined
+ * @fmt: printf-style format string applied to each part
+ * @sep: separator inserted between parts when joining
+ *
+ * A new string is allocated to hold the joined result. The user must free the
+ * returned string. Returns NULL on error.
+ *
+ * Each part of the array is converted via the equivalent of sprintf(output,
+ * fmt, part), so fmt should include a single "%s" format specification. If fmt
+ * is NULL, a default "%s" will be used to copy each part verbatim.
+ *
+ * The separator is arbitrary. When the separator is NULL, a single space will
+ * be used.
+ */
+char *str_join(const char * const parts[],
+ const char *restrict fmt, const char *restrict sep);
+
#endif /* LABWC_STRING_HELPERS_H */
diff --git a/include/config/libinput.h b/include/config/libinput.h
index 4ec0ebb7..912009ea 100644
--- a/include/config/libinput.h
+++ b/include/config/libinput.h
@@ -23,11 +23,13 @@ struct libinput_category {
int left_handed;
enum libinput_config_tap_state tap;
enum libinput_config_tap_button_map tap_button_map;
- int tap_and_drag; /* -1 or libinput_config_drag_state */
- int drag_lock; /* -1 or libinput_config_drag_lock_state */
- int accel_profile; /* -1 or libinput_config_accel_profile */
- int middle_emu; /* -1 or libinput_config_middle_emulation_state */
- int dwt; /* -1 or libinput_config_dwt_state */
+ int tap_and_drag; /* -1 or libinput_config_drag_state */
+ int drag_lock; /* -1 or libinput_config_drag_lock_state */
+ int accel_profile; /* -1 or libinput_config_accel_profile */
+ int middle_emu; /* -1 or libinput_config_middle_emulation_state */
+ int dwt; /* -1 or libinput_config_dwt_state */
+ int click_method; /* -1 or libinput_config_click_method */
+ int send_events_mode; /* -1 or libinput_config_send_events_mode */
};
enum lab_libinput_device_type get_device_type(const char *s);
diff --git a/include/config/session.h b/include/config/session.h
index d1efbb60..e882f0e8 100644
--- a/include/config/session.h
+++ b/include/config/session.h
@@ -2,6 +2,8 @@
#ifndef LABWC_SESSION_H
#define LABWC_SESSION_H
+struct server;
+
/**
* session_environment_init - set enrivonment variables based on =
* pairs in `${XDG_CONFIG_DIRS:-/etc/xdg}/lawbc/environment` with user override
@@ -13,6 +15,12 @@ void session_environment_init(void);
* session_autostart_init - run autostart file as shell script
* Note: Same as `sh ~/.config/labwc/autostart` (or equivalent XDG config dir)
*/
-void session_autostart_init(void);
+void session_autostart_init(struct server *server);
+
+/**
+ * session_shutdown - run session shutdown file as shell script
+ * Note: Same as `sh ~/.config/labwc/shutdown` (or equivalent XDG config dir)
+ */
+void session_shutdown(struct server *server);
#endif /* LABWC_SESSION_H */
diff --git a/include/edges.h b/include/edges.h
index 4e7549eb..6ce029c3 100644
--- a/include/edges.h
+++ b/include/edges.h
@@ -3,11 +3,15 @@
#define LABWC_EDGES_H
#include
+#include
+#include
#include "common/macros.h"
struct border;
struct output;
+struct server;
struct view;
+struct wlr_box;
static inline int
clipped_add(int a, int b)
@@ -102,7 +106,7 @@ void edges_adjust_geom(struct view *view, struct border edges,
void edges_find_neighbors(struct border *nearest_edges, struct view *view,
struct wlr_box target, struct output *output,
- edge_validator_t validator, bool use_pending);
+ edge_validator_t validator, bool use_pending, bool ignore_hidden);
void edges_find_outputs(struct border *nearest_edges, struct view *view,
struct wlr_box target, struct output *output,
@@ -116,4 +120,5 @@ void edges_adjust_resize_geom(struct view *view, struct border edges,
bool edges_traverse_edge(struct edge current, struct edge target, struct edge edge);
+void edges_calculate_visibility(struct server *server, struct view *ignored_view);
#endif /* LABWC_EDGES_H */
diff --git a/include/layers.h b/include/layers.h
index d64c434f..52583035 100644
--- a/include/layers.h
+++ b/include/layers.h
@@ -28,6 +28,7 @@ struct lab_layer_popup {
/* To simplify moving popup nodes from the bottom to the top layer */
struct wlr_box output_toplevel_sx_box;
+ struct wl_listener commit;
struct wl_listener destroy;
struct wl_listener new_popup;
};
diff --git a/include/view.h b/include/view.h
index fdedbe14..b0cb8471 100644
--- a/include/view.h
+++ b/include/view.h
@@ -166,6 +166,7 @@ struct view {
bool tearing_hint;
bool visible_on_all_workspaces;
enum view_edge tiled;
+ uint32_t edges_visible; /* enum wlr_edges bitset */
bool inhibits_keybinds;
xkb_layout_index_t keyboard_layout;
@@ -502,7 +503,8 @@ void view_on_output_destroy(struct view *view);
void view_connect_map(struct view *view, struct wlr_surface *surface);
void view_destroy(struct view *view);
-struct output *view_get_adjacent_output(struct view *view, enum view_edge edge);
+struct output *view_get_adjacent_output(struct view *view, enum view_edge edge,
+ bool wrap);
enum view_axis view_axis_parse(const char *direction);
enum view_edge view_edge_parse(const char *direction);
diff --git a/meson.build b/meson.build
index a0a43263..adbd5455 100644
--- a/meson.build
+++ b/meson.build
@@ -1,7 +1,7 @@
project(
'labwc',
'c',
- version: '0.7.0',
+ version: '0.7.1',
license: 'GPL-2.0-only',
meson_version: '>=0.59.0',
default_options: [
diff --git a/po/LINGUAS b/po/LINGUAS
index 58f25cb0..1b2407b5 100644
--- a/po/LINGUAS
+++ b/po/LINGUAS
@@ -1 +1 @@
-de es et eu fi gl id it ja ka lt nl pa pl pt ru sv tr uk zh_CN
+cs de es et eu fi gl hu id it ja ka lt nl pa pl pt ru sv tr uk zh_CN
diff --git a/po/cs.po b/po/cs.po
new file mode 100644
index 00000000..104e5012
--- /dev/null
+++ b/po/cs.po
@@ -0,0 +1,69 @@
+# Labwc pot file
+# Copyright (C) 2024
+# This file is distributed under the same license as the labwc package.
+# zenobit , 2024.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: labwc\n"
+"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
+"POT-Creation-Date: 2024-01-15 16:00-0500\n"
+"PO-Revision-Date: 2024-03-02 02:00+0100\n"
+"Last-Translator: zenobit \n"
+"Language-Team: Czech \n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: src/menu/menu.c:697
+msgid "Reconfigure"
+msgstr "Překonfigurovat"
+
+#: src/menu/menu.c:699
+msgid "Exit"
+msgstr "Odejít"
+
+#: src/menu/menu.c:715
+msgid "Minimize"
+msgstr "Minimalizovat"
+
+#: src/menu/menu.c:717
+msgid "Maximize"
+msgstr "Maximalizovat"
+
+#: src/menu/menu.c:719
+msgid "Fullscreen"
+msgstr "Na celou obrazovku"
+
+#: src/menu/menu.c:721
+msgid "Roll up/down"
+msgstr "Rolovat nahoru/dolů"
+
+#: src/menu/menu.c:723
+msgid "Decorations"
+msgstr "Dekorace"
+
+#: src/menu/menu.c:725
+msgid "Always on Top"
+msgstr "Vždy nahoře"
+
+#: src/menu/menu.c:730
+msgid "Move left"
+msgstr "Posunout doleva"
+
+#: src/menu/menu.c:737
+msgid "Move right"
+msgstr "Posunout doprava"
+
+#: src/menu/menu.c:742
+msgid "Always on Visible Workspace"
+msgstr "Vždy na viditelné Pracovní Ploše"
+
+#: src/menu/menu.c:745
+msgid "Workspace"
+msgstr "Pracovní Plocha"
+
+#: src/menu/menu.c:748
+msgid "Close"
+msgstr "Zavřít"
diff --git a/po/fi.po b/po/fi.po
index d67d0355..b5021b65 100644
--- a/po/fi.po
+++ b/po/fi.po
@@ -8,13 +8,16 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: Automatically generated\n"
-"Language-Team: none\n"
+"PO-Revision-Date: 2024-02-29 14:23+0000\n"
+"Last-Translator: Jouni Järvinen \n"
+"Language-Team: Finnish \n"
"Language: fi\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:697
msgid "Reconfigure"
@@ -22,7 +25,7 @@ msgstr ""
#: src/menu/menu.c:699
msgid "Exit"
-msgstr ""
+msgstr "Poistu"
#: src/menu/menu.c:715
msgid "Minimize"
diff --git a/po/hu.po b/po/hu.po
new file mode 100644
index 00000000..da861355
--- /dev/null
+++ b/po/hu.po
@@ -0,0 +1,72 @@
+# Labwc pot file
+# Copyright (C) 2024
+# This file is distributed under the same license as the labwc package.
+# FIRST AUTHOR , YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: labwc\n"
+"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
+"POT-Creation-Date: 2024-01-15 16:00-0500\n"
+"PO-Revision-Date: 2024-02-19 21:23+0000\n"
+"Last-Translator: winerysearch \n"
+"Language-Team: Hungarian \n"
+"Language: hu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 4.2.1\n"
+
+#: src/menu/menu.c:697
+msgid "Reconfigure"
+msgstr "Rekonfigurál"
+
+#: src/menu/menu.c:699
+msgid "Exit"
+msgstr "Kilépés"
+
+#: src/menu/menu.c:715
+msgid "Minimize"
+msgstr "Kis méret"
+
+#: src/menu/menu.c:717
+msgid "Maximize"
+msgstr "Teljes méret"
+
+#: src/menu/menu.c:719
+msgid "Fullscreen"
+msgstr "Teljes képernyő"
+
+#: src/menu/menu.c:721
+msgid "Roll up/down"
+msgstr "Felhúz / Legördül"
+
+#: src/menu/menu.c:723
+msgid "Decorations"
+msgstr "Dekorációk"
+
+#: src/menu/menu.c:725
+msgid "Always on Top"
+msgstr "Mindig felül"
+
+#: src/menu/menu.c:730
+msgid "Move left"
+msgstr "Balra dokkol"
+
+#: src/menu/menu.c:737
+msgid "Move right"
+msgstr "Jobbra dokkol"
+
+#: src/menu/menu.c:742
+msgid "Always on Visible Workspace"
+msgstr "Kitűz"
+
+#: src/menu/menu.c:745
+msgid "Workspace"
+msgstr "Munkaasztal"
+
+#: src/menu/menu.c:748
+msgid "Close"
+msgstr "Bezárás"
diff --git a/po/lt.po b/po/lt.po
index 65ab1166..7ac63f85 100644
--- a/po/lt.po
+++ b/po/lt.po
@@ -8,14 +8,17 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
-"PO-Revision-Date: 2024-01-04 15:23+0000\n"
+"PO-Revision-Date: 2024-02-29 14:23+0000\n"
"Last-Translator: Moo \n"
-"Language-Team: Lithuanian \n"
+"Language-Team: Lithuanian \n"
"Language: lt\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n % 10 == 1 && (n % 100 < 11 || n % 100 > 19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? 1 : 2);\n"
+"Plural-Forms: nplurals=3; plural=(n % 10 == 1 && (n % 100 < 11 || n % 100 > "
+"19)) ? 0 : ((n % 10 >= 2 && n % 10 <= 9 && (n % 100 < 11 || n % 100 > 19)) ? "
+"1 : 2);\n"
"X-Generator: Weblate 4.2.1\n"
#: src/menu/menu.c:697
@@ -40,7 +43,7 @@ msgstr "Visas ekranas"
#: src/menu/menu.c:721
msgid "Roll up/down"
-msgstr ""
+msgstr "Užraityti/atraityti"
#: src/menu/menu.c:723
msgid "Decorations"
diff --git a/po/pa.po b/po/pa.po
index 77cd6b2c..655a83b1 100644
--- a/po/pa.po
+++ b/po/pa.po
@@ -8,9 +8,10 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
-"PO-Revision-Date: 2023-12-26 07:23+0000\n"
+"PO-Revision-Date: 2024-02-21 14:23+0000\n"
"Last-Translator: A S Alam \n"
-"Language-Team: Punjabi \n"
+"Language-Team: Punjabi \n"
"Language: pa\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -40,7 +41,7 @@ msgstr "ਪੂਰੀ ਸਕਰੀਨ"
#: src/menu/menu.c:721
msgid "Roll up/down"
-msgstr ""
+msgstr "ਉੱਤੇ/ਹੇਠਾਂ ਸਕਰਾਓ"
#: src/menu/menu.c:723
msgid "Decorations"
diff --git a/po/ru.po b/po/ru.po
index f9e34051..be979099 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
-"PO-Revision-Date: 2024-01-30 08:23+0000\n"
-"Last-Translator: pixis1 \n"
+"PO-Revision-Date: 2024-02-26 12:23+0000\n"
+"Last-Translator: Alice Ventus \n"
"Language-Team: Russian \n"
"Language: ru\n"
@@ -41,7 +41,6 @@ msgid "Fullscreen"
msgstr "На весь экран"
#: src/menu/menu.c:721
-#, fuzzy
msgid "Roll up/down"
msgstr "Свернуть/развернуть в заголовок"
diff --git a/po/tr.po b/po/tr.po
index 64ad85f6..20505905 100644
--- a/po/tr.po
+++ b/po/tr.po
@@ -8,9 +8,10 @@ msgstr ""
"Project-Id-Version: labwc\n"
"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n"
"POT-Creation-Date: 2024-01-15 16:00-0500\n"
-"PO-Revision-Date: 2023-12-26 09:00+0000\n"
+"PO-Revision-Date: 2024-02-24 22:23+0000\n"
"Last-Translator: Sabri Ünal \n"
-"Language-Team: Turkish \n"
+"Language-Team: Turkish \n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -40,7 +41,7 @@ msgstr "Tam Ekran"
#: src/menu/menu.c:721
msgid "Roll up/down"
-msgstr ""
+msgstr "Yukarı/aşağı katla"
#: src/menu/menu.c:723
msgid "Decorations"
diff --git a/protocols/meson.build b/protocols/meson.build
index 0ec9154b..acaf0215 100644
--- a/protocols/meson.build
+++ b/protocols/meson.build
@@ -18,6 +18,7 @@ server_protocols = [
wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml',
wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml',
wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml',
+ wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml',
wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml',
'wlr-layer-shell-unstable-v1.xml',
'wlr-input-inhibitor-unstable-v1.xml',
diff --git a/src/action.c b/src/action.c
index e1187906..1cf8960a 100644
--- a/src/action.c
+++ b/src/action.c
@@ -397,6 +397,10 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char
}
goto cleanup;
}
+ if (!strcmp(argument, "wrap")) {
+ action_arg_add_bool(action, argument, parse_bool(content, false));
+ goto cleanup;
+ }
break;
case ACTION_TYPE_VIRTUAL_OUTPUT_ADD:
case ACTION_TYPE_VIRTUAL_OUTPUT_REMOVE:
@@ -924,10 +928,15 @@ actions_run(struct view *activator, struct server *server,
} else {
/* Config parsing makes sure that direction is a valid direction */
enum view_edge edge = action_get_int(action, "direction", 0);
- target = view_get_adjacent_output(view, edge);
+ bool wrap = action_get_bool(action, "wrap", false);
+ target = view_get_adjacent_output(view, edge, wrap);
}
if (!target) {
- wlr_log(WLR_ERROR, "Invalid output.");
+ /*
+ * Most likely because we're already on the
+ * output furthest in the requested direction.
+ */
+ wlr_log(WLR_DEBUG, "Invalid output");
break;
}
view_move_to_output(view, target);
diff --git a/src/common/parse-bool.c b/src/common/parse-bool.c
index 289ac0af..edef698a 100644
--- a/src/common/parse-bool.c
+++ b/src/common/parse-bool.c
@@ -14,12 +14,16 @@ parse_bool(const char *str, int default_value)
return true;
} else if (!strcasecmp(str, "on")) {
return true;
+ } else if (!strcmp(str, "1")) {
+ return true;
} else if (!strcasecmp(str, "no")) {
return false;
} else if (!strcasecmp(str, "false")) {
return false;
} else if (!strcasecmp(str, "off")) {
return false;
+ } else if (!strcmp(str, "0")) {
+ return false;
}
error_not_a_boolean:
wlr_log(WLR_ERROR, "(%s) is not a boolean value", str);
diff --git a/src/common/string-helpers.c b/src/common/string-helpers.c
index 37fdf4d0..0a1a8c81 100644
--- a/src/common/string-helpers.c
+++ b/src/common/string-helpers.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
+#include
#include
#include
#include
@@ -83,3 +84,73 @@ strdup_printf(const char *fmt, ...)
}
return p;
}
+
+char *
+str_join(const char * const parts[],
+ const char *restrict fmt, const char *restrict sep)
+{
+ assert(parts);
+
+ if (!fmt) {
+ fmt = "%s";
+ }
+
+ if (!sep) {
+ sep = " ";
+ }
+
+ size_t size = 0;
+ size_t n_parts = 0;
+
+ size_t sep_len = strlen(sep);
+
+ /* Count the length of each formatted string */
+ for (const char *const *s = parts; *s; ++s) {
+ int n = snprintf(NULL, 0, fmt, *s);
+ if (n < 0) {
+ return NULL;
+ }
+ size += (size_t)n;
+ ++n_parts;
+ }
+
+ if (n_parts < 1) {
+ return NULL;
+ }
+
+ /* Need (n_parts - 1) separators, plus one NULL terminator */
+ size += (n_parts - 1) * sep_len + 1;
+
+ /* Concatenate the strings and separators */
+ char *buf = xzalloc(size);
+ char *p = buf;
+ for (const char *const *s = parts; *s; ++s) {
+ int n = 0;
+
+ if (p != buf) {
+ n = snprintf(p, size, "%s", sep);
+ if (n < 0 || (size_t)n >= size) {
+ p = NULL;
+ break;
+ }
+ size -= (size_t)n;
+ p += (size_t)n;
+ }
+
+ n = snprintf(p, size, fmt, *s);
+ if (n < 0 || (size_t)n >= size) {
+ p = NULL;
+ break;
+ }
+ size -= (size_t)n;
+ p += (size_t)n;
+ }
+
+ if (!p) {
+ free(buf);
+ return NULL;
+ }
+
+ return buf;
+}
+
diff --git a/src/config/libinput.c b/src/config/libinput.c
index 7ce62b79..93ab9af2 100644
--- a/src/config/libinput.c
+++ b/src/config/libinput.c
@@ -23,6 +23,8 @@ libinput_category_init(struct libinput_category *l)
l->accel_profile = -1;
l->middle_emu = -1;
l->dwt = -1;
+ l->click_method = -1;
+ l->send_events_mode = -1;
}
enum lab_libinput_device_type
diff --git a/src/config/rcxml.c b/src/config/rcxml.c
index 61784e13..56f93e36 100644
--- a/src/config/rcxml.c
+++ b/src/config/rcxml.c
@@ -461,6 +461,29 @@ get_accel_profile(const char *s)
return -1;
}
+static int
+get_send_events_mode(const char *s)
+{
+ if (!s) {
+ goto err;
+ }
+
+ int ret = parse_bool(s, -1);
+ if (ret >= 0) {
+ return ret
+ ? LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
+ : LIBINPUT_CONFIG_SEND_EVENTS_DISABLED;
+ }
+
+ if (!strcasecmp(s, "disabledOnExternalMouse")) {
+ return LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE;
+ }
+
+err:
+ wlr_log(WLR_INFO, "Not a recognised send events mode");
+ return -1;
+}
+
static void
fill_libinput_category(char *nodename, char *content)
{
@@ -563,6 +586,22 @@ fill_libinput_category(char *nodename, char *content)
current_libinput_category->dwt = ret
? LIBINPUT_CONFIG_DWT_ENABLED
: LIBINPUT_CONFIG_DWT_DISABLED;
+ } else if (!strcasecmp(nodename, "clickMethod")) {
+ if (!strcasecmp(content, "none")) {
+ current_libinput_category->click_method =
+ LIBINPUT_CONFIG_CLICK_METHOD_NONE;
+ } else if (!strcasecmp(content, "clickfinger")) {
+ current_libinput_category->click_method =
+ LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER;
+ } else if (!strcasecmp(content, "buttonAreas")) {
+ current_libinput_category->click_method =
+ LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
+ } else {
+ wlr_log(WLR_ERROR, "invalid clickMethod");
+ }
+ } else if (!strcasecmp(nodename, "sendEventsMode")) {
+ current_libinput_category->send_events_mode =
+ get_send_events_mode(content);
}
}
diff --git a/src/config/session.c b/src/config/session.c
index c87b0865..56cef717 100644
--- a/src/config/session.c
+++ b/src/config/session.c
@@ -1,19 +1,35 @@
// SPDX-License-Identifier: GPL-2.0-only
#define _POSIX_C_SOURCE 200809L
+#include
#include
#include
#include
#include
#include
+#include
+#include
#include
#include "common/buf.h"
#include "common/dir.h"
#include "common/file-helpers.h"
+#include "common/mem.h"
+#include "common/parse-bool.h"
#include "common/spawn.h"
#include "common/string-helpers.h"
#include "config/session.h"
#include "labwc.h"
+static const char *const env_vars[] = {
+ "DISPLAY",
+ "WAYLAND_DISPLAY",
+ "XDG_CURRENT_DESKTOP",
+ "XCURSOR_SIZE",
+ "XCURSOR_THEME",
+ "XDG_SESSION_TYPE",
+ "LABWC_PID",
+ NULL
+};
+
static void
process_line(char *line)
{
@@ -65,23 +81,71 @@ read_environment_file(const char *filename)
}
static void
-update_activation_env(const char *env_keys)
+backend_check_drm(struct wlr_backend *backend, void *is_drm)
{
+ if (wlr_backend_is_drm(backend)) {
+ *(bool *)is_drm = true;
+ }
+}
+
+static bool
+should_update_activation(struct server *server)
+{
+ assert(server);
+
+ static const char *act_env = "LABWC_UPDATE_ACTIVATION_ENV";
+ char *env = getenv(act_env);
+ if (env) {
+ /* Respect any valid preference from the environment */
+ int enabled = parse_bool(env, -1);
+
+ if (enabled == -1) {
+ wlr_log(WLR_ERROR, "ignoring non-Boolean variable %s", act_env);
+ } else {
+ wlr_log(WLR_DEBUG, "%s is %s",
+ act_env, enabled ? "true" : "false");
+ return enabled;
+ }
+ }
+
+ /* With no valid preference, update when a DRM backend is in use */
+ bool have_drm = false;
+ wlr_multi_for_each_backend(server->backend, backend_check_drm, &have_drm);
+ return have_drm;
+}
+
+static void
+update_activation_env(struct server *server, bool initialize)
+{
+ if (!should_update_activation(server)) {
+ return;
+ }
+
if (!getenv("DBUS_SESSION_BUS_ADDRESS")) {
/* Prevent accidentally auto-launching a dbus session */
wlr_log(WLR_INFO, "Not updating dbus execution environment: "
"DBUS_SESSION_BUS_ADDRESS not set");
return;
}
+
wlr_log(WLR_INFO, "Updating dbus execution environment");
- char *cmd = strdup_printf("dbus-update-activation-environment %s", env_keys);
+ char *env_keys = str_join(env_vars, "%s", " ");
+ char *env_unset_keys = initialize ? NULL : str_join(env_vars, "%s=", " ");
+
+ char *cmd =
+ strdup_printf("dbus-update-activation-environment %s",
+ initialize ? env_keys : env_unset_keys);
spawn_async_no_shell(cmd);
free(cmd);
- cmd = strdup_printf("systemctl --user import-environment %s", env_keys);
+ cmd = strdup_printf("systemctl --user %s %s",
+ initialize ? "import-environment" : "unset-environment", env_keys);
spawn_async_no_shell(cmd);
free(cmd);
+
+ free(env_keys);
+ free(env_unset_keys);
}
void
@@ -120,14 +184,11 @@ session_environment_init(void)
paths_destroy(&paths);
}
-void
-session_autostart_init(void)
+static void
+run_session_script(const char *script)
{
- /* Update dbus and systemd user environment, each may fail gracefully */
- update_activation_env("DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP");
-
struct wl_list paths;
- paths_config_create(&paths, "autostart");
+ paths_config_create(&paths, script);
bool should_merge_config = rc.merge_config;
struct wl_list *(*iter)(struct wl_list *list);
@@ -138,7 +199,7 @@ session_autostart_init(void)
if (!file_exists(path->string)) {
continue;
}
- wlr_log(WLR_INFO, "run autostart file %s", path->string);
+ wlr_log(WLR_INFO, "run session script %s", path->string);
char *cmd = strdup_printf("sh %s", path->string);
spawn_async_no_shell(cmd);
free(cmd);
@@ -149,3 +210,20 @@ session_autostart_init(void)
}
paths_destroy(&paths);
}
+
+void
+session_autostart_init(struct server *server)
+{
+ /* Update dbus and systemd user environment, each may fail gracefully */
+ update_activation_env(server, /* initialize */ true);
+ run_session_script("autostart");
+}
+
+void
+session_shutdown(struct server *server)
+{
+ run_session_script("shutdown");
+
+ /* Clear the dbus and systemd user environment, each may fail gracefully */
+ update_activation_env(server, /* initialize */ false);
+}
diff --git a/src/debug.c b/src/debug.c
index bac6cbaa..af722f3e 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -1,19 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-only
#include
#include
+#include "common/graphic-helpers.h"
#include "common/scene-helpers.h"
#include "debug.h"
#include "labwc.h"
#include "node.h"
#include "ssd.h"
#include "view.h"
+#include "workspaces.h"
#define HEADER_CHARS "------------------------------"
#define INDENT_SIZE 3
+#define LEFT_COL_SPACE 35
+
#define IGNORE_SSD true
#define IGNORE_MENU true
-#define LEFT_COL_SPACE 35
+#define IGNORE_OSD_PREVIEW_OUTLINE true
static struct view *last_view;
@@ -42,13 +46,13 @@ get_layer_name(uint32_t layer)
{
switch (layer) {
case ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND:
- return "layer-background";
+ return "output->layer-background";
case ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM:
- return "layer-bottom";
+ return "output->layer-bottom";
case ZWLR_LAYER_SHELL_V1_LAYER_TOP:
- return "layer-top";
+ return "output->layer-top";
case ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY:
- return "layer-overlay";
+ return "output->layer-overlay";
default:
abort();
}
@@ -57,16 +61,27 @@ get_layer_name(uint32_t layer)
static const char *
get_view_part(struct view *view, struct wlr_scene_node *node)
{
- if (view && node == &view->scene_tree->node) {
- return "view";
+ static char view_name[LEFT_COL_SPACE];
+ if (!view) {
+ return NULL;
}
- if (view && node == view->scene_node) {
+ if (node == &view->scene_tree->node) {
+ const char *app_id = view_get_string_prop(view, "app_id");
+ if (!app_id) {
+ return "view";
+ }
+ snprintf(view_name, sizeof(view_name), "view (%s)", app_id);
+ return view_name;
+ }
+ if (node == view->scene_node) {
return "view->scene_node";
}
- if (view) {
- return ssd_debug_get_node_name(view->ssd, node);
+ if (view->resize_indicator.tree
+ && node == &view->resize_indicator.tree->node) {
+ /* Created on-demand */
+ return "view->resize_indicator";
}
- return NULL;
+ return ssd_debug_get_node_name(view->ssd, node);
}
static const char *
@@ -81,12 +96,20 @@ get_special(struct server *server, struct wlr_scene_node *node)
if (node == &server->view_tree->node) {
return "server->view_tree";
}
+ if (node == &server->view_tree_always_on_bottom->node) {
+ return "server->always_on_bottom";
+ }
if (node == &server->view_tree_always_on_top->node) {
- return "server->view_tree_always_on_top";
+ return "server->always_on_top";
}
if (node->parent == server->view_tree) {
- /* Add node_descriptor just to get the name here? */
- return "workspace";
+ struct workspace *workspace;
+ wl_list_for_each(workspace, &server->workspaces, link) {
+ if (&workspace->tree->node == node) {
+ return workspace->name;
+ }
+ }
+ return "unknown workspace";
}
if (node->parent == &server->scene->tree) {
struct output *output;
@@ -95,15 +118,34 @@ get_special(struct server *server, struct wlr_scene_node *node)
return "output->osd_tree";
}
if (node == &output->layer_popup_tree->node) {
- return "output->popup_tree";
+ return "output->layer_popup_tree";
}
for (int i = 0; i < 4; i++) {
if (node == &output->layer_tree[i]->node) {
return get_layer_name(i);
}
}
+ if (node == &output->session_lock_tree->node) {
+ return "output->session_lock_tree";
+ }
}
}
+ if (node == &server->xdg_popup_tree->node) {
+ return "server->xdg_popup_tree";
+ }
+ if (node == &server->seat.drag.icons->node) {
+ return "seat->drag.icons";
+ }
+ if (server->seat.region_overlay.tree
+ && node == &server->seat.region_overlay.tree->node) {
+ /* Created on-demand */
+ return "seat->region_overlay";
+ }
+ if (server->osd_state.preview_outline
+ && node == &server->osd_state.preview_outline->tree->node) {
+ /* Created on-demand */
+ return "osd_state->preview_outline";
+ }
#if HAVE_XWAYLAND
if (node == &server->unmanaged_tree->node) {
return "server->unmanaged_tree";
@@ -155,15 +197,21 @@ dump_tree(struct server *server, struct wlr_scene_node *node,
HEADER_CHARS, HEADER_CHARS, HEADER_CHARS);
printf(" ");
}
- int padding = LEFT_COL_SPACE - pos - strlen(type);
+ int max_width = LEFT_COL_SPACE - pos;
+ int padding = max_width - strlen(type);
+ if (padding < 0) {
+ padding = 0;
+ }
if (!pos) {
padding += 3;
}
- printf("%s %*c %4d %4d [%p]\n", type, padding, ' ', x, y, node);
+ printf("%.*s %*c %4d %4d [%p]\n", max_width - 1, type, padding, ' ', x, y, node);
if ((IGNORE_MENU && node == &server->menu_tree->node)
|| (IGNORE_SSD && last_view
- && ssd_debug_is_root_node(last_view->ssd, node))) {
+ && ssd_debug_is_root_node(last_view->ssd, node))
+ || (IGNORE_OSD_PREVIEW_OUTLINE && server->osd_state.preview_outline
+ && node == &server->osd_state.preview_outline->tree->node)) {
printf("%*c%s\n", pos + 4 + INDENT_SIZE, ' ', "");
return;
}
diff --git a/src/edges.c b/src/edges.c
index 86736a4d..3b507c3e 100644
--- a/src/edges.c
+++ b/src/edges.c
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
#include
#include
+#include
+#include
#include
#include "common/border.h"
#include "common/macros.h"
@@ -8,6 +10,7 @@
#include "edges.h"
#include "labwc.h"
#include "view.h"
+#include "node.h"
static void
edges_for_target_geometry(struct border *edges, struct view *view,
@@ -35,34 +38,54 @@ edges_initialize(struct border *edges)
}
static inline struct edge
-build_edge(struct border region, enum view_edge direction, int pad)
+build_edge(struct border region, enum wlr_edges direction, int pad)
{
struct edge edge = { 0 };
switch (direction) {
- case VIEW_EDGE_LEFT:
+ case WLR_EDGE_LEFT:
edge.offset = clipped_sub(region.left, pad);
edge.min = region.top;
edge.max = region.bottom;
break;
- case VIEW_EDGE_RIGHT:
+ case WLR_EDGE_RIGHT:
edge.offset = clipped_add(region.right, pad);
edge.min = region.top;
edge.max = region.bottom;
break;
- case VIEW_EDGE_UP:
+ case WLR_EDGE_TOP:
edge.offset = clipped_sub(region.top, pad);
edge.min = region.left;
edge.max = region.right;
break;
- case VIEW_EDGE_DOWN:
+ case WLR_EDGE_BOTTOM:
edge.offset = clipped_add(region.bottom, pad);
edge.min = region.left;
edge.max = region.right;
break;
- default:
+ case WLR_EDGE_NONE:
/* Should never be reached */
- assert(false);
+ wlr_log(WLR_ERROR, "invalid direction");
+ abort();
+ }
+
+ return edge;
+}
+
+static inline bool
+is_lesser(enum wlr_edges direction)
+{
+ return direction == WLR_EDGE_LEFT || direction == WLR_EDGE_TOP;
+}
+
+static inline struct edge
+build_visible_edge(struct border region, enum wlr_edges direction,
+ int pad, uint32_t edges_visible)
+{
+ struct edge edge = build_edge(region, direction, pad);
+
+ if (!(edges_visible & direction)) {
+ edge.offset = is_lesser(direction) ? INT_MIN : INT_MAX;
}
return edge;
@@ -72,7 +95,7 @@ static void
validate_single_region_edge(int *valid_edge,
struct border view, struct border target,
struct border region, edge_validator_t validator,
- enum view_edge direction)
+ enum wlr_edges direction, uint32_t edges_visible)
{
/*
* When a view snaps to another while moving to its target, it can do
@@ -90,42 +113,63 @@ validate_single_region_edge(int *valid_edge,
* the region borders for aligned edges only.
*/
- bool lesser = direction == VIEW_EDGE_LEFT || direction == VIEW_EDGE_UP;
+ enum wlr_edges opposing = WLR_EDGE_NONE;
+
+ switch (direction) {
+ case WLR_EDGE_TOP:
+ opposing = WLR_EDGE_BOTTOM;
+ break;
+ case WLR_EDGE_BOTTOM:
+ opposing = WLR_EDGE_TOP;
+ break;
+ case WLR_EDGE_LEFT:
+ opposing = WLR_EDGE_RIGHT;
+ break;
+ case WLR_EDGE_RIGHT:
+ opposing = WLR_EDGE_LEFT;
+ break;
+ case WLR_EDGE_NONE:
+ /* Should never be reached */
+ assert(false);
+ return;
+ }
validator(valid_edge,
build_edge(view, direction, 0),
build_edge(target, direction, 0),
- build_edge(region, view_edge_invert(direction), 0),
- build_edge(region, direction, rc.gap), lesser);
+ build_visible_edge(region, opposing, 0, edges_visible),
+ build_visible_edge(region, direction, rc.gap, edges_visible),
+ is_lesser(direction));
}
static void
validate_edges(struct border *valid_edges,
struct border view, struct border target,
- struct border region, edge_validator_t validator)
+ struct border region, uint32_t edges_visible,
+ edge_validator_t validator)
{
/* Check for edges encountered during movement of left edge */
validate_single_region_edge(&valid_edges->left,
- view, target, region, validator, VIEW_EDGE_LEFT);
+ view, target, region, validator, WLR_EDGE_LEFT, edges_visible);
/* Check for edges encountered during movement of right edge */
validate_single_region_edge(&valid_edges->right,
- view, target, region, validator, VIEW_EDGE_RIGHT);
+ view, target, region, validator, WLR_EDGE_RIGHT, edges_visible);
/* Check for edges encountered during movement of top edge */
validate_single_region_edge(&valid_edges->top,
- view, target, region, validator, VIEW_EDGE_UP);
+ view, target, region, validator, WLR_EDGE_TOP, edges_visible);
/* Check for edges encountered during movement of bottom edge */
validate_single_region_edge(&valid_edges->bottom,
- view, target, region, validator, VIEW_EDGE_DOWN);
+ view, target, region, validator, WLR_EDGE_BOTTOM, edges_visible);
}
static void
validate_single_output_edge(int *valid_edge,
struct border view, struct border target,
struct border region, edge_validator_t validator,
- enum view_edge direction)
+ enum wlr_edges direction)
{
static struct border unbounded = {
.top = INT_MIN,
@@ -134,13 +178,11 @@ validate_single_output_edge(int *valid_edge,
.left = INT_MIN,
};
- bool lesser = direction == VIEW_EDGE_LEFT || direction == VIEW_EDGE_UP;
-
validator(valid_edge,
build_edge(view, direction, 0),
build_edge(target, direction, 0),
build_edge(region, direction, 0),
- build_edge(unbounded, direction, 0), lesser);
+ build_edge(unbounded, direction, 0), is_lesser(direction));
}
static void
@@ -182,32 +224,170 @@ validate_output_edges(struct border *valid_edges,
/* Left edge encounters a half-infinite region to the left of the output */
validate_single_output_edge(&valid_edges->left,
- view, target, output, validator, VIEW_EDGE_LEFT);
+ view, target, output, validator, WLR_EDGE_LEFT);
/* Right edge encounters a half-infinite region to the right of the output */
validate_single_output_edge(&valid_edges->right,
- view, target, output, validator, VIEW_EDGE_RIGHT);
+ view, target, output, validator, WLR_EDGE_RIGHT);
/* Top edge encounters a half-infinite region above the output */
validate_single_output_edge(&valid_edges->top,
- view, target, output, validator, VIEW_EDGE_UP);
+ view, target, output, validator, WLR_EDGE_TOP);
/* Bottom edge encounters a half-infinite region below the output */
validate_single_output_edge(&valid_edges->bottom,
- view, target, output, validator, VIEW_EDGE_DOWN);
+ view, target, output, validator, WLR_EDGE_BOTTOM);
+}
+
+/* Test if parts of the current view is covered by the remaining space in the region */
+static void
+subtract_view_from_space(struct view *view, pixman_region32_t *available)
+{
+ struct wlr_box view_size = ssd_max_extents(view);
+ pixman_box32_t view_rect = {
+ .x1 = view_size.x,
+ .x2 = view_size.x + view_size.width,
+ .y1 = view_size.y,
+ .y2 = view_size.y + view_size.height
+ };
+
+ pixman_region_overlap_t overlap =
+ pixman_region32_contains_rectangle(available, &view_rect);
+
+ switch (overlap) {
+ case PIXMAN_REGION_IN:
+ view->edges_visible = WLR_EDGE_TOP | WLR_EDGE_RIGHT
+ | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT;
+ break;
+ case PIXMAN_REGION_OUT:
+ view->edges_visible = 0;
+ return;
+ case PIXMAN_REGION_PART:
+ ; /* works around "a label can only be part of a statement" */
+ pixman_region32_t intersection;
+ pixman_region32_init(&intersection);
+ pixman_region32_intersect_rect(&intersection, available,
+ view_size.x, view_size.y,
+ view_size.width, view_size.height);
+
+ int nrects;
+ const pixman_box32_t *rects =
+ pixman_region32_rectangles(&intersection, &nrects);
+
+ view->edges_visible = 0;
+ for (int i = 0; i < nrects; i++) {
+ if (rects[i].x1 == view_rect.x1) {
+ view->edges_visible |= WLR_EDGE_LEFT;
+ }
+ if (rects[i].y1 == view_rect.y1) {
+ view->edges_visible |= WLR_EDGE_TOP;
+ }
+ if (rects[i].x2 == view_rect.x2) {
+ view->edges_visible |= WLR_EDGE_RIGHT;
+ }
+ if (rects[i].y2 == view_rect.y2) {
+ view->edges_visible |= WLR_EDGE_BOTTOM;
+ }
+ }
+ pixman_region32_fini(&intersection);
+ break;
+ }
+
+ /* Subtract the view geometry from the available region for the next check */
+ pixman_region32_t view_region;
+ pixman_region32_init_rects(&view_region, &view_rect, 1);
+ pixman_region32_subtract(available, available, &view_region);
+ pixman_region32_fini(&view_region);
+}
+
+static void
+subtract_node_tree(struct wlr_scene_tree *tree, pixman_region32_t *available,
+ struct view *ignored_view)
+{
+ struct view *view;
+ struct wlr_scene_node *node;
+ struct node_descriptor *node_desc;
+ wl_list_for_each_reverse(node, &tree->children, link) {
+ if (!node->enabled) {
+ /*
+ * This skips everything that is not being
+ * rendered, including minimized / unmapped
+ * windows and workspaces other than the
+ * current one.
+ */
+ continue;
+ }
+
+ node_desc = node->data;
+ if (node_desc && node_desc->type == LAB_NODE_DESC_VIEW) {
+ view = node_view_from_node(node);
+ if (view != ignored_view) {
+ subtract_view_from_space(view, available);
+ }
+ } else if (node->type == WLR_SCENE_NODE_TREE) {
+ subtract_node_tree(wlr_scene_tree_from_node(node),
+ available, ignored_view);
+ }
+ }
+}
+
+void
+edges_calculate_visibility(struct server *server, struct view *ignored_view)
+{
+ /*
+ * The region stores the available output layout space
+ * and subtracts the window geometries in reverse rendering
+ * order, e.g. a window rendered on top is subtracted first.
+ *
+ * This allows to detect if a window is actually visible.
+ * If there is no overlap of its geometry and the remaining
+ * region it must be completely covered by other windows.
+ *
+ */
+ pixman_region32_t region;
+ pixman_region32_init(®ion);
+
+ /*
+ * Initialize the region with each individual output.
+ *
+ * If we were to use NULL for the reference output we
+ * would get a single combined wlr_box of the whole
+ * layout which could cover actual invisible areas
+ * in case the output resolutions differ.
+ */
+ struct output *output;
+ struct wlr_box layout_box;
+ wl_list_for_each(output, &server->outputs, link) {
+ if (!output_is_usable(output)) {
+ continue;
+ }
+ wlr_output_layout_get_box(server->output_layout,
+ output->wlr_output, &layout_box);
+ pixman_region32_union_rect(®ion, ®ion,
+ layout_box.x, layout_box.y, layout_box.width, layout_box.height);
+ }
+
+ subtract_node_tree(&server->scene->tree, ®ion, ignored_view);
+
+ pixman_region32_fini(®ion);
}
void
edges_find_neighbors(struct border *nearest_edges, struct view *view,
struct wlr_box target, struct output *output,
- edge_validator_t validator, bool use_pending)
+ edge_validator_t validator, bool use_pending, bool ignore_hidden)
{
assert(view);
assert(validator);
assert(nearest_edges);
+ if (!output_is_usable(view->output)) {
+ wlr_log(WLR_DEBUG, "ignoring edge search for view on unsable output");
+ return;
+ }
+
struct border view_edges = { 0 };
struct border target_edges = { 0 };
@@ -223,22 +403,21 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view,
continue;
}
- if (output && v->output != output) {
+ uint32_t edges_visible = ignore_hidden ? v->edges_visible :
+ WLR_EDGE_TOP | WLR_EDGE_LEFT
+ | WLR_EDGE_BOTTOM | WLR_EDGE_RIGHT;
+
+ if (edges_visible == 0) {
continue;
}
- /*
- * If view and v are on different outputs, make sure part of
- * view is actually in the usable area of the output of v.
- */
- if (view->output != v->output) {
- struct wlr_box usable =
- output_usable_area_in_layout_coords(v->output);
+ if (output && output != v->output && !view_on_output(v, output)) {
+ continue;
+ }
- struct wlr_box ol;
- if (!wlr_box_intersection(&ol, view_geom, &usable)) {
- continue;
- }
+ /* Both view and v must share a common output */
+ if (view->output != v->output && !(view->outputs & v->outputs)) {
+ continue;
}
struct border border = ssd_get_margin(v->ssd);
@@ -252,7 +431,7 @@ edges_find_neighbors(struct border *nearest_edges, struct view *view,
};
validate_edges(nearest_edges, view_edges,
- target_edges, win_edges, validator);
+ target_edges, win_edges, edges_visible, validator);
}
}
@@ -265,6 +444,12 @@ edges_find_outputs(struct border *nearest_edges, struct view *view,
assert(validator);
assert(nearest_edges);
+ if (!output_is_usable(view->output)) {
+ wlr_log(WLR_DEBUG,
+ "ignoring edge search for view on unsable output");
+ return;
+ }
+
struct border view_edges = { 0 };
struct border target_edges = { 0 };
diff --git a/src/input/key-state.c b/src/input/key-state.c
index c512a762..8ce66ff0 100644
--- a/src/input/key-state.c
+++ b/src/input/key-state.c
@@ -79,9 +79,7 @@ key_state_pressed_sent_keycodes(void)
report(&bound, "before - bound:");
/* pressed_sent = pressed - bound */
- memcpy(pressed_sent.keys, pressed.keys,
- MAX_PRESSED_KEYS * sizeof(uint32_t));
- pressed_sent.nr_keys = pressed.nr_keys;
+ pressed_sent = pressed;
for (int i = 0; i < bound.nr_keys; ++i) {
remove_key(&pressed_sent, bound.keys[i]);
}
diff --git a/src/interactive.c b/src/interactive.c
index 432027a6..f4b09e1d 100644
--- a/src/interactive.c
+++ b/src/interactive.c
@@ -1,4 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
+#include "edges.h"
#include "input/keyboard.h"
#include "labwc.h"
#include "regions.h"
@@ -123,6 +124,9 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges)
if (rc.resize_indicator) {
resize_indicator_show(view);
}
+ if (rc.window_edge_strength) {
+ edges_calculate_visibility(server, view);
+ }
}
/* Returns true if view was snapped to any edge */
diff --git a/src/layers.c b/src/layers.c
index 79cffe94..20e9e683 100644
--- a/src/layers.c
+++ b/src/layers.c
@@ -260,14 +260,35 @@ popup_handle_destroy(struct wl_listener *listener, void *data)
wl_container_of(listener, popup, destroy);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->new_popup.link);
+
+ /* Usually already removed unless there was no commit at all */
+ if (popup->commit.notify) {
+ wl_list_remove(&popup->commit.link);
+ }
+
free(popup);
}
+static void
+popup_handle_commit(struct wl_listener *listener, void *data)
+{
+ struct lab_layer_popup *popup =
+ wl_container_of(listener, popup, commit);
+
+ if (popup->wlr_popup->base->initial_commit) {
+ wlr_xdg_popup_unconstrain_from_box(popup->wlr_popup,
+ &popup->output_toplevel_sx_box);
+
+ /* Prevent getting called over and over again */
+ wl_list_remove(&popup->commit.link);
+ popup->commit.notify = NULL;
+ }
+}
+
static void popup_handle_new_popup(struct wl_listener *listener, void *data);
static struct lab_layer_popup *
-create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent,
- struct wlr_box *output_toplevel_sx_box)
+create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent)
{
struct lab_layer_popup *popup = znew(*popup);
popup->wlr_popup = wlr_popup;
@@ -282,10 +303,13 @@ create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent,
popup->destroy.notify = popup_handle_destroy;
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
+
popup->new_popup.notify = popup_handle_new_popup;
wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
- wlr_xdg_popup_unconstrain_from_box(wlr_popup, output_toplevel_sx_box);
+ popup->commit.notify = popup_handle_commit;
+ wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
+
return popup;
}
@@ -297,8 +321,7 @@ popup_handle_new_popup(struct wl_listener *listener, void *data)
wl_container_of(listener, lab_layer_popup, new_popup);
struct wlr_xdg_popup *wlr_popup = data;
struct lab_layer_popup *new_popup = create_popup(wlr_popup,
- lab_layer_popup->scene_tree,
- &lab_layer_popup->output_toplevel_sx_box);
+ lab_layer_popup->scene_tree);
new_popup->output_toplevel_sx_box =
lab_layer_popup->output_toplevel_sx_box;
}
@@ -357,8 +380,7 @@ handle_new_popup(struct wl_listener *listener, void *data)
.width = output_box.width,
.height = output_box.height,
};
- struct lab_layer_popup *popup = create_popup(wlr_popup,
- surface->tree, &output_toplevel_sx_box);
+ struct lab_layer_popup *popup = create_popup(wlr_popup, surface->tree);
popup->output_toplevel_sx_box = output_toplevel_sx_box;
if (surface->layer_surface->current.layer
diff --git a/src/main.c b/src/main.c
index 9bfe3a1d..35b2f3fa 100644
--- a/src/main.c
+++ b/src/main.c
@@ -171,13 +171,15 @@ main(int argc, char *argv[])
menu_init(&server);
- session_autostart_init();
+ session_autostart_init(&server);
if (startup_cmd) {
spawn_async_no_shell(startup_cmd);
}
wl_display_run(server.wl_display);
+ session_shutdown(&server);
+
server_finish(&server);
menu_finish(&server);
diff --git a/src/output.c b/src/output.c
index 6d05ee94..16eee4b8 100644
--- a/src/output.c
+++ b/src/output.c
@@ -150,6 +150,32 @@ output_request_state_notify(struct wl_listener *listener, void *data)
struct output *output = wl_container_of(listener, output, request_state);
const struct wlr_output_event_request_state *event = data;
+ /*
+ * If wlroots ever requests other state changes here we could
+ * restore more of ddc9047a67cd53b2948f71fde1bbe9118000dd3f.
+ */
+ if (event->state->committed == WLR_OUTPUT_STATE_MODE) {
+ /* Only the mode has changed */
+ switch (event->state->mode_type) {
+ case WLR_OUTPUT_STATE_MODE_FIXED:
+ wlr_output_set_mode(output->wlr_output, event->state->mode);
+ break;
+ case WLR_OUTPUT_STATE_MODE_CUSTOM:
+ wlr_output_set_custom_mode(output->wlr_output,
+ event->state->custom_mode.width,
+ event->state->custom_mode.height,
+ event->state->custom_mode.refresh);
+ break;
+ }
+ wlr_output_schedule_frame(output->wlr_output);
+ return;
+ }
+
+ /*
+ * Fallback path for everything that we didn't handle above.
+ * The commit will cause a black frame injection so this
+ * path causes flickering during resize of nested outputs.
+ */
if (!wlr_output_commit_state(output->wlr_output, event->state)) {
wlr_log(WLR_ERROR, "Backend requested a new state that could not be applied");
}
@@ -405,10 +431,11 @@ output_update_for_layout_change(struct server *server)
cursor_update_image(&server->seat);
}
-static void
+static bool
output_config_apply(struct server *server,
struct wlr_output_configuration_v1 *config)
{
+ bool success = true;
server->pending_output_layout_change++;
struct wlr_output_configuration_head_v1 *head;
@@ -436,8 +463,15 @@ output_config_apply(struct server *server,
output_enable_adaptive_sync(o, head->state.adaptive_sync_enabled);
}
if (!wlr_output_commit(o)) {
- wlr_log(WLR_ERROR, "Output config commit failed");
- continue;
+ /*
+ * FIXME: This is only part of the story, we should revert
+ * all previously commited outputs as well here.
+ *
+ * See https://github.com/labwc/labwc/pull/1528
+ */
+ wlr_log(WLR_INFO, "Output config commit failed: %s", o->name);
+ success = false;
+ break;
}
/* Only do Layout specific actions if the commit went trough */
@@ -477,6 +511,7 @@ output_config_apply(struct server *server,
server->pending_output_layout_change--;
do_output_layout_change(server);
+ return success;
}
static bool
@@ -509,11 +544,17 @@ verify_output_config_v1(const struct wlr_output_configuration_v1 *config)
if (wlr_output_is_wl(head->state.output) && refresh != 0) {
/* Wayland backend does not support refresh rates */
- err_msg = "Wayland backend refresh rate unsupported";
+ err_msg = "Wayland backend refresh rates unsupported";
goto custom_mode_failed;
}
}
+ if (wlr_output_is_wl(head->state.output)
+ && !head->state.adaptive_sync_enabled) {
+ err_msg = "Wayland backend requires adaptive sync";
+ goto custom_mode_failed;
+ }
+
/*
* Ensure the new output state can be applied on
* its own and inform the client when it can not.
@@ -569,8 +610,7 @@ handle_output_manager_apply(struct wl_listener *listener, void *data)
bool config_is_good = verify_output_config_v1(config);
- if (config_is_good) {
- output_config_apply(server, config);
+ if (config_is_good && output_config_apply(server, config)) {
wlr_output_configuration_v1_send_succeeded(config);
} else {
wlr_output_configuration_v1_send_failed(config);
diff --git a/src/resistance.c b/src/resistance.c
index e7869fc1..7d317de8 100644
--- a/src/resistance.c
+++ b/src/resistance.c
@@ -114,8 +114,9 @@ resistance_move_apply(struct view *view, double *x, double *y)
if (rc.window_edge_strength != 0) {
/* Find any relevant window edges encountered by this move */
- edges_find_neighbors(&next_edges, view, target, NULL,
- check_edge_window, /* use_pending */ false);
+ edges_find_neighbors(&next_edges,
+ view, target, NULL, check_edge_window,
+ /* use_pending */ false, /* ignore_hidden */ true);
}
/* If any "best" edges were encountered during this move, snap motion */
@@ -143,8 +144,9 @@ resistance_resize_apply(struct view *view, struct wlr_box *new_geom)
if (rc.window_edge_strength != 0) {
/* Find any relevant window edges encountered by this move */
- edges_find_neighbors(&next_edges, view, *new_geom, NULL,
- check_edge_window, /* use_pending */ false);
+ edges_find_neighbors(&next_edges,
+ view, *new_geom, NULL, check_edge_window,
+ /* use_pending */ false, /* ignore_hidden */ true);
}
/* If any "best" edges were encountered during this move, snap motion */
diff --git a/src/seat.c b/src/seat.c
index 52b1b5d0..e4e69efe 100644
--- a/src/seat.c
+++ b/src/seat.c
@@ -91,6 +91,24 @@ get_category(struct wlr_input_device *device)
static void
configure_libinput(struct wlr_input_device *wlr_input_device)
{
+ /*
+ * TODO: We do not check any return values for the various
+ * libinput_device_config_*_set_*() calls. It would
+ * be nice if we could inform the users via log file
+ * that some libinput setting could not be applied.
+ *
+ * TODO: We are currently using int32_t with -1 as default
+ * to desribe the not-configured state. This is not
+ * really optimal as we can't properly deal with
+ * enum values that are 0. After some discussion via
+ * IRC the best way forward seem to be to use a
+ * uint32_t instead and UINT32_MAX as indicator for
+ * a not-configured state. This allows to properly
+ * test the enum being a member of a bitset via
+ * mask & value == value. All libinput enums are
+ * way below UINT32_MAX.
+ */
+
if (!wlr_input_device) {
wlr_log(WLR_ERROR, "no wlr_input_device");
return;
@@ -189,6 +207,36 @@ configure_libinput(struct wlr_input_device *wlr_input_device)
wlr_log(WLR_INFO, "dwt configured");
libinput_device_config_dwt_set_enabled(libinput_dev, dc->dwt);
}
+
+ if ((dc->click_method != LIBINPUT_CONFIG_CLICK_METHOD_NONE
+ && (libinput_device_config_click_get_methods(libinput_dev)
+ & dc->click_method) == 0)
+ || dc->click_method < 0) {
+ wlr_log(WLR_INFO, "click method not configured");
+ } else {
+ wlr_log(WLR_INFO, "click method configured");
+
+ /*
+ * Note, the documentation claims that:
+ * > [...] The device may require changing to a neutral state
+ * > first before activating the new method.
+ *
+ * However, just setting the method seems to work without
+ * issues.
+ */
+
+ libinput_device_config_click_set_method(libinput_dev, dc->click_method);
+ }
+
+ if ((dc->send_events_mode != LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
+ && (libinput_device_config_send_events_get_modes(libinput_dev)
+ & dc->send_events_mode) == 0)
+ || dc->send_events_mode < 0) {
+ wlr_log(WLR_INFO, "send events mode not configured");
+ } else {
+ wlr_log(WLR_INFO, "send events mode configured");
+ libinput_device_config_send_events_set_mode(libinput_dev, dc->send_events_mode);
+ }
}
static struct wlr_output *
diff --git a/src/server.c b/src/server.c
index c0ee7682..e1e48dd6 100644
--- a/src/server.c
+++ b/src/server.c
@@ -17,6 +17,7 @@
#include
#if HAVE_XWAYLAND
#include
+#include "xwayland-shell-v1-protocol.h"
#endif
#include "drm-lease-v1-protocol.h"
#include "config/rcxml.h"
@@ -172,9 +173,11 @@ server_global_filter(const struct wl_client *client, const struct wl_global *glo
(void)iface; (void)server;
#if HAVE_XWAYLAND
- struct wl_client *xwayland_client =
- server->xwayland ? server->xwayland->server->client : NULL;
- if (xwayland_client && client == xwayland_client) {
+ struct wl_client *xwayland_client = (server->xwayland && server->xwayland->server)
+ ? server->xwayland->server->client
+ : NULL;
+
+ if (client == xwayland_client) {
/*
* Filter out wp_drm_lease_device_v1 for now as it is resulting in
* issues with Xwayland applications lagging over time.
@@ -184,6 +187,9 @@ server_global_filter(const struct wl_client *client, const struct wl_global *glo
if (!strcmp(iface->name, wp_drm_lease_device_v1_interface.name)) {
return false;
}
+ } else if (!strcmp(iface->name, xwayland_shell_v1_interface.name)) {
+ /* Filter out the xwayland shell for usual clients */
+ return false;
}
#endif
diff --git a/src/snap.c b/src/snap.c
index 1215f0be..0db5d11c 100644
--- a/src/snap.c
+++ b/src/snap.c
@@ -121,8 +121,9 @@ snap_move_to_edge(struct view *view, enum view_edge direction,
struct border next_edges;
edges_initialize(&next_edges);
- edges_find_neighbors(&next_edges, view, target,
- output, check_edge, /* use_pending */ true);
+ edges_find_neighbors(&next_edges,
+ view, target, output, check_edge,
+ /* use_pending */ true, /* ignore_hidden */ false);
/* If any "best" edges were encountered, limit motion */
edges_adjust_move_coords(view, next_edges,
@@ -196,8 +197,9 @@ snap_grow_to_next_edge(struct view *view, enum view_edge direction,
edges_initialize(&next_edges);
/* Limit motion to any intervening edge of other views on this output */
- edges_find_neighbors(&next_edges, view, *geo,
- output, check_edge, /* use_pending */ true);
+ edges_find_neighbors(&next_edges,
+ view, *geo, output, check_edge,
+ /* use_pending */ true, /* ignore_hidden */ false);
edges_adjust_resize_geom(view, next_edges,
resize_edges, geo, /* use_pending */ true);
}
@@ -255,8 +257,9 @@ snap_shrink_to_next_edge(struct view *view, enum view_edge direction,
view->output, check_edge, /* use_pending */ true);
/* Limit motion to any intervening edge of ther views on this output */
- edges_find_neighbors(&next_edges, view, *geo,
- view->output, check_edge, /* use_pending */ true);
+ edges_find_neighbors(&next_edges,
+ view, *geo, view->output, check_edge,
+ /* use_pending */ true, /* ignore_hidden */ false);
edges_adjust_resize_geom(view, next_edges,
resize_edges, geo, /* use_pending */ true);
diff --git a/src/view.c b/src/view.c
index 87b29e00..52627643 100644
--- a/src/view.c
+++ b/src/view.c
@@ -2,6 +2,7 @@
#include
#include
#include
+#include
#include "common/macros.h"
#include "common/match.h"
#include "common/mem.h"
@@ -1519,8 +1520,44 @@ view_on_output_destroy(struct view *view)
view->output = NULL;
}
+static enum wlr_direction
+opposite_direction(enum wlr_direction direction)
+{
+ switch (direction) {
+ case WLR_DIRECTION_RIGHT:
+ return WLR_DIRECTION_LEFT;
+ case WLR_DIRECTION_LEFT:
+ return WLR_DIRECTION_RIGHT;
+ case WLR_DIRECTION_DOWN:
+ return WLR_DIRECTION_UP;
+ case WLR_DIRECTION_UP:
+ return WLR_DIRECTION_DOWN;
+ default:
+ return 0;
+ }
+}
+
+static enum wlr_direction
+get_wlr_direction(enum view_edge edge)
+{
+ switch (edge) {
+ case VIEW_EDGE_LEFT:
+ return WLR_DIRECTION_LEFT;
+ case VIEW_EDGE_RIGHT:
+ return WLR_DIRECTION_RIGHT;
+ case VIEW_EDGE_UP:
+ return WLR_DIRECTION_UP;
+ case VIEW_EDGE_DOWN:
+ return WLR_DIRECTION_DOWN;
+ case VIEW_EDGE_CENTER:
+ case VIEW_EDGE_INVALID:
+ default:
+ return 0;
+ }
+}
+
struct output *
-view_get_adjacent_output(struct view *view, enum view_edge edge)
+view_get_adjacent_output(struct view *view, enum view_edge edge, bool wrap)
{
assert(view);
struct output *output = view->output;
@@ -1530,32 +1567,31 @@ view_get_adjacent_output(struct view *view, enum view_edge edge)
return NULL;
}
+ struct wlr_box box = output_usable_area_in_layout_coords(output);
+ int lx = box.x + box.width / 2;
+ int ly = box.y + box.height / 2;
+
/* Determine any adjacent output in the appropriate direction */
struct wlr_output *new_output = NULL;
struct wlr_output *current_output = output->wlr_output;
struct wlr_output_layout *layout = view->server->output_layout;
- switch (edge) {
- case VIEW_EDGE_LEFT:
- new_output = wlr_output_layout_adjacent_output(
- layout, WLR_DIRECTION_LEFT, current_output, 1, 0);
- break;
- case VIEW_EDGE_RIGHT:
- new_output = wlr_output_layout_adjacent_output(
- layout, WLR_DIRECTION_RIGHT, current_output, 1, 0);
- break;
- case VIEW_EDGE_UP:
- new_output = wlr_output_layout_adjacent_output(
- layout, WLR_DIRECTION_UP, current_output, 0, 1);
- break;
- case VIEW_EDGE_DOWN:
- new_output = wlr_output_layout_adjacent_output(
- layout, WLR_DIRECTION_DOWN, current_output, 0, 1);
- break;
- default:
- break;
+ enum wlr_direction direction = get_wlr_direction(edge);
+ new_output = wlr_output_layout_adjacent_output(layout, direction,
+ current_output, lx, ly);
+
+ /*
+ * Optionally wrap around from top-to-bottom or left-to-right, and vice
+ * versa.
+ */
+ if (wrap && !new_output) {
+ new_output = wlr_output_layout_farthest_output(layout,
+ opposite_direction(direction), current_output, lx, ly);
}
- /* When "adjacent" output is the same as the original, there is no adjacent */
+ /*
+ * When "adjacent" output is the same as the original, there is no
+ * adjacent
+ */
if (!new_output || new_output == current_output) {
return NULL;
}
@@ -1629,7 +1665,8 @@ view_move_to_edge(struct view *view, enum view_edge direction, bool snap_to_wind
}
/* Otherwise, move to edge of next adjacent display, if possible */
- struct output *output = view_get_adjacent_output(view, direction);
+ struct output *output =
+ view_get_adjacent_output(view, direction, /* wrap */ false);
if (!output) {
return;
}
@@ -1788,7 +1825,7 @@ view_snap_to_edge(struct view *view, enum view_edge edge,
if (across_outputs && view->tiled == edge && view->maximized == VIEW_AXIS_NONE) {
/* We are already tiled for this edge; try to switch outputs */
- output = view_get_adjacent_output(view, edge);
+ output = view_get_adjacent_output(view, edge, /* wrap */ false);
if (!output) {
/*
diff --git a/src/xdg-popup.c b/src/xdg-popup.c
index 496c5c0c..65c13e8d 100644
--- a/src/xdg-popup.c
+++ b/src/xdg-popup.c
@@ -16,15 +16,17 @@ struct xdg_popup {
struct view *parent_view;
struct wlr_xdg_popup *wlr_popup;
+ struct wl_listener commit;
struct wl_listener destroy;
struct wl_listener new_popup;
};
static void
-popup_unconstrain(struct view *view, struct wlr_xdg_popup *popup)
+popup_unconstrain(struct xdg_popup *popup)
{
+ struct view *view = popup->parent_view;
struct server *server = view->server;
- struct wlr_box *popup_box = &popup->current.geometry;
+ struct wlr_box *popup_box = &popup->wlr_popup->current.geometry;
struct wlr_output_layout *output_layout = server->output_layout;
struct wlr_output *wlr_output = wlr_output_layout_output_at(
output_layout, view->current.x + popup_box->x,
@@ -39,7 +41,7 @@ popup_unconstrain(struct view *view, struct wlr_xdg_popup *popup)
.width = output_box.width,
.height = output_box.height,
};
- wlr_xdg_popup_unconstrain_from_box(popup, &output_toplevel_box);
+ wlr_xdg_popup_unconstrain_from_box(popup->wlr_popup, &output_toplevel_box);
}
static void
@@ -48,9 +50,28 @@ handle_xdg_popup_destroy(struct wl_listener *listener, void *data)
struct xdg_popup *popup = wl_container_of(listener, popup, destroy);
wl_list_remove(&popup->destroy.link);
wl_list_remove(&popup->new_popup.link);
+
+ /* Usually already removed unless there was no commit at all */
+ if (popup->commit.notify) {
+ wl_list_remove(&popup->commit.link);
+ }
free(popup);
}
+static void
+handle_xdg_popup_commit(struct wl_listener *listener, void *data)
+{
+ struct xdg_popup *popup = wl_container_of(listener, popup, commit);
+
+ if (popup->wlr_popup->base->initial_commit) {
+ popup_unconstrain(popup);
+
+ /* Prevent getting called over and over again */
+ wl_list_remove(&popup->commit.link);
+ popup->commit.notify = NULL;
+ }
+}
+
static void
popup_handle_new_xdg_popup(struct wl_listener *listener, void *data)
{
@@ -75,9 +96,13 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup)
popup->destroy.notify = handle_xdg_popup_destroy;
wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
+
popup->new_popup.notify = popup_handle_new_xdg_popup;
wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
+ popup->commit.notify = handle_xdg_popup_commit;
+ wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
+
/*
* We must add xdg popups to the scene graph so they get rendered. The
* wlroots scene graph provides a helper for this, but to use it we must
@@ -100,6 +125,4 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup)
wlr_scene_xdg_surface_create(parent_tree, wlr_popup->base);
node_descriptor_create(wlr_popup->base->surface->data,
LAB_NODE_DESC_XDG_POPUP, view);
-
- popup_unconstrain(view, wlr_popup);
}