From b63c6a0fc2bd5be0c142a6f5e2c64d56a7a8eb81 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Sun, 16 Jun 2024 08:47:48 +0200 Subject: [PATCH 001/232] action: add ToggleTabletMouseEmulation Useful for switching between application-defined and mapped tablet pad buttons. Also for quickly troubleshooting tablet behavior. --- docs/labwc-actions.5.scd | 3 +++ docs/labwc-config.5.scd | 3 +++ src/action.c | 5 +++++ 3 files changed, 11 insertions(+) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index a8d8bba5..50f6ad33 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -286,6 +286,9 @@ Actions are used in menus and keyboard/mouse bindings. decorations (including those for which the server-side titlebar has been hidden) are not eligible for shading. +** + Toggle mouse emulation for drawing tablets on or off. + ** Toggle the screen magnifier on or off at the last magnification level used. diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index f2088771..85a8d437 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -684,6 +684,9 @@ extending outward from the snapped edge. tablet specific restrictions, e.g. no support for drag-and-drop, but also omits tablet specific features like reporting pen pressure. + Use the *ToggleTabletMouseEmulation* action for toggling between + mouse emulation on and off. + ** Pen and pad buttons behave like regular mouse buttons.With mouse emulation set to "no", which is the default, and if not specified diff --git a/src/action.c b/src/action.c index 135af253..80231b95 100644 --- a/src/action.c +++ b/src/action.c @@ -114,6 +114,7 @@ enum action_type { ACTION_TYPE_SHADE, ACTION_TYPE_UNSHADE, ACTION_TYPE_TOGGLE_SHADE, + ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION, ACTION_TYPE_TOGGLE_MAGNIFY, ACTION_TYPE_ZOOM_IN, ACTION_TYPE_ZOOM_OUT @@ -172,6 +173,7 @@ const char *action_names[] = { "Shade", "Unshade", "ToggleShade", + "ToggleTabletMouseEmulation", "ToggleMagnify", "ZoomIn", "ZoomOut", @@ -1111,6 +1113,9 @@ actions_run(struct view *activator, struct server *server, view_set_shade(view, false); } break; + case ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION: + rc.tablet.force_mouse_emulation = !rc.tablet.force_mouse_emulation; + break; case ACTION_TYPE_TOGGLE_MAGNIFY: magnify_toggle(server); break; From 1495708095b998f03aa8f07a6796764981e2dcd6 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Sun, 16 Jun 2024 09:10:46 +0200 Subject: [PATCH 002/232] input: test if tablet device is a libinput device Otherwise we might end with a failed assertion if the tablet or pad isn't a libinput device. Fixes https://github.com/labwc/labwc/issues/1916 --- src/input/tablet-pad.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/input/tablet-pad.c b/src/input/tablet-pad.c index 1729e356..37f597ff 100644 --- a/src/input/tablet-pad.c +++ b/src/input/tablet-pad.c @@ -25,12 +25,25 @@ tablet_pad_attach_tablet(struct seat *seat) /* loop over all tablets and all pads and link by device group */ struct drawing_tablet *tablet; wl_list_for_each(tablet, &seat->tablets, link) { + if (!wlr_input_device_is_libinput(tablet->wlr_input_device)) { + /* + * Prevent iterating over non-libinput devices. This might + * be the case when a tablet is exposed by the Wayland + * protocol backend when running labwc as a nested compositor. + */ + continue; + } + struct libinput_device *tablet_device = wlr_libinput_get_device_handle(tablet->wlr_input_device); struct libinput_device_group *tablet_group = libinput_device_get_device_group(tablet_device); wl_list_for_each(pad, &seat->tablet_pads, link) { + if (!wlr_input_device_is_libinput(pad->wlr_input_device)) { + continue; + } + struct libinput_device *pad_device = wlr_libinput_get_device_handle(pad->wlr_input_device); struct libinput_device_group *pad_group = From edd674f96be7fe0684157b867fe0bdf70c5a4ad3 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 23 Jun 2024 06:46:16 +0900 Subject: [PATCH 003/232] po/meson.build: minor formatting --- po/meson.build | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/po/meson.build b/po/meson.build index 708d4aad..bcc74cd3 100644 --- a/po/meson.build +++ b/po/meson.build @@ -1,11 +1,18 @@ i18n = import('i18n') -add_project_arguments('-DGETTEXT_PACKAGE="' + meson.project_name() + '"', + +add_project_arguments( + '-DGETTEXT_PACKAGE="' + meson.project_name() + '"', '-DLOCALEDIR="' + get_option('prefix') / get_option('localedir') + '"', - language:'c') -i18n.gettext(meson.project_name(), - args: ['--directory=' + source_root, + language:'c', +) + +i18n.gettext( + meson.project_name(), + args: [ + '--directory=' + source_root, '--add-comments=TRANSLATORS', '--keyword=_', - '--msgid-bugs=https://github.com/labwc/labwc/issues'], - preset: 'glib' + '--msgid-bugs=https://github.com/labwc/labwc/issues', + ], + preset: 'glib', ) From 2e00630d802593f3091a2686223d727190c32abb Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 23 Jun 2024 06:51:22 +0900 Subject: [PATCH 004/232] src/main.c: initialize locale after reading environments file This makes the locale for client-menu items and workspace names follow the env var `LANG` defined in `~/.config/environments`. --- src/main.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main.c b/src/main.c index e974a35f..6d3200cd 100644 --- a/src/main.c +++ b/src/main.c @@ -112,11 +112,6 @@ idle_callback(void *data) int main(int argc, char *argv[]) { -#if HAVE_NLS - setlocale(LC_ALL, ""); - bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); - textdomain(GETTEXT_PACKAGE); -#endif char *startup_cmd = NULL; char *primary_client = NULL; enum wlr_log_importance verbosity = WLR_ERROR; @@ -173,6 +168,14 @@ main(int argc, char *argv[]) die_on_detecting_suid(); session_environment_init(); + +#if HAVE_NLS + /* Initialize locale after setting env vars */ + setlocale(LC_ALL, ""); + bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); + textdomain(GETTEXT_PACKAGE); +#endif + rcxml_read(rc.config_file); /* From 832c007e6bf352df2ef2e52fc2845e3bd05d2e64 Mon Sep 17 00:00:00 2001 From: stefonarch Date: Sun, 23 Jun 2024 12:56:59 +0200 Subject: [PATCH 005/232] Updated labwc.pot file --- po/labwc.pot | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/po/labwc.pot b/po/labwc.pot index ea893ecd..b177829e 100644 --- a/po/labwc.pot +++ b/po/labwc.pot @@ -17,54 +17,54 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +msgid "Roll Up/Down" msgstr "" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +msgid "Move Left" msgstr "" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +msgid "Move Right" msgstr "" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "" From 7440c5096a3c27ca404a3492fc5a8f9e47180578 Mon Sep 17 00:00:00 2001 From: Weblate Date: Sun, 23 Jun 2024 14:19:01 +0200 Subject: [PATCH 006/232] Translation updates from weblate Co-authored-by: Weblate Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/ Translation: Labwc/labwc --- po/ar.po | 38 ++++++++++++++++++++++---------------- po/cs.po | 38 ++++++++++++++++++++++---------------- po/de.po | 38 ++++++++++++++++++++++---------------- po/el.po | 41 +++++++++++++++++++++++------------------ po/es.po | 38 ++++++++++++++++++++++---------------- po/et.po | 38 ++++++++++++++++++++++---------------- po/eu.po | 38 ++++++++++++++++++++++---------------- po/fa.po | 32 ++++++++++++++++---------------- po/fi.po | 41 +++++++++++++++++++++++------------------ po/fr.po | 41 +++++++++++++++++++++++------------------ po/gl.po | 38 ++++++++++++++++++++++---------------- po/hu.po | 38 ++++++++++++++++++++++---------------- po/id.po | 38 ++++++++++++++++++++++---------------- po/it.po | 38 ++++++++++++++++++++++---------------- po/ja.po | 38 ++++++++++++++++++++++---------------- po/ka.po | 36 ++++++++++++++++++++---------------- po/ko.po | 32 ++++++++++++++++---------------- po/lt.po | 38 ++++++++++++++++++++++---------------- po/nl.po | 38 ++++++++++++++++++++++---------------- po/pa.po | 38 ++++++++++++++++++++++---------------- po/pl.po | 38 ++++++++++++++++++++++---------------- po/pt.po | 41 +++++++++++++++++++++++------------------ po/ru.po | 38 ++++++++++++++++++++++---------------- po/sv.po | 38 ++++++++++++++++++++++---------------- po/tr.po | 38 ++++++++++++++++++++++---------------- po/uk.po | 38 ++++++++++++++++++++++---------------- po/zh_CN.po | 38 ++++++++++++++++++++++---------------- 27 files changed, 584 insertions(+), 440 deletions(-) diff --git a/po/ar.po b/po/ar.po index af13bbcf..685063fe 100644 --- a/po/ar.po +++ b/po/ar.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "إعادة تهيئة" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "أخرج" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "صغّر" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "كبّر" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "ملء الشاشة" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "طي/إلغاء طي" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "إخف الإطار" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "دائمًا في القمة" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "أنقل يسارا" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "أنقل يمينا" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "أظهر في مساحات العمل دائما" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "مساحة عمل" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "غلق" diff --git a/po/cs.po b/po/cs.po index 7077c59b..aa9af0f4 100644 --- a/po/cs.po +++ b/po/cs.po @@ -16,54 +16,60 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Překonfigurovat" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Odejít" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Minimalizovat" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Maximalizovat" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Na celou obrazovku" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Rolovat nahoru/dolů" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Dekorace" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Vždy nahoře" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Posunout doleva" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Posunout doprava" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Vždy na viditelné Pracovní Ploše" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Pracovní Plocha" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Zavřít" diff --git a/po/de.po b/po/de.po index 2b293c4e..5467e8eb 100644 --- a/po/de.po +++ b/po/de.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Rekonfigurieren" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Beenden" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Minimieren" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Maximieren" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Vollbild" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Hoch/Runterrollen" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Dekorationen" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Immer im Vordergrund" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "nach links" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "nach rechts" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Immer auf aktiver Arbeitsfläche" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Arbeitsfläche" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Schließen" diff --git a/po/el.po b/po/el.po index 6d67478e..11a41b11 100644 --- a/po/el.po +++ b/po/el.po @@ -10,8 +10,7 @@ msgstr "" "POT-Creation-Date: 2024-03-17 11:06+1000\n" "PO-Revision-Date: 2024-04-20 15:23+0000\n" "Last-Translator: Yannis Drougas \n" -"Language-Team: Greek \n" +"Language-Team: Greek \n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Επαναρύθμιση" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Έξοδος" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Ελαχιστοποίηση" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Μεγιστοποίηση" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Πλήρης οθόνη" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Τύλιγμα/Ξετύλιγμα" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Διακόσμηση" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Πάντα στο προσκήνιο" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Στα αριστερά" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Στα δεξιά" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Πάντα στην ενεργή επιφάνεια εργασίας" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Επιφάνεια εργασίας" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Κλείσιμο" diff --git a/po/es.po b/po/es.po index 66a402af..96881661 100644 --- a/po/es.po +++ b/po/es.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Reconfigurar" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Salir" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Minimizar" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Maximizar" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Pantalla completa" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Enrollar hacia arriba/abajo" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Decoraciones" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Siempre encima" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Mover a la izquierda" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Mover a la derecha" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Siempre en un espacio de trabajo visible" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Espacio de trabajo" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Cerrar" diff --git a/po/et.po b/po/et.po index c7074bb4..5849bea1 100644 --- a/po/et.po +++ b/po/et.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Seadista uuesti" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Välju" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Vähenda" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Suurenda" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Täisekraanivaade" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Keri üles/alla" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Akende välimus" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Alati kõige peal" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Tõsta vasakule" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Tõsta paremale" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Alati nähtav töölaual" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Töölaud" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Sulge" diff --git a/po/eu.po b/po/eu.po index 73696473..5382fe9c 100644 --- a/po/eu.po +++ b/po/eu.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Berriz konfiguratu" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Irten" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Minimizatu" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Maximizatu" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Pantaila osoa" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Ibili gora/behera" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Apaingarriak" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Beti gainean" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Mugitu ezkerrera" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Mugitu eskuinera" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Beti ikusgai dagoen lan-eremuan" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Langunea" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Itxi" diff --git a/po/fa.po b/po/fa.po index 2112ef54..321300bf 100644 --- a/po/fa.po +++ b/po/fa.po @@ -16,54 +16,54 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +msgid "Roll Up/Down" msgstr "" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +msgid "Move Left" msgstr "" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +msgid "Move Right" msgstr "" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "" diff --git a/po/fi.po b/po/fi.po index 53a2d01f..8dd79bc6 100644 --- a/po/fi.po +++ b/po/fi.po @@ -10,8 +10,7 @@ msgstr "" "POT-Creation-Date: 2024-03-17 11:06+1000\n" "PO-Revision-Date: 2024-04-20 15:23+0000\n" "Last-Translator: Jouni Järvinen \n" -"Language-Team: Finnish \n" +"Language-Team: Finnish \n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Uudelleenmääritä" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Poistu" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Pienennä" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Suurenna" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Koko näyttö" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Rullaa ylös/alas" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Kehykset" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Aina ylimpänä" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Siirrä vasemmalle" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Siirrä oikealle" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Aina näkyvässä työtilassa" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Työtila" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Sulje" diff --git a/po/fr.po b/po/fr.po index f7b5466c..dc50c5d4 100644 --- a/po/fr.po +++ b/po/fr.po @@ -10,8 +10,7 @@ msgstr "" "POT-Creation-Date: 2024-03-17 11:06+1000\n" "PO-Revision-Date: 2024-03-20 18:15+0000\n" "Last-Translator: rico542 \n" -"Language-Team: French \n" +"Language-Team: French \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Reconfigurer" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Quitter" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Minimiser" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Maximiser" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Plein écran" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Enrouler/Dérouler" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Décorations" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Toujours au dessus" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Bouger à gauche" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Bouger à droite" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Toujours sur le bureau visible" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Bureau" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Fermer" diff --git a/po/gl.po b/po/gl.po index b5c42a49..ccdac6cf 100644 --- a/po/gl.po +++ b/po/gl.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Reconfigurar" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Saír" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Minimizar" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Maximizar" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Pantalla completa" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Rodar cara arriba/abaixo" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Decoracións" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Sempre enriba" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Mover á esquerda" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Mover á dereita" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Sempre no espazo de traballo visible" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Espazo de traballo" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Pechar" diff --git a/po/hu.po b/po/hu.po index 1c7b70df..24f6fc13 100644 --- a/po/hu.po +++ b/po/hu.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Rekonfigurál" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Kilépés" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Kis méret" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Teljes méret" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Teljes képernyő" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Felhúz / Legördül" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Dekorációk" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Mindig felül" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Balra dokkol" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Jobbra dokkol" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Kitűz" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Munkaasztal" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Bezárás" diff --git a/po/id.po b/po/id.po index 32915663..c4f3c248 100644 --- a/po/id.po +++ b/po/id.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Atur Ulang" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Keluar" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Sembunyikan" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Perluas Jendela" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Tampil Menyeluruh" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Gulir atas/bawah" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Dekorasi" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Selalu di Muka" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Geser ke Kiri" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Geser ke Kanan" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Selalu pada Ruang Kerja yang Terlihat" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Ruang Kerja" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Tutup" diff --git a/po/it.po b/po/it.po index 49c330ee..b4fa2a4e 100644 --- a/po/it.po +++ b/po/it.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Riconfigura" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Esci" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Minimizza" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Massimizza" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Schermo intero" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Arrotola/srotola" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Decorazioni" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Sempre sopra" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Sposta a sinistra" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Sposta a destra" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Sempre sull'area di lavoro visibile" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Area di lavoro" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Chiudi" diff --git a/po/ja.po b/po/ja.po index d8445cfa..52968d18 100644 --- a/po/ja.po +++ b/po/ja.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "再設定" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "終了" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "最小化" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "最大化" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "フルスクリーン" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "折りたたむ/広げる" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "デコレーション" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "常に最前面に表示" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "左に移動" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "右に移動" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "現在のワークスペースに常に表示" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "ワークスペース" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "閉じる" diff --git a/po/ka.po b/po/ka.po index 470d3cc0..fd9b0b36 100644 --- a/po/ka.po +++ b/po/ka.po @@ -18,56 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.2.2\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "თავიდან მორგება" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "გასვლა" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "ჩაკეცვა" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "გადიდება" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "მთელ ეკრანზე" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +msgid "Roll Up/Down" msgstr "" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "დეკორაციები" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 #, fuzzy #| msgid "Always On Top" msgid "Always on Top" msgstr "ყოველთვისყველაზეზემოდან" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "მარცხნივ გაწევა" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "მარჯვნივ გაწევა" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "სამუშაო ადგილი" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "დახურვა" diff --git a/po/ko.po b/po/ko.po index ec2bf863..27408cd4 100644 --- a/po/ko.po +++ b/po/ko.po @@ -16,54 +16,54 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +msgid "Roll Up/Down" msgstr "" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +msgid "Move Left" msgstr "" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +msgid "Move Right" msgstr "" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "" diff --git a/po/lt.po b/po/lt.po index 22235c49..8b51f763 100644 --- a/po/lt.po +++ b/po/lt.po @@ -18,54 +18,60 @@ msgstr "" "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:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Konfigūruoti iš naujo" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Išeiti" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Suskleisti" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Išskleisti" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Visas ekranas" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Užraityti/atraityti" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Dekoracijos" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Visada viršuje" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Perkelti kairėn" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Perkelti dešinėn" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Visada matomoje darbo srityje" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Darbo sritis" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Užverti" diff --git a/po/nl.po b/po/nl.po index b34c1f73..be05af85 100644 --- a/po/nl.po +++ b/po/nl.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Opnieuw instellen" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Afsluiten" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Minimaliseren" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Maximaliseren" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Schermvullende weergave" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Op-/Afrollen" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Decoraties" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Altijd bovenaan" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Naar links verplaatsen" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Naar rechts verplaatsen" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Altijd op zichtbaar werkblad" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Werkblad" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Sluiten" diff --git a/po/pa.po b/po/pa.po index c40d76f2..a8063718 100644 --- a/po/pa.po +++ b/po/pa.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "ਮੁੜ-ਸੰਰਚਨਾ ਕਰੋ" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "ਬਾਹਰ" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "ਘੱਟੋ-ਘੱਟ" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "ਵੱਧ ਤੋਂ ਵੱਧ" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "ਪੂਰੀ ਸਕਰੀਨ" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "ਉੱਤੇ/ਹੇਠਾਂ ਸਕਰਾਓ" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "ਸਜਾਵਟ" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "ਹਮੇਸ਼ਾਂ ਉੱਤੇ ਰੱਖੋ" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "ਖੱਬੇ ਭੇਜੋ" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "ਸੱਜੇ ਭੇਜੋ" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "ਹਮੇਸ਼ਾਂ ਦਿੱਖ ਵਰਕਸਪੇਸ ਉੱਤੇ" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "ਵਰਕਸਪੇਸ" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "ਬੰਦ ਕਰੋ" diff --git a/po/pl.po b/po/pl.po index 37374a5a..18dde305 100644 --- a/po/pl.po +++ b/po/pl.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=3; plural=n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Rekonfiguruj" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Wyjdź" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Minimalizuj" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Maksymalizuj" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Pełny ekran" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Zwiń w górę/w dół" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Dekoracje" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Zawsze na wierzchu" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Przenieś w lewo" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Przenieś w prawo" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Zawsze na widocznym obszarze roboczym" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Przestrzeń robocza" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Zamknij" diff --git a/po/pt.po b/po/pt.po index 330f6024..827552d2 100644 --- a/po/pt.po +++ b/po/pt.po @@ -10,8 +10,7 @@ msgstr "" "POT-Creation-Date: 2024-03-17 11:06+1000\n" "PO-Revision-Date: 2024-03-27 22:23+0000\n" "Last-Translator: Hugo Carvalho \n" -"Language-Team: Portuguese \n" +"Language-Team: Portuguese \n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -19,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Reconfigurar" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Sair" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Minimizar" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Maximizar" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Ecrã inteiro" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Rolar para cima/baixo" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Decorações" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Sempre no topo" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Mover para a esquerda" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Mover para a direita" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Sempre visível na área de trabalho" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Área de trabalho" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Fechar" diff --git a/po/ru.po b/po/ru.po index 282fc429..c7c6cf61 100644 --- a/po/ru.po +++ b/po/ru.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Перенастроить" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Выход" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Свернуть" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Развернуть" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "На весь экран" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Свернуть/развернуть в заголовок" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Декорации" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Всегда на переднем плане" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Переместить влево" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Переместить вправо" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "На всех рабочих пространствах" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Рабочее пространство" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Закрыть" diff --git a/po/sv.po b/po/sv.po index a55e17c6..82d26b92 100644 --- a/po/sv.po +++ b/po/sv.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Konfigurera om" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Avsluta" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Minimera" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Maximera" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Fullskärm" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Rulla upp/ned" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Dekorationer" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Alltid överst" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Flytta till vänster" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Flytta till höger" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Alltid på aktiv arbetsyta" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Arbetsyta" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Stäng" diff --git a/po/tr.po b/po/tr.po index 36ed1252..8bf5a992 100644 --- a/po/tr.po +++ b/po/tr.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Yeniden Yapılandır" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Çıkış" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Küçült" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Büyüt" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "Tam Ekran" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Yukarı/aşağı katla" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Süslemeler" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Her Zaman Üstte" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Sola taşı" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Sağa taşı" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Her Zaman Görünür Çalışma Alanında" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Çalışma Alanı" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Kapat" diff --git a/po/uk.po b/po/uk.po index 4ac221f7..1d98701d 100644 --- a/po/uk.po +++ b/po/uk.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "Переналаштувати" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "Вихід" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "Згорнути" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "Розгорнути" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "На весь екран" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "Згорнути/розгорнути" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "Декорації" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "Завжди зверху" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "Перемістити ліворуч" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "Перемістити праворуч" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "Завжди на видимому робочому просторі" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "Робочий простір" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "Закрити" diff --git a/po/zh_CN.po b/po/zh_CN.po index 43e1526b..eeff7499 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -18,54 +18,60 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Weblate 4.2.1\n" -#: src/menu/menu.c:704 +#: src/menu/menu.c:829 msgid "Reconfigure" msgstr "配置重载" -#: src/menu/menu.c:706 +#: src/menu/menu.c:831 msgid "Exit" msgstr "退出" -#: src/menu/menu.c:722 +#: src/menu/menu.c:847 msgid "Minimize" msgstr "最小化" -#: src/menu/menu.c:724 +#: src/menu/menu.c:849 msgid "Maximize" msgstr "最大化" -#: src/menu/menu.c:726 +#: src/menu/menu.c:851 msgid "Fullscreen" msgstr "全屏" -#: src/menu/menu.c:728 -msgid "Roll up/down" +#: src/menu/menu.c:853 +#, fuzzy +#| msgid "Roll up/down" +msgid "Roll Up/Down" msgstr "滚动 上/下" -#: src/menu/menu.c:730 +#: src/menu/menu.c:855 msgid "Decorations" msgstr "装饰" -#: src/menu/menu.c:732 +#: src/menu/menu.c:857 msgid "Always on Top" msgstr "最上层显示" -#: src/menu/menu.c:737 -msgid "Move left" +#: src/menu/menu.c:862 +#, fuzzy +#| msgid "Move left" +msgid "Move Left" msgstr "左移" -#: src/menu/menu.c:744 -msgid "Move right" +#: src/menu/menu.c:869 +#, fuzzy +#| msgid "Move right" +msgid "Move Right" msgstr "右移" -#: src/menu/menu.c:749 +#: src/menu/menu.c:874 msgid "Always on Visible Workspace" msgstr "始终在可见工作区" -#: src/menu/menu.c:752 src/config/rcxml.c:1384 +#: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" msgstr "工作区" -#: src/menu/menu.c:755 +#: src/menu/menu.c:880 msgid "Close" msgstr "关闭" From 9fc41a9730fcdeeefd69de6a9f35774e30f0ce26 Mon Sep 17 00:00:00 2001 From: Weblate Date: Mon, 24 Jun 2024 21:19:25 +0200 Subject: [PATCH 007/232] Translation updates from weblate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Abdullah Albaroty Co-authored-by: Jan Rolski Co-authored-by: Priit Jõerüüt Co-authored-by: Standreas Co-authored-by: Weblate Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/ar/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/de/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/et/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/it/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/pl/ Translation: Labwc/labwc --- po/ar.po | 8 +------- po/cs.po | 3 --- po/de.po | 8 +------- po/el.po | 3 --- po/es.po | 3 --- po/et.po | 8 +------- po/eu.po | 3 --- po/fi.po | 3 --- po/fr.po | 3 --- po/gl.po | 3 --- po/hu.po | 3 --- po/id.po | 3 --- po/it.po | 8 +------- po/ja.po | 3 --- po/ka.po | 3 --- po/lt.po | 3 --- po/nl.po | 3 --- po/pa.po | 3 --- po/pl.po | 8 +------- po/pt.po | 3 --- po/ru.po | 3 --- po/sv.po | 3 --- po/tr.po | 3 --- po/uk.po | 3 --- po/zh_CN.po | 3 --- 25 files changed, 5 insertions(+), 95 deletions(-) diff --git a/po/ar.po b/po/ar.po index 685063fe..f2f25e52 100644 --- a/po/ar.po +++ b/po/ar.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-03-17 11:06+1000\n" -"PO-Revision-Date: 2024-03-11 10:23+0000\n" +"PO-Revision-Date: 2024-06-24 19:19+0000\n" "Last-Translator: Abdullah Albaroty \n" "Language-Team: Arabic \n" "Language: ar\n" @@ -39,8 +39,6 @@ msgid "Fullscreen" msgstr "ملء الشاشة" #: src/menu/menu.c:853 -#, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "طي/إلغاء طي" @@ -53,14 +51,10 @@ msgid "Always on Top" msgstr "دائمًا في القمة" #: src/menu/menu.c:862 -#, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "أنقل يسارا" #: src/menu/menu.c:869 -#, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "أنقل يمينا" diff --git a/po/cs.po b/po/cs.po index aa9af0f4..993acf81 100644 --- a/po/cs.po +++ b/po/cs.po @@ -38,7 +38,6 @@ msgstr "Na celou obrazovku" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Rolovat nahoru/dolů" @@ -52,13 +51,11 @@ msgstr "Vždy nahoře" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Posunout doleva" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Posunout doprava" diff --git a/po/de.po b/po/de.po index 5467e8eb..558e1abc 100644 --- a/po/de.po +++ b/po/de.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-03-17 11:06+1000\n" -"PO-Revision-Date: 2024-03-04 07:14+0000\n" +"PO-Revision-Date: 2024-06-23 13:42+0000\n" "Last-Translator: Standreas \n" "Language-Team: German \n" "Language: de\n" @@ -39,8 +39,6 @@ msgid "Fullscreen" msgstr "Vollbild" #: src/menu/menu.c:853 -#, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Hoch/Runterrollen" @@ -53,14 +51,10 @@ msgid "Always on Top" msgstr "Immer im Vordergrund" #: src/menu/menu.c:862 -#, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "nach links" #: src/menu/menu.c:869 -#, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "nach rechts" diff --git a/po/el.po b/po/el.po index 11a41b11..5e6b9126 100644 --- a/po/el.po +++ b/po/el.po @@ -40,7 +40,6 @@ msgstr "Πλήρης οθόνη" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Τύλιγμα/Ξετύλιγμα" @@ -54,13 +53,11 @@ msgstr "Πάντα στο προσκήνιο" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Στα αριστερά" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Στα δεξιά" diff --git a/po/es.po b/po/es.po index 96881661..58f2fc53 100644 --- a/po/es.po +++ b/po/es.po @@ -40,7 +40,6 @@ msgstr "Pantalla completa" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Enrollar hacia arriba/abajo" @@ -54,13 +53,11 @@ msgstr "Siempre encima" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Mover a la izquierda" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Mover a la derecha" diff --git a/po/et.po b/po/et.po index 5849bea1..fa579dcb 100644 --- a/po/et.po +++ b/po/et.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-03-17 11:06+1000\n" -"PO-Revision-Date: 2024-01-23 09:23+0000\n" +"PO-Revision-Date: 2024-06-24 19:19+0000\n" "Last-Translator: Priit Jõerüüt \n" "Language-Team: Estonian \n" "Language: et\n" @@ -39,8 +39,6 @@ msgid "Fullscreen" msgstr "Täisekraanivaade" #: src/menu/menu.c:853 -#, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Keri üles/alla" @@ -53,14 +51,10 @@ msgid "Always on Top" msgstr "Alati kõige peal" #: src/menu/menu.c:862 -#, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Tõsta vasakule" #: src/menu/menu.c:869 -#, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Tõsta paremale" diff --git a/po/eu.po b/po/eu.po index 5382fe9c..6f95ab81 100644 --- a/po/eu.po +++ b/po/eu.po @@ -40,7 +40,6 @@ msgstr "Pantaila osoa" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Ibili gora/behera" @@ -54,13 +53,11 @@ msgstr "Beti gainean" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Mugitu ezkerrera" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Mugitu eskuinera" diff --git a/po/fi.po b/po/fi.po index 8dd79bc6..ec84446f 100644 --- a/po/fi.po +++ b/po/fi.po @@ -40,7 +40,6 @@ msgstr "Koko näyttö" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Rullaa ylös/alas" @@ -54,13 +53,11 @@ msgstr "Aina ylimpänä" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Siirrä vasemmalle" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Siirrä oikealle" diff --git a/po/fr.po b/po/fr.po index dc50c5d4..b91bf9fb 100644 --- a/po/fr.po +++ b/po/fr.po @@ -40,7 +40,6 @@ msgstr "Plein écran" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Enrouler/Dérouler" @@ -54,13 +53,11 @@ msgstr "Toujours au dessus" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Bouger à gauche" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Bouger à droite" diff --git a/po/gl.po b/po/gl.po index ccdac6cf..79bd6187 100644 --- a/po/gl.po +++ b/po/gl.po @@ -40,7 +40,6 @@ msgstr "Pantalla completa" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Rodar cara arriba/abaixo" @@ -54,13 +53,11 @@ msgstr "Sempre enriba" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Mover á esquerda" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Mover á dereita" diff --git a/po/hu.po b/po/hu.po index 24f6fc13..30ad28fd 100644 --- a/po/hu.po +++ b/po/hu.po @@ -40,7 +40,6 @@ msgstr "Teljes képernyő" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Felhúz / Legördül" @@ -54,13 +53,11 @@ msgstr "Mindig felül" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Balra dokkol" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Jobbra dokkol" diff --git a/po/id.po b/po/id.po index c4f3c248..9932e205 100644 --- a/po/id.po +++ b/po/id.po @@ -40,7 +40,6 @@ msgstr "Tampil Menyeluruh" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Gulir atas/bawah" @@ -54,13 +53,11 @@ msgstr "Selalu di Muka" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Geser ke Kiri" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Geser ke Kanan" diff --git a/po/it.po b/po/it.po index b4fa2a4e..d7c79fa4 100644 --- a/po/it.po +++ b/po/it.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-03-17 11:06+1000\n" -"PO-Revision-Date: 2024-01-23 09:23+0000\n" +"PO-Revision-Date: 2024-06-23 13:43+0000\n" "Last-Translator: Standreas \n" "Language-Team: Italian \n" "Language: it\n" @@ -39,8 +39,6 @@ msgid "Fullscreen" msgstr "Schermo intero" #: src/menu/menu.c:853 -#, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Arrotola/srotola" @@ -53,14 +51,10 @@ msgid "Always on Top" msgstr "Sempre sopra" #: src/menu/menu.c:862 -#, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Sposta a sinistra" #: src/menu/menu.c:869 -#, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Sposta a destra" diff --git a/po/ja.po b/po/ja.po index 52968d18..d20fa4e5 100644 --- a/po/ja.po +++ b/po/ja.po @@ -40,7 +40,6 @@ msgstr "フルスクリーン" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "折りたたむ/広げる" @@ -54,13 +53,11 @@ msgstr "常に最前面に表示" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "左に移動" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "右に移動" diff --git a/po/ka.po b/po/ka.po index fd9b0b36..b6bcc437 100644 --- a/po/ka.po +++ b/po/ka.po @@ -48,19 +48,16 @@ msgstr "დეკორაციები" #: src/menu/menu.c:857 #, fuzzy -#| msgid "Always On Top" msgid "Always on Top" msgstr "ყოველთვისყველაზეზემოდან" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "მარცხნივ გაწევა" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "მარჯვნივ გაწევა" diff --git a/po/lt.po b/po/lt.po index 8b51f763..0a87dd87 100644 --- a/po/lt.po +++ b/po/lt.po @@ -40,7 +40,6 @@ msgstr "Visas ekranas" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Užraityti/atraityti" @@ -54,13 +53,11 @@ msgstr "Visada viršuje" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Perkelti kairėn" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Perkelti dešinėn" diff --git a/po/nl.po b/po/nl.po index be05af85..6286467d 100644 --- a/po/nl.po +++ b/po/nl.po @@ -40,7 +40,6 @@ msgstr "Schermvullende weergave" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Op-/Afrollen" @@ -54,13 +53,11 @@ msgstr "Altijd bovenaan" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Naar links verplaatsen" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Naar rechts verplaatsen" diff --git a/po/pa.po b/po/pa.po index a8063718..6b2b8edd 100644 --- a/po/pa.po +++ b/po/pa.po @@ -40,7 +40,6 @@ msgstr "ਪੂਰੀ ਸਕਰੀਨ" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "ਉੱਤੇ/ਹੇਠਾਂ ਸਕਰਾਓ" @@ -54,13 +53,11 @@ msgstr "ਹਮੇਸ਼ਾਂ ਉੱਤੇ ਰੱਖੋ" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "ਖੱਬੇ ਭੇਜੋ" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "ਸੱਜੇ ਭੇਜੋ" diff --git a/po/pl.po b/po/pl.po index 18dde305..a1abd724 100644 --- a/po/pl.po +++ b/po/pl.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-03-17 11:06+1000\n" -"PO-Revision-Date: 2024-01-23 09:23+0000\n" +"PO-Revision-Date: 2024-06-24 19:19+0000\n" "Last-Translator: Jan Rolski \n" "Language-Team: Polish \n" "Language: pl\n" @@ -39,8 +39,6 @@ msgid "Fullscreen" msgstr "Pełny ekran" #: src/menu/menu.c:853 -#, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Zwiń w górę/w dół" @@ -53,14 +51,10 @@ msgid "Always on Top" msgstr "Zawsze na wierzchu" #: src/menu/menu.c:862 -#, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Przenieś w lewo" #: src/menu/menu.c:869 -#, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Przenieś w prawo" diff --git a/po/pt.po b/po/pt.po index 827552d2..ec5e0ae2 100644 --- a/po/pt.po +++ b/po/pt.po @@ -40,7 +40,6 @@ msgstr "Ecrã inteiro" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Rolar para cima/baixo" @@ -54,13 +53,11 @@ msgstr "Sempre no topo" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Mover para a esquerda" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Mover para a direita" diff --git a/po/ru.po b/po/ru.po index c7c6cf61..25d15d0f 100644 --- a/po/ru.po +++ b/po/ru.po @@ -40,7 +40,6 @@ msgstr "На весь экран" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Свернуть/развернуть в заголовок" @@ -54,13 +53,11 @@ msgstr "Всегда на переднем плане" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Переместить влево" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Переместить вправо" diff --git a/po/sv.po b/po/sv.po index 82d26b92..3107acc6 100644 --- a/po/sv.po +++ b/po/sv.po @@ -40,7 +40,6 @@ msgstr "Fullskärm" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Rulla upp/ned" @@ -54,13 +53,11 @@ msgstr "Alltid överst" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Flytta till vänster" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Flytta till höger" diff --git a/po/tr.po b/po/tr.po index 8bf5a992..bb14cc0e 100644 --- a/po/tr.po +++ b/po/tr.po @@ -40,7 +40,6 @@ msgstr "Tam Ekran" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Yukarı/aşağı katla" @@ -54,13 +53,11 @@ msgstr "Her Zaman Üstte" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Sola taşı" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Sağa taşı" diff --git a/po/uk.po b/po/uk.po index 1d98701d..260ed747 100644 --- a/po/uk.po +++ b/po/uk.po @@ -40,7 +40,6 @@ msgstr "На весь екран" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "Згорнути/розгорнути" @@ -54,13 +53,11 @@ msgstr "Завжди зверху" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "Перемістити ліворуч" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "Перемістити праворуч" diff --git a/po/zh_CN.po b/po/zh_CN.po index eeff7499..438ad14b 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -40,7 +40,6 @@ msgstr "全屏" #: src/menu/menu.c:853 #, fuzzy -#| msgid "Roll up/down" msgid "Roll Up/Down" msgstr "滚动 上/下" @@ -54,13 +53,11 @@ msgstr "最上层显示" #: src/menu/menu.c:862 #, fuzzy -#| msgid "Move left" msgid "Move Left" msgstr "左移" #: src/menu/menu.c:869 #, fuzzy -#| msgid "Move right" msgid "Move Right" msgstr "右移" From f4f5a02b0a31cb63100958014a85d765a242ec7d Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 25 Jun 2024 21:20:12 +0900 Subject: [PATCH 008/232] dnd: show dnd icon above layer-shell surfaces --- src/dnd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dnd.c b/src/dnd.c index 04c016df..eeca114a 100644 --- a/src/dnd.c +++ b/src/dnd.c @@ -37,6 +37,7 @@ handle_drag_start(struct wl_listener *listener, void *data) if (drag->icon) { /* Cleans up automatically on drag->icon->events.destroy */ wlr_scene_drag_icon_create(seat->drag.icons, drag->icon); + wlr_scene_node_raise_to_top(&seat->drag.icons->node); wlr_scene_node_set_enabled(&seat->drag.icons->node, true); } wl_signal_add(&drag->events.destroy, &seat->drag.events.destroy); From 7f94486773d35f7302bbadf3784352af706d75fc Mon Sep 17 00:00:00 2001 From: stefonarch Date: Tue, 25 Jun 2024 13:01:45 +0200 Subject: [PATCH 009/232] Remove fuzzy for capitalized translations --- po/cs.po | 3 --- po/el.po | 3 --- po/es.po | 3 --- po/eu.po | 3 --- po/fi.po | 3 --- po/fr.po | 3 --- po/gl.po | 3 --- po/hu.po | 3 --- po/id.po | 3 --- po/ja.po | 3 --- po/ka.po | 3 --- po/lt.po | 3 --- po/nl.po | 3 --- po/pa.po | 3 --- po/pt.po | 3 --- po/ru.po | 3 --- po/sv.po | 3 --- po/tr.po | 3 --- po/uk.po | 3 --- po/zh_CN.po | 3 --- 20 files changed, 60 deletions(-) diff --git a/po/cs.po b/po/cs.po index 993acf81..eea0e36d 100644 --- a/po/cs.po +++ b/po/cs.po @@ -37,7 +37,6 @@ msgid "Fullscreen" msgstr "Na celou obrazovku" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Rolovat nahoru/dolů" @@ -50,12 +49,10 @@ msgid "Always on Top" msgstr "Vždy nahoře" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Posunout doleva" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Posunout doprava" diff --git a/po/el.po b/po/el.po index 5e6b9126..1542bf4b 100644 --- a/po/el.po +++ b/po/el.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Πλήρης οθόνη" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Τύλιγμα/Ξετύλιγμα" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Πάντα στο προσκήνιο" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Στα αριστερά" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Στα δεξιά" diff --git a/po/es.po b/po/es.po index 58f2fc53..c3181935 100644 --- a/po/es.po +++ b/po/es.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Pantalla completa" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Enrollar hacia arriba/abajo" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Siempre encima" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Mover a la izquierda" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Mover a la derecha" diff --git a/po/eu.po b/po/eu.po index 6f95ab81..a2324472 100644 --- a/po/eu.po +++ b/po/eu.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Pantaila osoa" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Ibili gora/behera" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Beti gainean" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Mugitu ezkerrera" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Mugitu eskuinera" diff --git a/po/fi.po b/po/fi.po index ec84446f..9a819687 100644 --- a/po/fi.po +++ b/po/fi.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Koko näyttö" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Rullaa ylös/alas" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Aina ylimpänä" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Siirrä vasemmalle" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Siirrä oikealle" diff --git a/po/fr.po b/po/fr.po index b91bf9fb..4dbbc8dd 100644 --- a/po/fr.po +++ b/po/fr.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Plein écran" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Enrouler/Dérouler" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Toujours au dessus" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Bouger à gauche" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Bouger à droite" diff --git a/po/gl.po b/po/gl.po index 79bd6187..2b001bea 100644 --- a/po/gl.po +++ b/po/gl.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Pantalla completa" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Rodar cara arriba/abaixo" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Sempre enriba" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Mover á esquerda" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Mover á dereita" diff --git a/po/hu.po b/po/hu.po index 30ad28fd..83492743 100644 --- a/po/hu.po +++ b/po/hu.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Teljes képernyő" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Felhúz / Legördül" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Mindig felül" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Balra dokkol" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Jobbra dokkol" diff --git a/po/id.po b/po/id.po index 9932e205..1f098f47 100644 --- a/po/id.po +++ b/po/id.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Tampil Menyeluruh" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Gulir atas/bawah" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Selalu di Muka" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Geser ke Kiri" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Geser ke Kanan" diff --git a/po/ja.po b/po/ja.po index d20fa4e5..fd81aa59 100644 --- a/po/ja.po +++ b/po/ja.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "フルスクリーン" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "折りたたむ/広げる" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "常に最前面に表示" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "左に移動" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "右に移動" diff --git a/po/ka.po b/po/ka.po index b6bcc437..3299a6ca 100644 --- a/po/ka.po +++ b/po/ka.po @@ -47,17 +47,14 @@ msgid "Decorations" msgstr "დეკორაციები" #: src/menu/menu.c:857 -#, fuzzy msgid "Always on Top" msgstr "ყოველთვისყველაზეზემოდან" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "მარცხნივ გაწევა" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "მარჯვნივ გაწევა" diff --git a/po/lt.po b/po/lt.po index 0a87dd87..6fcbf748 100644 --- a/po/lt.po +++ b/po/lt.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Visas ekranas" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Užraityti/atraityti" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Visada viršuje" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Perkelti kairėn" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Perkelti dešinėn" diff --git a/po/nl.po b/po/nl.po index 6286467d..52e49773 100644 --- a/po/nl.po +++ b/po/nl.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Schermvullende weergave" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Op-/Afrollen" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Altijd bovenaan" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Naar links verplaatsen" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Naar rechts verplaatsen" diff --git a/po/pa.po b/po/pa.po index 6b2b8edd..e598408b 100644 --- a/po/pa.po +++ b/po/pa.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "ਪੂਰੀ ਸਕਰੀਨ" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "ਉੱਤੇ/ਹੇਠਾਂ ਸਕਰਾਓ" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "ਹਮੇਸ਼ਾਂ ਉੱਤੇ ਰੱਖੋ" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "ਖੱਬੇ ਭੇਜੋ" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "ਸੱਜੇ ਭੇਜੋ" diff --git a/po/pt.po b/po/pt.po index ec5e0ae2..e69e7dde 100644 --- a/po/pt.po +++ b/po/pt.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Ecrã inteiro" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Rolar para cima/baixo" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Sempre no topo" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Mover para a esquerda" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Mover para a direita" diff --git a/po/ru.po b/po/ru.po index 25d15d0f..70fd2b05 100644 --- a/po/ru.po +++ b/po/ru.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "На весь экран" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Свернуть/развернуть в заголовок" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Всегда на переднем плане" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Переместить влево" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Переместить вправо" diff --git a/po/sv.po b/po/sv.po index 3107acc6..c596177e 100644 --- a/po/sv.po +++ b/po/sv.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Fullskärm" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Rulla upp/ned" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Alltid överst" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Flytta till vänster" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Flytta till höger" diff --git a/po/tr.po b/po/tr.po index bb14cc0e..f4858b69 100644 --- a/po/tr.po +++ b/po/tr.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "Tam Ekran" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Yukarı/aşağı katla" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Her Zaman Üstte" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Sola taşı" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Sağa taşı" diff --git a/po/uk.po b/po/uk.po index 260ed747..85b0f06b 100644 --- a/po/uk.po +++ b/po/uk.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "На весь екран" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "Згорнути/розгорнути" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "Завжди зверху" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "Перемістити ліворуч" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "Перемістити праворуч" diff --git a/po/zh_CN.po b/po/zh_CN.po index 438ad14b..ac4fcc41 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -39,7 +39,6 @@ msgid "Fullscreen" msgstr "全屏" #: src/menu/menu.c:853 -#, fuzzy msgid "Roll Up/Down" msgstr "滚动 上/下" @@ -52,12 +51,10 @@ msgid "Always on Top" msgstr "最上层显示" #: src/menu/menu.c:862 -#, fuzzy msgid "Move Left" msgstr "左移" #: src/menu/menu.c:869 -#, fuzzy msgid "Move Right" msgstr "右移" From f2755a4e2ea28939994752491031c1eaef5d121e Mon Sep 17 00:00:00 2001 From: droc12345 <80716141+droc12345@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:03:56 -0500 Subject: [PATCH 010/232] action: remember initial direction of PreviousView ...when cycling windows. Also make the toggling of direction when shift is pressed relative to the initial direction. For example if W-j is bound to PreviousWindow, subsequent key presses will continue to cycle backwards unless shift if pressed. Add documentation for using shift/arrow keys in Next/Previous --- docs/labwc-actions.5.scd | 11 +++++++---- include/labwc.h | 14 ++++++++------ src/action.c | 28 +++++++++++++++++++++------ src/input/keyboard.c | 41 ++++++++++++++++++++++++++++++++-------- 4 files changed, 70 insertions(+), 24 deletions(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index 50f6ad33..e2edf387 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -99,11 +99,14 @@ Actions are used in menus and keyboard/mouse bindings. Resize and move active window according to the given region. See labwc-config(5) for further information on how to define regions. -** - Cycle focus to next window. - +**++ ** - Cycle focus to previous window. + Cycle focus to next/previous window respectively.++ + Default keybind for NextWindow is Alt-Tab. + + The shift key is used to reverse direction while cycling. + + The arrow keys are used to move forwards/backwards while cycling. ** Re-load configuration and theme files. diff --git a/include/labwc.h b/include/labwc.h index e3f1ad23..a19f8243 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -210,6 +210,12 @@ struct seat { struct lab_data_buffer; struct workspace; +enum lab_cycle_dir { + LAB_CYCLE_DIR_NONE, + LAB_CYCLE_DIR_FORWARD, + LAB_CYCLE_DIR_BACKWARD, +}; + struct server { struct wl_display *wl_display; struct wl_event_loop *wl_event_loop; /* Can be used for timer events */ @@ -349,6 +355,8 @@ struct server { struct wlr_scene_tree *preview_parent; struct wlr_scene_node *preview_anchor; struct multi_rect *preview_outline; + enum lab_cycle_dir initial_direction; + bool initial_keybind_contained_shift; } osd_state; struct theme *theme; @@ -442,12 +450,6 @@ struct view *desktop_topmost_focusable_view(struct server *server); */ void desktop_update_top_layer_visiblity(struct server *server); -enum lab_cycle_dir { - LAB_CYCLE_DIR_NONE, - LAB_CYCLE_DIR_FORWARD, - LAB_CYCLE_DIR_BACKWARD, -}; - /** * desktop_cycle_view - return view to 'cycle' to * @start_view: reference point for finding next view to cycle to diff --git a/src/action.c b/src/action.c index 80231b95..0aa45d7a 100644 --- a/src/action.c +++ b/src/action.c @@ -701,6 +701,26 @@ run_if_action(struct view *view, struct server *server, struct action *action) return !strcmp(branch, "then"); } +static bool +shift_is_pressed(struct server *server) +{ + uint32_t modifiers = wlr_keyboard_get_modifiers( + &server->seat.keyboard_group->keyboard); + return modifiers & WLR_MODIFIER_SHIFT; +} + +static void +start_window_cycling(struct server *server, enum lab_cycle_dir direction) +{ + /* Remember direction so it can be followed by subsequent key presses */ + server->osd_state.initial_direction = direction; + server->osd_state.initial_keybind_contained_shift = + shift_is_pressed(server); + server->osd_state.cycle_view = desktop_cycle_view(server, + server->osd_state.cycle_view, direction); + osd_update(server); +} + void actions_run(struct view *activator, struct server *server, struct wl_list *actions, uint32_t resize_edges) @@ -791,14 +811,10 @@ actions_run(struct view *activator, struct server *server, } break; case ACTION_TYPE_NEXT_WINDOW: - server->osd_state.cycle_view = desktop_cycle_view(server, - server->osd_state.cycle_view, LAB_CYCLE_DIR_FORWARD); - osd_update(server); + start_window_cycling(server, LAB_CYCLE_DIR_FORWARD); break; case ACTION_TYPE_PREVIOUS_WINDOW: - server->osd_state.cycle_view = desktop_cycle_view(server, - server->osd_state.cycle_view, LAB_CYCLE_DIR_BACKWARD); - osd_update(server); + start_window_cycling(server, LAB_CYCLE_DIR_BACKWARD); break; case ACTION_TYPE_RECONFIGURE: kill(getpid(), SIGHUP); diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 147ec4dc..5806441a 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -383,6 +383,16 @@ handle_menu_keys(struct server *server, struct keysyms *syms) } } +static void +toggle_direction(enum lab_cycle_dir *direction) +{ + if (*direction == LAB_CYCLE_DIR_FORWARD) { + *direction = LAB_CYCLE_DIR_BACKWARD; + } else if (*direction == LAB_CYCLE_DIR_BACKWARD) { + *direction = LAB_CYCLE_DIR_FORWARD; + } +} + static void handle_cycle_view_key(struct server *server, struct keyinfo *keyinfo) { @@ -397,21 +407,36 @@ handle_cycle_view_key(struct server *server, struct keyinfo *keyinfo) /* cycle to next */ if (!keyinfo->is_modifier) { - bool back_key = false; + enum lab_cycle_dir direction = server->osd_state.initial_direction; for (int i = 0; i < keyinfo->translated.nr_syms; i++) { if (keyinfo->translated.syms[i] == XKB_KEY_Up || keyinfo->translated.syms[i] == XKB_KEY_Left) { - back_key = true; - break; + direction = LAB_CYCLE_DIR_BACKWARD; + goto miss_shift_toggle; + } + if (keyinfo->translated.syms[i] == XKB_KEY_Down + || keyinfo->translated.syms[i] == XKB_KEY_Right) { + direction = LAB_CYCLE_DIR_FORWARD; + goto miss_shift_toggle; } } - bool backwards = (keyinfo->modifiers & WLR_MODIFIER_SHIFT) || back_key; - enum lab_cycle_dir dir = backwards - ? LAB_CYCLE_DIR_BACKWARD - : LAB_CYCLE_DIR_FORWARD; + bool shift_is_pressed = keyinfo->modifiers & WLR_MODIFIER_SHIFT; + if (shift_is_pressed != server->osd_state.initial_keybind_contained_shift) { + /* + * Shift reverses the direction - unless shift was part of the + * original keybind in which case we do the opposite. + * For example with S-A-Tab bound to PreviousWindow, shift with + * subsequent key presses should carry on cycling backwards. + */ + toggle_direction(&direction); + } + + /* Only one direction modifier is allowed, either arrow keys OR shift */ +miss_shift_toggle: + server->osd_state.cycle_view = desktop_cycle_view(server, - server->osd_state.cycle_view, dir); + server->osd_state.cycle_view, direction); osd_update(server); } } From 98347e454e4c38028cbcfb9ec5b52405e9c6d4e8 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Sat, 22 Jun 2024 15:12:12 +0200 Subject: [PATCH 011/232] ssd: show squared corners when the view is tiled --- include/ssd-internal.h | 1 + src/ssd/ssd-titlebar.c | 20 +++++++++++++++----- src/ssd/ssd.c | 8 +++++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/include/ssd-internal.h b/include/ssd-internal.h index 639d143e..fda196e6 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -50,6 +50,7 @@ struct ssd { */ struct { bool was_maximized; /* To un-round corner buttons and toggle icon on maximize */ + bool was_tiled_not_maximized; /* To un-round corner buttons */ struct wlr_box geometry; struct ssd_state_title { char *text; diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index cdbe4d36..87d0f2e9 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -117,11 +117,16 @@ ssd_titlebar_create(struct ssd *ssd) ssd_update_title(ssd); - if (view->maximized == VIEW_AXIS_BOTH) { + bool maximized = view->maximized == VIEW_AXIS_BOTH; + if (maximized) { set_squared_corners(ssd, true); set_maximize_alt_icon(ssd, true); ssd->state.was_maximized = true; } + if (view_is_tiled(view) && !maximized) { + set_squared_corners(ssd, true); + ssd->state.was_tiled_not_maximized = true; + } } static bool @@ -187,11 +192,16 @@ ssd_titlebar_update(struct ssd *ssd) int width = view->current.width; struct theme *theme = view->server->theme; - bool maximized = (view->maximized == VIEW_AXIS_BOTH); - if (ssd->state.was_maximized != maximized) { - set_squared_corners(ssd, maximized); - set_maximize_alt_icon(ssd, maximized); + bool maximized = view->maximized == VIEW_AXIS_BOTH; + bool tiled_not_maximized = view_is_tiled(ssd->view) && !maximized; + if (ssd->state.was_maximized != maximized + || ssd->state.was_tiled_not_maximized != tiled_not_maximized) { + set_squared_corners(ssd, maximized || tiled_not_maximized); + if (ssd->state.was_maximized != maximized) { + set_maximize_alt_icon(ssd, maximized); + } ssd->state.was_maximized = maximized; + ssd->state.was_tiled_not_maximized = tiled_not_maximized; } if (width == ssd->state.geometry.width) { diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 2d382ca0..2ed852ec 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -246,7 +246,7 @@ ssd_update_geometry(struct ssd *ssd) ssd_extents_update(ssd); ssd->state.geometry = current; } - bool maximized = (ssd->view->maximized == VIEW_AXIS_BOTH); + bool maximized = ssd->view->maximized == VIEW_AXIS_BOTH; if (ssd->state.was_maximized != maximized) { ssd_border_update(ssd); ssd_titlebar_update(ssd); @@ -258,6 +258,12 @@ ssd_update_geometry(struct ssd *ssd) */ ssd->state.was_maximized = maximized; } + bool tiled_and_not_maximized = view_is_tiled(ssd->view) && !maximized; + if (ssd->state.was_tiled_not_maximized != tiled_and_not_maximized) { + ssd_titlebar_update(ssd); + /* see above about being future proof */ + ssd->state.was_tiled_not_maximized = tiled_and_not_maximized; + } return; } ssd_extents_update(ssd); From 2c979fe2691be65e000adbbeb5bd452692d80c7c Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Sat, 22 Jun 2024 23:27:37 +0200 Subject: [PATCH 012/232] ssd: extend border over squared corners --- src/ssd/ssd-border.c | 69 ++++++++++++++++++++++++++++++++------------ src/ssd/ssd.c | 5 ++-- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index 74f29ffa..63604244 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -86,6 +86,38 @@ ssd_border_update(struct ssd *ssd) int height = view_effective_height(view, /* use_pending */ false); int full_width = width + 2 * theme->border_width; + /* + * From here on we have to cover the following border scenarios: + * Non-tiled (partial border, rounded corners): + * _____________ + * o oox + * |---------------| + * |_______________| + * + * Tiled (full border, squared corners): + * _______________ + * |o oox| + * |---------------| + * |_______________| + * + * Tiled or non-tiled with zero title height (full boarder, no title): + * _______________ + * |_______________| + */ + + int side_height = ssd->state.was_tiled_not_maximized + ? height + ssd->titlebar.height + : height; + int side_y = ssd->state.was_tiled_not_maximized + ? -ssd->titlebar.height + : 0; + int top_width = ssd->titlebar.height <= 0 || ssd->state.was_tiled_not_maximized + ? full_width + : width - 2 * SSD_BUTTON_WIDTH; + int top_x = ssd->titlebar.height <= 0 || ssd->state.was_tiled_not_maximized + ? 0 + : theme->border_width + SSD_BUTTON_WIDTH; + struct ssd_part *part; struct wlr_scene_rect *rect; struct ssd_sub_tree *subtree; @@ -95,34 +127,35 @@ ssd_border_update(struct ssd *ssd) switch (part->type) { case LAB_SSD_PART_LEFT: wlr_scene_rect_set_size(rect, - theme->border_width, height); + theme->border_width, + side_height); + wlr_scene_node_set_position(part->node, + 0, + side_y); continue; case LAB_SSD_PART_RIGHT: wlr_scene_rect_set_size(rect, - theme->border_width, height); + theme->border_width, + side_height); wlr_scene_node_set_position(part->node, - theme->border_width + width, 0); + theme->border_width + width, + side_y); continue; case LAB_SSD_PART_BOTTOM: wlr_scene_rect_set_size(rect, - full_width, theme->border_width); + full_width, + theme->border_width); wlr_scene_node_set_position(part->node, - 0, height); + 0, + height); continue; case LAB_SSD_PART_TOP: - if (ssd->titlebar.height > 0) { - wlr_scene_rect_set_size(rect, - width - 2 * SSD_BUTTON_WIDTH, - theme->border_width); - wlr_scene_node_set_position(part->node, - theme->border_width + SSD_BUTTON_WIDTH, - -(ssd->titlebar.height + theme->border_width)); - } else { - wlr_scene_rect_set_size(rect, - full_width, theme->border_width); - wlr_scene_node_set_position(part->node, - 0, -theme->border_width); - } + wlr_scene_rect_set_size(rect, + top_width, + theme->border_width); + wlr_scene_node_set_position(part->node, + top_x, + -(ssd->titlebar.height + theme->border_width)); continue; default: continue; diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 2ed852ec..86ac1272 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -248,8 +248,8 @@ ssd_update_geometry(struct ssd *ssd) } bool maximized = ssd->view->maximized == VIEW_AXIS_BOTH; if (ssd->state.was_maximized != maximized) { - ssd_border_update(ssd); ssd_titlebar_update(ssd); + ssd_border_update(ssd); ssd_shadow_update(ssd); /* * Not strictly necessary as ssd_titlebar_update() @@ -261,14 +261,15 @@ ssd_update_geometry(struct ssd *ssd) bool tiled_and_not_maximized = view_is_tiled(ssd->view) && !maximized; if (ssd->state.was_tiled_not_maximized != tiled_and_not_maximized) { ssd_titlebar_update(ssd); + ssd_border_update(ssd); /* see above about being future proof */ ssd->state.was_tiled_not_maximized = tiled_and_not_maximized; } return; } ssd_extents_update(ssd); - ssd_border_update(ssd); ssd_titlebar_update(ssd); + ssd_border_update(ssd); ssd_shadow_update(ssd); ssd->state.geometry = current; } From 3b605b014295c2b6ca693340fcfeec422e64883f Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Sat, 22 Jun 2024 23:40:19 +0200 Subject: [PATCH 013/232] ssd: set squared corners for tiled views conditionally Respect the snapping settings for notify-client. --- include/view.h | 1 + src/ssd/ssd-titlebar.c | 6 ++++-- src/view.c | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/include/view.h b/include/view.h index b21d7ca2..b4f90e6a 100644 --- a/include/view.h +++ b/include/view.h @@ -495,6 +495,7 @@ void view_toggle_always_on_bottom(struct view *view); void view_toggle_visible_on_all_workspaces(struct view *view); bool view_is_tiled(struct view *view); +bool view_is_tiled_and_notify_tiled(struct view *view); bool view_is_floating(struct view *view); void view_move_to_workspace(struct view *view, struct workspace *workspace); enum ssd_mode view_get_ssd_mode(struct view *view); diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 87d0f2e9..32d6131a 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -123,7 +123,7 @@ ssd_titlebar_create(struct ssd *ssd) set_maximize_alt_icon(ssd, true); ssd->state.was_maximized = true; } - if (view_is_tiled(view) && !maximized) { + if (view_is_tiled_and_notify_tiled(view) && !maximized) { set_squared_corners(ssd, true); ssd->state.was_tiled_not_maximized = true; } @@ -193,7 +193,9 @@ ssd_titlebar_update(struct ssd *ssd) struct theme *theme = view->server->theme; bool maximized = view->maximized == VIEW_AXIS_BOTH; - bool tiled_not_maximized = view_is_tiled(ssd->view) && !maximized; + bool tiled_not_maximized = view_is_tiled_and_notify_tiled(ssd->view) + && !maximized; + if (ssd->state.was_maximized != maximized || ssd->state.was_tiled_not_maximized != tiled_not_maximized) { set_squared_corners(ssd, maximized || tiled_not_maximized); diff --git a/src/view.c b/src/view.c index f83c9a66..5c98a935 100644 --- a/src/view.c +++ b/src/view.c @@ -1171,6 +1171,23 @@ view_is_tiled(struct view *view) || view->tiled_region_evacuate); } +bool +view_is_tiled_and_notify_tiled(struct view *view) +{ + switch (rc.snap_tiling_events_mode) { + case LAB_TILING_EVENTS_NEVER: + return false; + case LAB_TILING_EVENTS_REGION: + return view->tiled_region || view->tiled_region_evacuate; + case LAB_TILING_EVENTS_EDGE: + return view->tiled; + case LAB_TILING_EVENTS_ALWAYS: + return view_is_tiled(view); + } + + return false; +} + bool view_is_floating(struct view *view) { From 74e1ba72e3d03d15df5f2f168847ba6ab939cafa Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 19 Jun 2024 11:48:39 +0900 Subject: [PATCH 014/232] view: don't try to restore to very small width/height on unmaximize Thonny (Python IDE made with Tk) may set the window geometry to 1x1 and maximizes the window before mapping. This set `view->natural_geometry` to 1x1, so labwc tried to restore the window geometry to it on unmaximize, causing validation errors in `ssd_update_geometry()` as its width and height are smaller than `LAB_MIN_VIEW_{WIDTH,HEIGHT}`. This commit fixes it by not allowing geometries smaller than `LAB_MIN_VIEW_{WIDTH,HEIGHT}` in `view->natural_geometry`. --- src/view.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/view.c b/src/view.c index 5c98a935..1646decd 100644 --- a/src/view.c +++ b/src/view.c @@ -841,10 +841,12 @@ view_store_natural_geometry(struct view *view) /** * If an application was started maximized or fullscreened, its - * natural_geometry width/height may still be zero in which case we set - * some fallback values. This is the case with foot and Qt applications. + * natural_geometry width/height may still be zero (or very small + * values) in which case we set some fallback values. This is the case + * with foot and some Qt/Tk applications. */ - if (wlr_box_empty(&view->pending)) { + if (view->pending.width < LAB_MIN_VIEW_WIDTH + || view->pending.height < LAB_MIN_VIEW_HEIGHT) { set_fallback_geometry(view); } else { view->natural_geometry = view->pending; From b8c3e064e76ca3722c19cee76f925dd71b3c6377 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 28 Jun 2024 18:27:43 +0100 Subject: [PATCH 015/232] menu: support menu.overlap.x with pipemenus No inteded functional change intended with refactoring of get_submenu_position(). Fixes #1870 --- src/menu/menu.c | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index f4871422..d456fb01 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -702,22 +702,23 @@ menu_get_full_width(struct menu *menu) return width + max_child_width; } +/** + * get_submenu_position() - get output layout coordinates of menu window + * @item: the menuitem that triggers the submenu (static or dynamic) + */ static struct wlr_box get_submenu_position(struct menuitem *item, enum menu_align align) { struct wlr_box pos = { 0 }; struct menu *menu = item->parent; struct theme *theme = menu->server->theme; - int lx = menu->scene_tree->node.x; - int ly = menu->scene_tree->node.y; + pos.x = menu->scene_tree->node.x; + pos.y = menu->scene_tree->node.y; if (align & LAB_MENU_OPEN_RIGHT) { - pos.x = lx + menu->size.width - theme->menu_overlap_x; - } else { - pos.x = lx; + pos.x += menu->size.width - theme->menu_overlap_x; } - int rel_y = item->tree->node.y; - pos.y = ly + rel_y - theme->menu_overlap_y; + pos.y += item->tree->node.y - theme->menu_overlap_y; return pos; } @@ -1117,19 +1118,9 @@ create_pipe_menu(struct pipe_context *ctx) /* Set menu-widths before configuring */ post_processing(ctx->server); - /* - * TODO: - * (1) Combine this with the code in get_submenu_position() - * and/or menu_configure() - * (2) Take into account menu_overlap_{x,y} - */ enum menu_align align = ctx->item->parent->align; - int x = pipe_parent->scene_tree->node.x; - int y = pipe_parent->scene_tree->node.y + ctx->item->tree->node.y; - if (align & LAB_MENU_OPEN_RIGHT) { - x += pipe_parent->size.width; - } - menu_configure(pipe_menu, x, y, align); + struct wlr_box pos = get_submenu_position(ctx->item, align); + menu_configure(pipe_menu, pos.x, pos.y, align); validate(ctx->server); From 45b197b8a4e6da4e54c8db77be6ae64e8895f451 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Mon, 17 Jun 2024 16:36:07 +0200 Subject: [PATCH 016/232] magnifier: fix flickering on simultaneous gamma changes Gamma changes take another code path and thus did not render the magnifier. This patch consalidates both code paths and therefore also renders the magnifier on gamma changes. Fixes: #1905 --- include/common/scene-helpers.h | 4 +- src/common/scene-helpers.c | 16 ++++++-- src/magnifier.c | 6 +-- src/output.c | 72 ++++++++++++++++++++-------------- 4 files changed, 61 insertions(+), 37 deletions(-) diff --git a/include/common/scene-helpers.h b/include/common/scene-helpers.h index 11697a94..021d3b16 100644 --- a/include/common/scene-helpers.h +++ b/include/common/scene-helpers.h @@ -7,6 +7,7 @@ struct wlr_scene_node; struct wlr_surface; struct wlr_scene_output; +struct wlr_output_state; struct wlr_surface *lab_wlr_surface_from_node(struct wlr_scene_node *node); @@ -18,6 +19,7 @@ struct wlr_surface *lab_wlr_surface_from_node(struct wlr_scene_node *node); struct wlr_scene_node *lab_wlr_scene_get_prev_node(struct wlr_scene_node *node); /* A variant of wlr_scene_output_commit() that respects wlr_output->pending */ -bool lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output); +bool lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output, + struct wlr_output_state *output_state); #endif /* LABWC_SCENE_HELPERS_H */ diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c index 755d65d4..3ed59726 100644 --- a/src/common/scene-helpers.c +++ b/src/common/scene-helpers.c @@ -40,11 +40,12 @@ lab_wlr_scene_get_prev_node(struct wlr_scene_node *node) * as it doesn't use the pending state at all. */ bool -lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output) +lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output, + struct wlr_output_state *state) { assert(scene_output); + assert(state); struct wlr_output *wlr_output = scene_output->output; - struct wlr_output_state *state = &wlr_output->pending; struct output *output = wlr_output->data; bool wants_magnification = output_wants_magnification(output); @@ -70,11 +71,18 @@ lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output) magnify(output, state->buffer, &additional_damage); } - if (!wlr_output_commit(wlr_output)) { - wlr_log(WLR_INFO, "Failed to commit output %s", + if (state == &wlr_output->pending) { + if (!wlr_output_commit(wlr_output)) { + wlr_log(WLR_INFO, "Failed to commit output %s", + wlr_output->name); + return false; + } + } else if (!wlr_output_commit_state(wlr_output, state)) { + wlr_log(WLR_INFO, "Failed to commit state for output %s", wlr_output->name); return false; } + /* * FIXME: Remove the following line as soon as * https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4253 diff --git a/src/magnifier.c b/src/magnifier.c index 327fd3e4..29b16154 100644 --- a/src/magnifier.c +++ b/src/magnifier.c @@ -96,7 +96,7 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box /* (Re)create the temporary buffer if required */ if (tmp_buffer && (tmp_buffer->width != width || tmp_buffer->height != height)) { - wlr_log(WLR_DEBUG, "tmp buffer size changed, dropping"); + wlr_log(WLR_DEBUG, "tmp magnifier buffer size changed, dropping"); assert(tmp_texture); wlr_texture_destroy(tmp_texture); wlr_buffer_drop(tmp_buffer); @@ -117,7 +117,7 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box tmp_texture = wlr_texture_from_buffer(server->renderer, tmp_buffer); } if (!tmp_texture) { - wlr_log(WLR_ERROR, "Failed to allocate temporary texture"); + wlr_log(WLR_ERROR, "Failed to allocate temporary magnifier texture"); wlr_buffer_drop(tmp_buffer); tmp_buffer = NULL; return; @@ -209,7 +209,7 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box }; wlr_render_pass_add_texture(tmp_render_pass, &opts); if (!wlr_render_pass_submit(tmp_render_pass)) { - wlr_log(WLR_ERROR, "Failed to submit render pass"); + wlr_log(WLR_ERROR, "Failed to submit magnifier render pass"); goto cleanup; } diff --git a/src/output.c b/src/output.c index bb2b9760..1bf01429 100644 --- a/src/output.c +++ b/src/output.c @@ -48,6 +48,36 @@ get_tearing_preference(struct output *output) return server->active_view->tearing_hint; } +static void +output_apply_gamma(struct output *output) +{ + assert(output); + assert(output->gamma_lut_changed); + + struct server *server = output->server; + struct wlr_scene_output *scene_output = output->scene_output; + + struct wlr_output_state pending; + wlr_output_state_init(&pending); + + output->gamma_lut_changed = false; + struct wlr_gamma_control_v1 *gamma_control = + wlr_gamma_control_manager_v1_get_control( + server->gamma_control_manager_v1, + output->wlr_output); + + if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { + wlr_output_state_finish(&pending); + return; + } + + if (!lab_wlr_scene_output_commit(scene_output, &pending)) { + wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); + } + + wlr_output_state_finish(&pending); +} + static void output_frame_notify(struct wl_listener *listener, void *data) { @@ -71,39 +101,23 @@ output_frame_notify(struct wl_listener *listener, void *data) return; } - struct wlr_output *wlr_output = output->wlr_output; - struct server *server = output->server; - if (output->gamma_lut_changed) { - struct wlr_output_state pending; - wlr_output_state_init(&pending); - if (!wlr_scene_output_build_state(output->scene_output, &pending, NULL)) { - return; - } - output->gamma_lut_changed = false; - struct wlr_gamma_control_v1 *gamma_control = - wlr_gamma_control_manager_v1_get_control( - server->gamma_control_manager_v1, wlr_output); - if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { - wlr_output_state_finish(&pending); - return; - } + /* + * We are not mixing the gamma state with + * other pending output changes to make it + * easier to handle a failed output commit + * due to gamma without impacting other + * unrelated output changes. + */ + output_apply_gamma(output); + } else { + output->wlr_output->pending.tearing_page_flip = + get_tearing_preference(output); - if (!wlr_output_commit_state(output->wlr_output, &pending)) { - wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); - wlr_output_state_finish(&pending); - return; - } - - wlr_damage_ring_rotate(&output->scene_output->damage_ring); - wlr_output_state_finish(&pending); - return; + lab_wlr_scene_output_commit(output->scene_output, + &output->wlr_output->pending); } - output->wlr_output->pending.tearing_page_flip = - get_tearing_preference(output); - lab_wlr_scene_output_commit(output->scene_output); - struct timespec now = { 0 }; clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(output->scene_output, &now); From a98f2635ea60e416891cbc381a1a3c0d6a3478de Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 27 Jun 2024 19:17:38 -0400 Subject: [PATCH 017/232] xdg: support xdg-shell v3 with popup repositioning See https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3514 which added support on the wlroots side. We now re-run popup positioning (for both xdg-shell and layer-shell popups) when the "reposition" event is received. This allows popups that change size (such as qmpanel's applications menu) to be positioned correctly. xdg-shell v3 also gives the compositor some additional "hints" for popup positioning (reactive, parent_size, and parent_configure_serial) which are available but we don't make use of currently. --- include/layers.h | 1 + src/layers.c | 13 +++++++++++++ src/xdg-popup.c | 12 ++++++++++++ src/xdg.c | 2 +- 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/include/layers.h b/include/layers.h index 7b6270ed..827c2ada 100644 --- a/include/layers.h +++ b/include/layers.h @@ -32,6 +32,7 @@ struct lab_layer_popup { struct wl_listener commit; struct wl_listener destroy; struct wl_listener new_popup; + struct wl_listener reposition; }; void layers_init(struct server *server); diff --git a/src/layers.c b/src/layers.c index 4a853fe3..df556761 100644 --- a/src/layers.c +++ b/src/layers.c @@ -356,6 +356,7 @@ 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); + wl_list_remove(&popup->reposition.link); /* Usually already removed unless there was no commit at all */ if (popup->commit.notify) { @@ -381,6 +382,15 @@ popup_handle_commit(struct wl_listener *listener, void *data) } } +static void +popup_handle_reposition(struct wl_listener *listener, void *data) +{ + struct lab_layer_popup *popup = + wl_container_of(listener, popup, reposition); + wlr_xdg_popup_unconstrain_from_box(popup->wlr_popup, + &popup->output_toplevel_sx_box); +} + static void popup_handle_new_popup(struct wl_listener *listener, void *data); static struct lab_layer_popup * @@ -410,6 +420,9 @@ create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent) popup->commit.notify = popup_handle_commit; wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); + popup->reposition.notify = popup_handle_reposition; + wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); + return popup; } diff --git a/src/xdg-popup.c b/src/xdg-popup.c index 0d1ed909..d6bc99f0 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -19,6 +19,7 @@ struct xdg_popup { struct wl_listener commit; struct wl_listener destroy; struct wl_listener new_popup; + struct wl_listener reposition; }; static void @@ -47,6 +48,7 @@ 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); + wl_list_remove(&popup->reposition.link); /* Usually already removed unless there was no commit at all */ if (popup->commit.notify) { @@ -69,6 +71,13 @@ handle_xdg_popup_commit(struct wl_listener *listener, void *data) } } +static void +handle_xdg_popup_reposition(struct wl_listener *listener, void *data) +{ + struct xdg_popup *popup = wl_container_of(listener, popup, reposition); + popup_unconstrain(popup); +} + static void popup_handle_new_xdg_popup(struct wl_listener *listener, void *data) { @@ -100,6 +109,9 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) popup->commit.notify = handle_xdg_popup_commit; wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit); + popup->reposition.notify = handle_xdg_popup_reposition; + wl_signal_add(&wlr_popup->events.reposition, &popup->reposition); + /* * 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 diff --git a/src/xdg.c b/src/xdg.c index 2b8bf96e..86ee06c3 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -14,7 +14,7 @@ #include "window-rules.h" #include "workspaces.h" -#define LAB_XDG_SHELL_VERSION (2) +#define LAB_XDG_SHELL_VERSION (3) #define CONFIGURE_TIMEOUT_MS 100 static struct xdg_toplevel_view * From a466591e0dee4eb9eb2c7d653b56f1bc21606370 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 2 Jul 2024 20:49:53 +0200 Subject: [PATCH 018/232] ssd/ssd-shadow.c: fix memory leak Before this patch, we were leaking memory [0] because the shadow implementation did not free the ssd_parts on destruction. There was also no check if shadows were actually enabled via rc.xml or not so this also impacted people who were not using shadows but were not setting the shadow size via their theme to 0. [0] 44 bytes per ssd_part * 8 parts * 2 states == 704 bytes per view closed. Note that Reconfigure also re-creates the SSD, thus we were also leaking 704 bytes * nr_views per Reconfigure. --- src/ssd/ssd-shadow.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/ssd/ssd-shadow.c b/src/ssd/ssd-shadow.c index cf54f6fa..3e6227aa 100644 --- a/src/ssd/ssd-shadow.c +++ b/src/ssd/ssd-shadow.c @@ -241,7 +241,12 @@ ssd_shadow_create(struct ssd *ssd) struct wlr_scene_tree *parent; FOR_EACH_STATE(ssd, subtree) { - if (subtree == &ssd->shadow.active) { + wl_list_init(&subtree->parts); + + if (!rc.shadows_enabled) { + /* Shadows are globally disabled */ + continue; + } else if (subtree == &ssd->shadow.active) { if (theme->window_active_shadow_size == 0) { /* Active window shadows are disabled */ continue; @@ -264,7 +269,6 @@ ssd_shadow_create(struct ssd *ssd) corner_bottom_buffer = &theme->shadow_corner_bottom_inactive->base; edge_buffer = &theme->shadow_edge_inactive->base; } - wl_list_init(&subtree->parts); make_shadow(&subtree->parts, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, parent, @@ -311,6 +315,16 @@ ssd_shadow_destroy(struct ssd *ssd) assert(ssd); assert(ssd->shadow.tree); + struct ssd_sub_tree *subtree; + FOR_EACH_STATE(ssd, subtree) { + ssd_destroy_parts(&subtree->parts); + /* + * subtree->tree will be destroyed when its + * parent (ssd->shadow.tree) is destroyed. + */ + subtree->tree = NULL; + } FOR_EACH_END + wlr_scene_node_destroy(&ssd->shadow.tree->node); ssd->shadow.tree = NULL; } From 370d62bedaaf1d3cbf804eeaf71599586cbb3812 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Tue, 2 Jul 2024 20:51:51 +0100 Subject: [PATCH 019/232] Remove subprojects/seatd.wrap as no longer needed --- subprojects/seatd.wrap | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 subprojects/seatd.wrap diff --git a/subprojects/seatd.wrap b/subprojects/seatd.wrap deleted file mode 100644 index 2194fc50..00000000 --- a/subprojects/seatd.wrap +++ /dev/null @@ -1,4 +0,0 @@ -[wrap-git] -url=https://git.sr.ht/~kennylevinsen/seatd -revision=0.6.4 - From 9153c22dab77837f93c96edbf5acfbc4ffb3b0c8 Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" Date: Wed, 3 Jul 2024 13:43:04 -0400 Subject: [PATCH 020/232] xwayland: allow persistence Closes: #1958. --- docs/labwc-config.5.scd | 7 +++++++ docs/rc.xml.all | 1 + include/config/rcxml.h | 1 + src/config/rcxml.c | 2 ++ src/xwayland.c | 4 +++- 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 85a8d437..f212ca0f 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -194,6 +194,13 @@ this is for compatibility with Openbox. be used with labwc the preferred mode of the monitor is used instead. Default is no. +** [yes|no] + Keep XWayland alive even when no clients are connected, rather than + using a "lazy" policy that allows the server to launch on demand and die + when it is no longer needed. Default is no. + + Note: changing this setting requires a restart of labwc. + ## PLACEMENT ** [center|automatic|cursor] diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 35e5c91b..1f4a0f69 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -13,6 +13,7 @@ no no no + no diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 8df7c9aa..a9e89589 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -55,6 +55,7 @@ struct rcxml { bool allow_tearing; bool reuse_output_mode; enum view_placement_policy placement_policy; + bool xwayland_persistence; /* focus */ bool focus_follow_mouse; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 0ff52bf3..d263cb9d 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -899,6 +899,8 @@ entry(xmlNode *node, char *nodename, char *content) if (rc.placement_policy == LAB_PLACE_INVALID) { rc.placement_policy = LAB_PLACE_CENTER; } + } else if (!strcasecmp(nodename, "xwaylandPersistence.core")) { + set_bool(content, &rc.xwayland_persistence); } else if (!strcmp(nodename, "name.theme")) { rc.theme_name = xstrdup(content); } else if (!strcmp(nodename, "cornerradius.theme")) { diff --git a/src/xwayland.c b/src/xwayland.c index d4eff83d..72eaa7cf 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -6,6 +6,7 @@ #include "common/array.h" #include "common/macros.h" #include "common/mem.h" +#include "config/rcxml.h" #include "labwc.h" #include "node.h" #include "ssd.h" @@ -1052,7 +1053,8 @@ void xwayland_server_init(struct server *server, struct wlr_compositor *compositor) { server->xwayland = - wlr_xwayland_create(server->wl_display, compositor, true); + wlr_xwayland_create(server->wl_display, + compositor, /* lazy */ !rc.xwayland_persistence); if (!server->xwayland) { wlr_log(WLR_ERROR, "cannot create xwayland server"); exit(EXIT_FAILURE); From 30694c2174ba0ca83d5364abd19b031ec41d29a0 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 2 Jul 2024 22:01:30 +0900 Subject: [PATCH 021/232] session-lock: remove cruft The color to fill screen with is always black and we don't need reset its color on every session-lock destruction. --- src/session-lock.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/session-lock.c b/src/session-lock.c index 72db111f..47f28067 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -276,12 +276,6 @@ handle_lock_destroy(struct wl_listener *listener, void *data) struct session_lock_manager *manager = wl_container_of(listener, manager, lock_destroy); - float *black = (float[4]) { 0.f, 0.f, 0.f, 1.f }; - struct session_lock_output *lock_output; - wl_list_for_each(lock_output, &manager->session_lock_outputs, link) { - wlr_scene_rect_set_color(lock_output->background, black); - } - wl_list_remove(&manager->lock_destroy.link); wl_list_remove(&manager->lock_unlock.link); wl_list_remove(&manager->lock_new_surface.link); From 11f02075c2dbe3fd94e79917b77fdf64a04fd2cc Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 2 Jul 2024 22:03:45 +0900 Subject: [PATCH 022/232] session-lock: refactor No changes in logic. --- include/session-lock.h | 2 +- src/session-lock.c | 27 ++++++++++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/include/session-lock.h b/include/session-lock.h index 0a11e7b4..c8961731 100644 --- a/include/session-lock.h +++ b/include/session-lock.h @@ -19,7 +19,7 @@ struct session_lock_manager { struct wlr_session_lock_v1 *lock; bool locked; - struct wl_list session_lock_outputs; + struct wl_list lock_outputs; struct wl_listener new_lock; struct wl_listener destroy; diff --git a/src/session-lock.c b/src/session-lock.c index 47f28067..30b4fcac 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -13,7 +13,7 @@ struct session_lock_output { struct wlr_session_lock_surface_v1 *surface; struct wl_event_source *blank_timer; - struct wl_list link; /* session_lock_manager.outputs */ + struct wl_list link; /* session_lock_manager.lock_outputs */ struct wl_listener destroy; struct wl_listener commit; @@ -37,7 +37,7 @@ refocus_output(struct session_lock_output *output) } struct session_lock_output *iter; - wl_list_for_each(iter, &output->manager->session_lock_outputs, link) { + wl_list_for_each(iter, &output->manager->lock_outputs, link) { if (iter == output || !iter->surface || !iter->surface->surface) { continue; } @@ -109,7 +109,7 @@ handle_new_surface(struct wl_listener *listener, void *data) struct wlr_session_lock_surface_v1 *lock_surface = data; struct output *output = lock_surface->output->data; struct session_lock_output *lock_output; - wl_list_for_each(lock_output, &manager->session_lock_outputs, link) { + wl_list_for_each(lock_output, &manager->lock_outputs, link) { if (lock_output->output == output) { goto found_lock_output; } @@ -211,7 +211,10 @@ session_lock_output_create(struct session_lock_manager *manager, struct output * goto exit_session; } - /* Delay blanking output by 100ms to prevent flashing */ + /* + * Delay blanking output by 100ms to prevent flicker. If the session is + * already locked, blank immediately. + */ lock_output->blank_timer = wl_event_loop_add_timer(manager->server->wl_event_loop, handle_output_blank_timeout, lock_output); @@ -235,7 +238,7 @@ session_lock_output_create(struct session_lock_manager *manager, struct output * lock_output_reconfigure(lock_output); - wl_list_insert(&manager->session_lock_outputs, &lock_output->link); + wl_list_insert(&manager->lock_outputs, &lock_output->link); return; exit_session: @@ -248,7 +251,7 @@ static void session_lock_destroy(struct session_lock_manager *manager) { struct session_lock_output *lock_output, *next; - wl_list_for_each_safe(lock_output, next, &manager->session_lock_outputs, link) { + wl_list_for_each_safe(lock_output, next, &manager->lock_outputs, link) { wlr_scene_node_destroy(&lock_output->tree->node); } if (manager->lock) { @@ -270,12 +273,17 @@ handle_lock_unlock(struct wl_listener *listener, void *data) cursor_update_focus(manager->server); } +/* Called when session-lock is destroyed without unlock */ static void handle_lock_destroy(struct wl_listener *listener, void *data) { struct session_lock_manager *manager = wl_container_of(listener, manager, lock_destroy); + /* + * Destroy session-lock, but manager->locked remains true and + * lock_outputs still hides the screens. + */ wl_list_remove(&manager->lock_destroy.link); wl_list_remove(&manager->lock_unlock.link); wl_list_remove(&manager->lock_new_surface.link); @@ -295,9 +303,10 @@ handle_new_session_lock(struct wl_listener *listener, void *data) } if (manager->locked) { wlr_log(WLR_INFO, "replacing abandoned lock"); + /* clear manager->lock_outputs */ session_lock_destroy(manager); } - assert(wl_list_empty(&manager->session_lock_outputs)); + assert(wl_list_empty(&manager->lock_outputs)); struct output *output; wl_list_for_each(output, &manager->server->outputs, link) { @@ -336,7 +345,7 @@ session_lock_init(struct server *server) server->session_lock_manager = manager; manager->server = server; manager->wlr_manager = wlr_session_lock_manager_v1_create(server->wl_display); - wl_list_init(&manager->session_lock_outputs); + wl_list_init(&manager->lock_outputs); manager->new_lock.notify = handle_new_session_lock; wl_signal_add(&manager->wlr_manager->events.new_lock, &manager->new_lock); @@ -359,7 +368,7 @@ session_lock_update_for_layout_change(struct server *server) struct session_lock_manager *manager = server->session_lock_manager; struct session_lock_output *lock_output; - wl_list_for_each(lock_output, &manager->session_lock_outputs, link) { + wl_list_for_each(lock_output, &manager->lock_outputs, link) { lock_output_reconfigure(lock_output); } } From 880522d142a24e05927d00c7a75e50f61d66f7dc Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 2 Jul 2024 22:04:02 +0900 Subject: [PATCH 023/232] session-lock: never allow multiple session-locks I forgot to set session_lock_manager->lock, so it was always NULL. This barely worked, but allowed multiple session-locks and conflicted with comments in session-lock.h. --- src/session-lock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/session-lock.c b/src/session-lock.c index 30b4fcac..09b62784 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -287,6 +287,7 @@ handle_lock_destroy(struct wl_listener *listener, void *data) wl_list_remove(&manager->lock_destroy.link); wl_list_remove(&manager->lock_unlock.link); wl_list_remove(&manager->lock_new_surface.link); + manager->lock = NULL; } static void @@ -323,6 +324,7 @@ handle_new_session_lock(struct wl_listener *listener, void *data) wl_signal_add(&lock->events.destroy, &manager->lock_destroy); manager->locked = true; + manager->lock = lock; wlr_session_lock_v1_send_locked(lock); } From 6bc93cf4687aa764d795bbe2b4220fe3cbadb0b6 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 2 Jul 2024 22:33:47 +0900 Subject: [PATCH 024/232] session-lock: restore focused view on unlock Before this commit, the topmost view is focused on unlock. This commit changes it to remember the focused view on lock then restore it on unlock. --- include/session-lock.h | 2 ++ src/session-lock.c | 12 +++++++++++- src/view.c | 4 ++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/include/session-lock.h b/include/session-lock.h index c8961731..715c859f 100644 --- a/include/session-lock.h +++ b/include/session-lock.h @@ -10,6 +10,8 @@ struct server; struct session_lock_manager { struct server *server; struct wlr_session_lock_manager_v1 *wlr_manager; + /* View re-focused on unlock */ + struct view *last_active_view; struct wlr_surface *focused; /* * When not locked: lock=NULL, locked=false diff --git a/src/session-lock.c b/src/session-lock.c index 09b62784..9bb54d1a 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -269,7 +269,14 @@ handle_lock_unlock(struct wl_listener *listener, void *data) wl_container_of(listener, manager, lock_unlock); session_lock_destroy(manager); manager->locked = false; - desktop_focus_topmost_view(manager->server); + + if (manager->last_active_view) { + desktop_focus_view(manager->last_active_view, /* raise */ false); + } else { + desktop_focus_topmost_view(manager->server); + } + manager->last_active_view = NULL; + cursor_update_focus(manager->server); } @@ -309,6 +316,9 @@ handle_new_session_lock(struct wl_listener *listener, void *data) } assert(wl_list_empty(&manager->lock_outputs)); + /* Remember the focused view to restore it on unlock */ + manager->last_active_view = manager->server->active_view; + struct output *output; wl_list_for_each(output, &manager->server->outputs, link) { session_lock_output_create(manager, output); diff --git a/src/view.c b/src/view.c index 1646decd..481ec195 100644 --- a/src/view.c +++ b/src/view.c @@ -2396,6 +2396,10 @@ view_destroy(struct view *view) server->active_view = NULL; } + if (server->session_lock_manager->last_active_view == view) { + server->session_lock_manager->last_active_view = NULL; + } + if (server->last_raised_view == view) { server->last_raised_view = NULL; } From 1b122422f5d43a8490ab8b1c5790831b29c4f6ad Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 3 Jul 2024 00:16:30 +0900 Subject: [PATCH 025/232] session-lock: clear focused surface on lock Before this commit, keyboard events were sent to the focused surface before the session-lock client maps its surfaces. --- src/session-lock.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/session-lock.c b/src/session-lock.c index 9bb54d1a..bdf3f053 100644 --- a/src/session-lock.c +++ b/src/session-lock.c @@ -318,6 +318,7 @@ handle_new_session_lock(struct wl_listener *listener, void *data) /* Remember the focused view to restore it on unlock */ manager->last_active_view = manager->server->active_view; + seat_focus_surface(&manager->server->seat, NULL); struct output *output; wl_list_for_each(output, &manager->server->outputs, link) { From 5979cc137a06e218c33d4e88ef484e9761264caf Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 8 Jul 2024 10:56:20 -0400 Subject: [PATCH 026/232] theme: fix memory leak of button icons --- src/theme.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/theme.c b/src/theme.c index 904aeadf..248a352a 100644 --- a/src/theme.c +++ b/src/theme.c @@ -1397,10 +1397,35 @@ theme_init(struct theme *theme, struct server *server, const char *theme_name) void theme_finish(struct theme *theme) { + zdrop(&theme->button_close_active_unpressed); + zdrop(&theme->button_maximize_active_unpressed); + zdrop(&theme->button_restore_active_unpressed); + zdrop(&theme->button_iconify_active_unpressed); + zdrop(&theme->button_menu_active_unpressed); + + zdrop(&theme->button_close_inactive_unpressed); + zdrop(&theme->button_maximize_inactive_unpressed); + zdrop(&theme->button_restore_inactive_unpressed); + zdrop(&theme->button_iconify_inactive_unpressed); + zdrop(&theme->button_menu_inactive_unpressed); + + zdrop(&theme->button_close_active_hover); + zdrop(&theme->button_maximize_active_hover); + zdrop(&theme->button_restore_active_hover); + zdrop(&theme->button_iconify_active_hover); + zdrop(&theme->button_menu_active_hover); + + zdrop(&theme->button_close_inactive_hover); + zdrop(&theme->button_maximize_inactive_hover); + zdrop(&theme->button_restore_inactive_hover); + zdrop(&theme->button_iconify_inactive_hover); + zdrop(&theme->button_menu_inactive_hover); + zdrop(&theme->corner_top_left_active_normal); zdrop(&theme->corner_top_left_inactive_normal); zdrop(&theme->corner_top_right_active_normal); zdrop(&theme->corner_top_right_inactive_normal); + zdrop(&theme->shadow_corner_top_active); zdrop(&theme->shadow_corner_bottom_active); zdrop(&theme->shadow_edge_active); From 3ca1e94b1f068ff46558848d971e6a7d83af97d9 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 8 Jul 2024 11:04:57 -0400 Subject: [PATCH 027/232] menu: try other paths (and fix memory leak) if fopen() fails --- src/menu/menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menu/menu.c b/src/menu/menu.c index d456fb01..b38c25e1 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -671,7 +671,7 @@ parse_xml(const char *filename, struct server *server) struct path *path = wl_container_of(elm, path, link); FILE *stream = fopen(path->string, "r"); if (!stream) { - return; + continue; } wlr_log(WLR_INFO, "read menu file %s", path->string); parse_stream(server, stream); From b1a6a365829aa6462015f57f5dd3bf3f6558b3de Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 8 Jul 2024 11:09:20 -0400 Subject: [PATCH 028/232] menu: fix small memory leaks --- src/menu/menu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/menu/menu.c b/src/menu/menu.c index b38c25e1..8f55808c 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -938,6 +938,8 @@ menu_free(struct menu *menu) */ wlr_scene_node_destroy(&menu->scene_tree->node); wl_list_remove(&menu->link); + zfree(menu->id); + zfree(menu->label); zfree(menu); } From c7b3f4f9d8bfa9a74bdb6a969f8ce749bd193100 Mon Sep 17 00:00:00 2001 From: Weblate Date: Wed, 3 Jul 2024 10:01:16 +0200 Subject: [PATCH 029/232] Translation updates from weblate Co-authored-by: Heimen Stoffels Co-authored-by: Weblate Co-authored-by: tark1998 Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/ko/ Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/nl/ Translation: Labwc/labwc --- po/ko.po | 35 +++++++++++++++++++---------------- po/nl.po | 2 +- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/po/ko.po b/po/ko.po index 27408cd4..0f560b65 100644 --- a/po/ko.po +++ b/po/ko.po @@ -8,62 +8,65 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-03-17 11:06+1000\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2024-07-03 08:01+0000\n" +"Last-Translator: tark1998 \n" +"Language-Team: Korean \n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 4.2.1\n" #: src/menu/menu.c:829 msgid "Reconfigure" -msgstr "" +msgstr "재설정" #: src/menu/menu.c:831 msgid "Exit" -msgstr "" +msgstr "종료" #: src/menu/menu.c:847 msgid "Minimize" -msgstr "" +msgstr "최소화" #: src/menu/menu.c:849 msgid "Maximize" -msgstr "" +msgstr "최대화" #: src/menu/menu.c:851 msgid "Fullscreen" -msgstr "" +msgstr "전체화면" #: src/menu/menu.c:853 msgid "Roll Up/Down" -msgstr "" +msgstr "접기/펼치기" #: src/menu/menu.c:855 msgid "Decorations" -msgstr "" +msgstr "창 장식" #: src/menu/menu.c:857 msgid "Always on Top" -msgstr "" +msgstr "항상 최상단에 표시" #: src/menu/menu.c:862 msgid "Move Left" -msgstr "" +msgstr "좌로 이동" #: src/menu/menu.c:869 msgid "Move Right" -msgstr "" +msgstr "우로 이동" #: src/menu/menu.c:874 msgid "Always on Visible Workspace" -msgstr "" +msgstr "현재 작업공간에 항상 표시" #: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" -msgstr "" +msgstr "작업공간" #: src/menu/menu.c:880 msgid "Close" -msgstr "" +msgstr "닫기" diff --git a/po/nl.po b/po/nl.po index 52e49773..f2d44dc7 100644 --- a/po/nl.po +++ b/po/nl.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-03-17 11:06+1000\n" -"PO-Revision-Date: 2024-01-23 09:23+0000\n" +"PO-Revision-Date: 2024-06-25 11:03+0000\n" "Last-Translator: Heimen Stoffels \n" "Language-Team: Dutch \n" "Language: nl\n" From 80ae55136e186792e5991ab739a800df7f815945 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 8 Jul 2024 20:09:00 +0100 Subject: [PATCH 030/232] NEWS.md: update for 0.7.3 --- NEWS.md | 57 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index 719785d7..6c0e28f2 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-06-09 | [unreleased] | 0.17.3 | | +| 2024-06-12 | [0.7.3] | 0.17.4 | 22731 | | 2024-05-10 | [0.7.2] | 0.17.3 | 21368 | | 2024-03-01 | [0.7.1] | 0.17.1 | 18624 | | 2023-12-22 | [0.7.0] | 0.17.1 | 16576 | @@ -29,15 +29,28 @@ The format is based on [Keep a Changelog] | 2021-04-15 | [0.2.0] | 0.13.0 | 5011 | | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | -## [unreleased] +## [0.7.3] + +Following a couple of big releases, this one feels like more steady with lots of +focus on bug fixes and stability. In terms of new features the most noteworthy +ones include improved tablet support (by @jp7677), `Super_L` on-release keybinds +(by @spl237 from the Raspberry Pi teams) and the screen magnifier which was a +joint effort by @spl237 and @Consolatis. ### Added +- Add config option `` to support keeping XWayland + alive even when no clients are connected. PR #1961 +- Support xdg-shell protocol v3 with popup repositioning. #1950 + Also see https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3514 + which adds support on the wlroots side. +- Add action `ToggleTabletMouseEmulation`. Written-by: jp7677 PR #1915 +- Implement ``. With thanks to @tokyo4j PR #1863 - Add `onRelease` option to `` in support of binding `Super_L` to a menu. Written-by: @spl237 PR #1888 - Add initial support for `security-context-v1` (user configurable blocklists are still missing). Written-by: @nesteroff PR #1817 -- Add partial support for `tablet-v2-manager`. Written-by: @jp7677 PR #1678 +- Add support for `tablet-v2-manager`. Written-by: @jp7677 PR #1678 #1882 - Add action `UnMaximize`. PR #1831 - Support multiple IME popups. PR #1823 - Add `All` context for mouse bindings which need to be handled irrespective of @@ -51,6 +64,33 @@ The format is based on [Keep a Changelog] ### Fixed +- When looking for menu.xml, go through all paths rather than just giving up + if not found in the first path searched. This makes it consistent with how + other config/theme files are handled. PR #1971 +- Fix memory leaks in theme.c and menu.c. PR #1971 +- Fix session-lock bugs related to keyboard focus. PR #1952 + - Clear focused surface on lock + - Restore focused view on unlock +- Fix memory leak in ssd/ssd-shadow.c PR #1954 +- Respect `menu.overlap.x` when using pipemenus. PR #1940 +- Do not try to restore windows to very small width/height on unmaximize. + This fixes a bug with Thonny (Python IDE made with Tk). PR #1938 +- Conditially set squared server-side decoration (SSD) corners when a view is + tiled. Written-by: @jp7677 PR #1926 +- Remember initial direction when starting window-cycling with `PreviousView`. + Also make the toggling of direction when shift is pressed relative to the + initial direction. For example if W-j is bound to PreviousWindow, subsequent + key presses will continue to cycle backwards unless shift is also pressed. + Written-by: @droc12345 PR #1919 +- Show dnd icon above layer-shell surfaces. PR #1936 +- Initialize locale after reading environment files so that client-menu items + and workspace names follow the env var `LANG` should that be set in + `~/.config/labwc/environment` (which is not recommended, but we prefer to + handle it properly if it is). PR #1927 +- Fix crash on `menu.xml` containing `` without a parent ``. + PR #1907 +- Reset XWayland cursor image on cursor theme reload to avoid trying to read + destroyed pixel data. PR #1895 - Prevent child views from opening outside of usable area. PR #1878 - Fix IME popups issues (flicker when popup surface is initially mapped and incorrectly showing multiple popups). PR #1872 @@ -74,15 +114,15 @@ The format is based on [Keep a Changelog] ### Changed +- Remove subprojects/seatd.wrap as no longer needed - Action `MoveToCursor` is deprecated in favour of: ``. ## [0.7.2] -This release is shaping up to be the second in a row that is larger than -usual in terms of both fixes and new features. Significant additions -include input-methods, pipemenus, snap-to-edge overlays and optionally -drop-shadows. +This release shaped up to be the second in a row that is larger than usual in +terms of both fixes and new features. Significant additions include +input-methods, pipemenus, snap-to-edge overlays and optionally drop-shadows. As usual, most of the commits are by the core devs: @ahesford, @Consolatis, @jlindgren90, @johanmalm and @tokyo4j, but we also have many great @@ -1394,7 +1434,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.2...HEAD +[unreleased]: https://github.com/labwc/labwc/compare/0.7.3...HEAD +[0.7.3]: https://github.com/labwc/labwc/compare/0.7.2...0.7.3 [0.7.2]: https://github.com/labwc/labwc/compare/0.7.1...0.7.2 [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 From 307f1991585474e0456c54ad4ec188b4824cc634 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 12 Jul 2024 17:23:47 +0100 Subject: [PATCH 031/232] build: bump version to 0.7.3 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index dcf564be..eddce0c3 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'labwc', 'c', - version: '0.7.2', + version: '0.7.3', license: 'GPL-2.0-only', meson_version: '>=0.59.0', default_options: [ From 3be20dc6c71f3de07fe7e1a9e3b729a9710e69fd Mon Sep 17 00:00:00 2001 From: Birger Schacht <1143280+b1rger@users.noreply.github.com> Date: Sat, 13 Jul 2024 14:14:22 +0200 Subject: [PATCH 032/232] docs: fix typo in labwc-config.5.scd --- docs/labwc-config.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index f212ca0f..1a5e3742 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -966,7 +966,7 @@ situation. ** How long (in milliseconds) the initial button release event is ignored for. The reason for this logic and behaviour is to avoid a fast - press-move-release sequence indended to just open the menu resulting in + press-move-release sequence intended to just open the menu resulting in the closure of the menu or the selection of (typically the first) menu item. This behaviour only affects the first button-release. It is not anticipated that most users will want to change this, but the config From e9fdd1f4938eacd8bdec3a9b0e0b840b4e3d7920 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sun, 14 Jul 2024 21:09:44 +0200 Subject: [PATCH 033/232] CI: add wlroots 0.17 packages for Arch and FreeBSD --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f91f07d..78912600 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,6 +39,7 @@ jobs: container: archlinux:base-devel env: TARGET: 'sh -xe' + PKG_CONFIG_PATH: '/usr/lib/wlroots0.17/pkgconfig' - name: Debian os: ubuntu-latest @@ -69,7 +70,7 @@ jobs: run: | pacman-key --init pacman -Syu --noconfirm - pacman -S --noconfirm git meson clang wlroots libdrm libinput \ + pacman -S --noconfirm git meson clang wlroots0.17 libdrm libinput \ wayland-protocols cairo pango libxml2 xorg-xwayland librsvg \ libdisplay-info @@ -91,7 +92,7 @@ jobs: sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf pkg set -yn pkg:mesa-dri # hack to skip llvm dependency pkg install -y git meson gcc pkgconf cairo pango evdev-proto \ - hwdata wayland-protocols wlroots libdisplay-info + hwdata wayland-protocols wlroots017 libdisplay-info run: echo "setup done" - name: Install Void Linux dependencies From 714511736ba2357d238e7d7de321ccc8d948fa97 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 10 Jul 2024 20:22:52 +0200 Subject: [PATCH 034/232] src/xdg.c: verify source surface for xdg_activation request wlroots < 0.17 didn't allow to reliably check the source surface of an xdg activation request as it reset the surface to NULL when it was destroyed before the token was used. This happens regularly for notifications for example. Thus we treated the token as valid even without checking for the source surface. wlroots 0.17 added a new_token signal where we can attach information to the existing token which we can then use when evaluating activation requests. This patch implements that check. --- include/labwc.h | 1 + src/xdg.c | 49 +++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index a19f8243..c4faf55b 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -248,6 +248,7 @@ struct server { struct wlr_xdg_activation_v1 *xdg_activation; struct wl_listener xdg_activation_request; + struct wl_listener xdg_activation_new_token; struct wl_list views; struct wl_list unmanaged_surfaces; diff --git a/src/xdg.c b/src/xdg.c index 86ee06c3..a24de0a8 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -665,10 +665,39 @@ static const struct view_impl xdg_toplevel_view_impl = { .get_pid = xdg_view_get_pid, }; +struct token_data { + bool had_valid_surface; + bool had_valid_seat; + struct wl_listener destroy; +}; + +static void +xdg_activation_handle_token_destroy(struct wl_listener *listener, void *data) +{ + struct token_data *token_data = wl_container_of(listener, token_data, destroy); + wl_list_remove(&token_data->destroy.link); + free(token_data); +} + +static void +xdg_activation_handle_new_token(struct wl_listener *listener, void *data) +{ + struct wlr_xdg_activation_token_v1 *token = data; + struct token_data *token_data = znew(*token_data); + token_data->had_valid_surface = !!token->surface; + token_data->had_valid_seat = !!token->seat; + token->data = token_data; + + token_data->destroy.notify = xdg_activation_handle_token_destroy; + wl_signal_add(&token->events.destroy, &token_data->destroy); +} + static void xdg_activation_handle_request(struct wl_listener *listener, void *data) { const struct wlr_xdg_activation_v1_request_activate_event *event = data; + struct token_data *token_data = event->token->data; + assert(token_data); struct wlr_xdg_surface *xdg_surface = wlr_xdg_surface_try_from_wlr_surface(event->surface); @@ -681,17 +710,16 @@ xdg_activation_handle_request(struct wl_listener *listener, void *data) wlr_log(WLR_INFO, "Not activating surface - no view attached to surface"); return; } - if (!event->token->seat) { + + if (!token_data->had_valid_seat) { wlr_log(WLR_INFO, "Denying focus request, seat wasn't supplied"); return; } - /* - * We do not check for event->token->surface here because it may already - * be destroyed and thus being NULL. With wlroots 0.17 we can hook into - * the `new_token` signal, attach further information to the token and - * then react to that information here instead. For now we just check - * for the seat / serial being correct and then allow the request. - */ + + if (!token_data->had_valid_surface) { + wlr_log(WLR_INFO, "Denying focus request, source surface not set"); + return; + } if (window_rules_get_property(view, "ignoreFocusRequest") == LAB_PROP_TRUE) { wlr_log(WLR_INFO, "Ignoring focus request due to window rule configuration"); @@ -830,8 +858,13 @@ xdg_shell_init(struct server *server) wlr_log(WLR_ERROR, "unable to create xdg_activation interface"); exit(EXIT_FAILURE); } + server->xdg_activation_request.notify = xdg_activation_handle_request; wl_signal_add(&server->xdg_activation->events.request_activate, &server->xdg_activation_request); + + server->xdg_activation_new_token.notify = xdg_activation_handle_new_token; + wl_signal_add(&server->xdg_activation->events.new_token, + &server->xdg_activation_new_token); } From 7dd7b5cc59561c306f7c7e7489a34d9fdf8b9e40 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 15 Jul 2024 21:47:10 +0100 Subject: [PATCH 035/232] menu: fix crash triggered by pipemenu without parent ...such as the one in the example below: We should consider supporting this construct in future. Reported-by: cry0xen via IRC --- src/menu/menu.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/menu/menu.c b/src/menu/menu.c index 8f55808c..143a8233 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -506,6 +506,23 @@ handle_menu_element(xmlNode *n, struct server *server) if (execute && label && id) { wlr_log(WLR_DEBUG, "pipemenu '%s:%s:%s'", id, label, execute); + if (!current_menu) { + /* + * We currently do not support pipemenus without a + * parent such as the one the example below: + * + * + * + * + * + * + * TODO: Consider supporting this + */ + wlr_log(WLR_ERROR, + "pipemenu '%s:%s:%s' has no parent ", + id, label, execute); + goto error; + } current_item = item_create(current_menu, label, /* arrow */ true); current_item_action = NULL; current_item->execute = xstrdup(execute); From d1fbb3c7ccd929ff4eb2bf97ec9704059d8895bf Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:53:54 +0100 Subject: [PATCH 036/232] chase: wlroots version dep to 0.18 --- meson.build | 2 +- subprojects/wlroots.wrap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index eddce0c3..629447b3 100644 --- a/meson.build +++ b/meson.build @@ -51,7 +51,7 @@ add_project_arguments('-DLABWC_VERSION=@0@'.format(version), language: 'c') wlroots = dependency( 'wlroots', default_options: ['default_library=static', 'examples=false'], - version: ['>=0.17.0', '<0.18.0'], + version: ['>=0.18.0', '<0.19.0'], ) wlroots_has_xwayland = wlroots.get_variable('have_xwayland') == 'true' diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 7ae85823..33e301a6 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 0.17 +revision = 3b4d7d2a926a0b65d3bad6649615c0b6a61c0336 [provide] dependency_names = wlroots From 3e614814feaf25a088a2e27eb5db649af7e3036f Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 19 Mar 2024 00:32:47 +0100 Subject: [PATCH 037/232] chase: input inhibit removal https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4440 --- include/labwc.h | 14 +------- src/input/cursor.c | 12 +------ src/input/keyboard.c | 8 ++--- src/seat.c | 5 --- src/server.c | 71 ---------------------------------------- subprojects/wlroots.wrap | 2 +- 6 files changed, 5 insertions(+), 107 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index c4faf55b..bb1efd8e 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -164,7 +164,6 @@ struct seat { /* Used to prevent region snapping when starting a move with A-Left */ bool region_prevent_snap; - struct wl_client *active_client_while_inhibited; struct wl_list inputs; struct wl_listener new_input; struct wl_listener focus_change; @@ -242,10 +241,6 @@ struct server { struct wl_listener xwayland_new_surface; #endif - struct wlr_input_inhibit_manager *input_inhibit; - struct wl_listener input_inhibit_activate; - struct wl_listener input_inhibit_deactivate; - struct wlr_xdg_activation_v1 *xdg_activation; struct wl_listener xdg_activation_request; struct wl_listener xdg_activation_new_token; @@ -430,7 +425,7 @@ void foreign_toplevel_update_outputs(struct view *view); * - optionally raise above other views * * It's okay to call this function even if the view isn't mapped or the - * session is locked/input is inhibited; it will simply do nothing. + * session is locked; it will simply do nothing. */ void desktop_focus_view(struct view *view, bool raise); @@ -514,13 +509,6 @@ void server_init(struct server *server); void server_start(struct server *server); void server_finish(struct server *server); -/* - * wlroots "input inhibitor" extension (required for swaylock) blocks - * any client other than the requesting client from receiving events - */ -bool input_inhibit_blocks_surface(struct seat *seat, - struct wl_resource *resource); - void create_constraint(struct wl_listener *listener, void *data); void constrain_cursor(struct server *server, struct wlr_pointer_constraint_v1 *constraint); diff --git a/src/input/cursor.c b/src/input/cursor.c index 3c144154..abc2cd74 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -358,15 +358,6 @@ cursor_update_image(struct seat *seat) cursor_names[cursor]); } -bool -input_inhibit_blocks_surface(struct seat *seat, struct wl_resource *resource) -{ - struct wl_client *inhibiting_client = - seat->active_client_while_inhibited; - return inhibiting_client - && inhibiting_client != wl_resource_get_client(resource); -} - static bool update_pressed_surface(struct seat *seat, struct cursor_context *ctx) { @@ -472,8 +463,7 @@ cursor_update_common(struct server *server, struct cursor_context *ctx, return false; } - if (ctx->surface && !input_inhibit_blocks_surface(seat, - ctx->surface->resource)) { + if (ctx->surface) { /* * Cursor is over an input-enabled client surface. The * cursor image will be set by request_cursor_notify() diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 5806441a..a1979aaa 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -457,8 +457,7 @@ handle_compositor_keybindings(struct keyboard *keyboard, if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { if (cur_keybind && cur_keybind->on_release) { key_state_bound_key_remove(event->keycode); - if (seat->server->session_lock_manager->locked - || seat->active_client_while_inhibited) { + if (seat->server->session_lock_manager->locked) { cur_keybind = NULL; return true; } @@ -476,13 +475,10 @@ handle_compositor_keybindings(struct keyboard *keyboard, } /* - * Ignore labwc keybindings if input is inhibited + * Ignore labwc keybindings if the session is locked. * It's important to do this after key_state_set_pressed() to ensure * _all_ key press/releases are registered */ - if (seat->active_client_while_inhibited) { - return false; - } if (seat->server->session_lock_manager->locked) { return false; } diff --git a/src/seat.c b/src/seat.c index ce1751c4..0e7f87f4 100644 --- a/src/seat.c +++ b/src/seat.c @@ -637,11 +637,6 @@ seat_focus(struct seat *seat, struct wlr_surface *surface, bool is_lock_surface) return; } - /* Respect input inhibit (also used by some lock screens) */ - if (input_inhibit_blocks_surface(seat, surface->resource)) { - return; - } - if (!wlr_seat_get_keyboard(seat->seat)) { /* * wlr_seat_keyboard_notify_enter() sends wl_keyboard.modifiers, diff --git a/src/server.c b/src/server.c index 0a093930..8b1a4fd4 100644 --- a/src/server.c +++ b/src/server.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -150,61 +149,6 @@ handle_sigchld(int signal, void *data) return 0; } -static void -seat_inhibit_input(struct seat *seat, struct wl_client *active_client) -{ - seat->active_client_while_inhibited = active_client; - - if (seat->focused_layer && active_client != - wl_resource_get_client(seat->focused_layer->resource)) { - seat_set_focus_layer(seat, NULL); - } - struct wlr_surface *previous_kb_surface = - seat->seat->keyboard_state.focused_surface; - if (previous_kb_surface && active_client != - wl_resource_get_client(previous_kb_surface->resource)) { - seat_focus_surface(seat, NULL); /* keyboard focus */ - } - - struct wlr_seat_client *previous_ptr_client = - seat->seat->pointer_state.focused_client; - if (previous_ptr_client && previous_ptr_client->client != active_client) { - wlr_seat_pointer_clear_focus(seat->seat); - } -} - -static void -seat_disinhibit_input(struct seat *seat) -{ - seat->active_client_while_inhibited = NULL; - - /* - * Triggers a refocus of the topmost surface layer if necessary - * TODO: Make layer surface focus per-output based on cursor position - */ - output_update_all_usable_areas(seat->server, /*layout_changed*/ false); -} - -static void -handle_input_inhibit(struct wl_listener *listener, void *data) -{ - wlr_log(WLR_INFO, "activate input inhibit"); - - struct server *server = - wl_container_of(listener, server, input_inhibit_activate); - seat_inhibit_input(&server->seat, server->input_inhibit->active_client); -} - -static void -handle_input_disinhibit(struct wl_listener *listener, void *data) -{ - wlr_log(WLR_INFO, "deactivate input inhibit"); - - struct server *server = - wl_container_of(listener, server, input_inhibit_deactivate); - seat_disinhibit_input(&server->seat); -} - static void handle_drm_lease_request(struct wl_listener *listener, void *data) { @@ -521,21 +465,6 @@ server_init(struct server *server) wl_signal_add(&server->constraints->events.new_constraint, &server->new_constraint); - server->input_inhibit = - wlr_input_inhibit_manager_create(server->wl_display); - if (!server->input_inhibit) { - wlr_log(WLR_ERROR, "unable to create input inhibit manager"); - exit(EXIT_FAILURE); - } - - wl_signal_add(&server->input_inhibit->events.activate, - &server->input_inhibit_activate); - server->input_inhibit_activate.notify = handle_input_inhibit; - - wl_signal_add(&server->input_inhibit->events.deactivate, - &server->input_inhibit_deactivate); - server->input_inhibit_deactivate.notify = handle_input_disinhibit; - server->foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(server->wl_display); diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 33e301a6..f6e78e26 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 3b4d7d2a926a0b65d3bad6649615c0b6a61c0336 +revision = 5dd614b9adc97bf1c89c8e2ebe8504841f8635ea [provide] dependency_names = wlroots From d37309008c36756cbaf7d38b779559682b597b1f Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 24 Apr 2024 01:52:23 +0200 Subject: [PATCH 038/232] chase: disable restacking of unmanaged windows https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4052 --- src/xwayland-unmanaged.c | 1 - src/xwayland.c | 8 -------- subprojects/wlroots.wrap | 2 +- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/src/xwayland-unmanaged.c b/src/xwayland-unmanaged.c index 72dce1ff..38d3c315 100644 --- a/src/xwayland-unmanaged.c +++ b/src/xwayland-unmanaged.c @@ -42,7 +42,6 @@ handle_map(struct wl_listener *listener, void *data) assert(!unmanaged->node); /* Stack new surface on top */ - wlr_xwayland_surface_restack(xsurface, NULL, XCB_STACK_MODE_ABOVE); wl_list_append(&unmanaged->server->unmanaged_surfaces, &unmanaged->link); CONNECT_SIGNAL(xsurface, unmanaged, set_geometry); diff --git a/src/xwayland.c b/src/xwayland.c index 72eaa7cf..0ed4da1f 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -781,14 +781,6 @@ xwayland_view_move_to_front(struct view *view) */ wlr_xwayland_surface_restack(xwayland_surface_from_view(view), NULL, XCB_STACK_MODE_ABOVE); - - /* Restack unmanaged surfaces on top */ - struct wl_list *list = &view->server->unmanaged_surfaces; - struct xwayland_unmanaged *u; - wl_list_for_each(u, list, link) { - wlr_xwayland_surface_restack(u->xwayland_surface, - NULL, XCB_STACK_MODE_ABOVE); - } } static void diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index f6e78e26..bde76b47 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 5dd614b9adc97bf1c89c8e2ebe8504841f8635ea +revision = f1762f428b0ef2989c81f57ea9e810403d34d946 [provide] dependency_names = wlroots From 50047db8b2f53561a3779c0ddbac65377642c70f Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:55:37 +0100 Subject: [PATCH 039/232] chase: output-layout takes wl_display in constructor https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4310 --- src/output.c | 2 +- src/server.c | 2 -- subprojects/wlroots.wrap | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/output.c b/src/output.c index 1bf01429..38de88de 100644 --- a/src/output.c +++ b/src/output.c @@ -431,7 +431,7 @@ output_init(struct server *server) * Create an output layout, which is a wlroots utility for working with * an arrangement of screens in a physical layout. */ - server->output_layout = wlr_output_layout_create(); + server->output_layout = wlr_output_layout_create(server->wl_display); if (!server->output_layout) { wlr_log(WLR_ERROR, "unable to create output layout"); exit(EXIT_FAILURE); diff --git a/src/server.c b/src/server.c index 8b1a4fd4..dcdf8c44 100644 --- a/src/server.c +++ b/src/server.c @@ -542,8 +542,6 @@ server_finish(struct server *server) wl_display_destroy_clients(server->wl_display); seat_finish(server); - wlr_output_layout_destroy(server->output_layout); - wl_display_destroy(server->wl_display); /* TODO: clean up various scene_tree nodes */ diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index bde76b47..549c6ca7 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = f1762f428b0ef2989c81f57ea9e810403d34d946 +revision = d61ec694b352c0f21c06958c5ef0417f3e424e3c [provide] dependency_names = wlroots From 3b2ab4a48ee56ac9d0304d6380f6cae5c0d03bd9 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Mon, 27 May 2024 15:55:46 +0200 Subject: [PATCH 040/232] chase: move xdg destroy signal to toplevel / popup https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4345 --- src/layers.c | 2 +- src/xdg-popup.c | 2 +- src/xdg.c | 2 +- subprojects/wlroots.wrap | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/layers.c b/src/layers.c index df556761..849e8d34 100644 --- a/src/layers.c +++ b/src/layers.c @@ -412,7 +412,7 @@ create_popup(struct wlr_xdg_popup *wlr_popup, struct wlr_scene_tree *parent) LAB_NODE_DESC_LAYER_POPUP, popup); popup->destroy.notify = popup_handle_destroy; - wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + wl_signal_add(&wlr_popup->events.destroy, &popup->destroy); popup->new_popup.notify = popup_handle_new_popup; wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); diff --git a/src/xdg-popup.c b/src/xdg-popup.c index d6bc99f0..c17d195a 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -101,7 +101,7 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) popup->wlr_popup = wlr_popup; popup->destroy.notify = handle_xdg_popup_destroy; - wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + wl_signal_add(&wlr_popup->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); diff --git a/src/xdg.c b/src/xdg.c index a24de0a8..16474e8f 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -824,9 +824,9 @@ xdg_surface_new(struct wl_listener *listener, void *data) xdg_surface->surface->data = tree; view_connect_map(view, xdg_surface->surface); - CONNECT_SIGNAL(xdg_surface, view, destroy); struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel; + CONNECT_SIGNAL(toplevel, view, destroy); CONNECT_SIGNAL(toplevel, view, request_move); CONNECT_SIGNAL(toplevel, view, request_resize); CONNECT_SIGNAL(toplevel, view, request_minimize); diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 549c6ca7..7434fb1f 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = d61ec694b352c0f21c06958c5ef0417f3e424e3c +revision = c85838892d56111809aa2edb83a2f22428bfa806 [provide] dependency_names = wlroots From d2579a008821264ad847dd094aefb5461ef2e622 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 19 Mar 2024 02:38:09 +0100 Subject: [PATCH 041/232] chase: handle xdg new toplevel event https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4345 --- include/labwc.h | 2 +- src/xdg.c | 20 ++++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index bb1efd8e..cdb4e42f 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -229,7 +229,7 @@ struct server { struct wlr_xdg_shell *xdg_shell; struct wlr_layer_shell_v1 *layer_shell; - struct wl_listener new_xdg_surface; + struct wl_listener new_xdg_toplevel; struct wl_listener new_layer_surface; struct wl_listener kde_server_decoration; diff --git a/src/xdg.c b/src/xdg.c index 16474e8f..14b209ae 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -743,19 +743,14 @@ xdg_activation_handle_request(struct wl_listener *listener, void *data) * to help the popups find their parent nodes */ static void -xdg_surface_new(struct wl_listener *listener, void *data) +xdg_toplevel_new(struct wl_listener *listener, void *data) { struct server *server = - wl_container_of(listener, server, new_xdg_surface); - struct wlr_xdg_surface *xdg_surface = data; + wl_container_of(listener, server, new_xdg_toplevel); + struct wlr_xdg_toplevel *xdg_toplevel = data; + struct wlr_xdg_surface *xdg_surface = xdg_toplevel->base; - /* - * We deal with popups in xdg-popup.c and layers.c as they have to be - * treated differently - */ - if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { - return; - } + assert(xdg_surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL); wlr_xdg_surface_ping(xdg_surface); @@ -850,8 +845,9 @@ xdg_shell_init(struct server *server) wlr_log(WLR_ERROR, "unable to create the XDG shell interface"); exit(EXIT_FAILURE); } - server->new_xdg_surface.notify = xdg_surface_new; - wl_signal_add(&server->xdg_shell->events.new_surface, &server->new_xdg_surface); + + server->new_xdg_toplevel.notify = xdg_toplevel_new; + wl_signal_add(&server->xdg_shell->events.new_toplevel, &server->new_xdg_toplevel); server->xdg_activation = wlr_xdg_activation_v1_create(server->wl_display); if (!server->xdg_activation) { From 6becc02ca4dd2f207896ebd878b0ddb7ebe40fcd Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 23 Apr 2024 22:01:35 +0200 Subject: [PATCH 042/232] chase: let wlr_scene track damage again https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4253 --- src/common/scene-helpers.c | 8 -------- subprojects/wlroots.wrap | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c index 3ed59726..55b44930 100644 --- a/src/common/scene-helpers.c +++ b/src/common/scene-helpers.c @@ -83,14 +83,6 @@ lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output, return false; } - /* - * FIXME: Remove the following line as soon as - * https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4253 - * is merged. At that point wlr_scene handles damage tracking internally - * again. - */ - wlr_damage_ring_rotate(&scene_output->damage_ring); - if (!wlr_box_empty(&additional_damage)) { wlr_damage_ring_add_box(&scene_output->damage_ring, &additional_damage); } diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 7434fb1f..3af4fd57 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = c85838892d56111809aa2edb83a2f22428bfa806 +revision = fe8916fef0bd5d0846dfadd5f8c49ac57beedc9a [provide] dependency_names = wlroots From 72fd2af4f236809bed9759f208d9fd40bdbf7314 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 19 Mar 2024 00:19:24 +0100 Subject: [PATCH 043/232] chase: tearing hint https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4480 --- src/tearing.c | 2 +- subprojects/wlroots.wrap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tearing.c b/src/tearing.c index 45113de5..583fe1e2 100644 --- a/src/tearing.c +++ b/src/tearing.c @@ -15,7 +15,7 @@ set_tearing_hint(struct wl_listener *listener, void *data) { struct tearing_controller *controller = wl_container_of(listener, controller, set_hint); struct view *view = view_from_wlr_surface(controller->tearing_control->surface); - if (view && controller->tearing_control->hint) { + if (view && controller->tearing_control->current) { view->tearing_hint = true; } } diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 3af4fd57..f1e3585e 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = fe8916fef0bd5d0846dfadd5f8c49ac57beedc9a +revision = 1968ada2132237f5bf8e40b6bf903fbce76c0b40 [provide] dependency_names = wlroots From d8f57fe4e7a3d73bb7690a6fc82fcffdc4557a63 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 19 Mar 2024 01:31:23 +0100 Subject: [PATCH 044/232] chase: presentation is now handled internally https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4482 --- src/server.c | 1 - subprojects/wlroots.wrap | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/server.c b/src/server.c index dcdf8c44..f784774d 100644 --- a/src/server.c +++ b/src/server.c @@ -442,7 +442,6 @@ server_init(struct server *server) wlr_log(WLR_ERROR, "unable to create presentation interface"); exit(EXIT_FAILURE); } - wlr_scene_set_presentation(server->scene, presentation); wlr_export_dmabuf_manager_v1_create(server->wl_display); wlr_screencopy_manager_v1_create(server->wl_display); diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index f1e3585e..cd493b39 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 1968ada2132237f5bf8e40b6bf903fbce76c0b40 +revision = ab924064f230cce2aea2e45bd113adea9d37f286 [provide] dependency_names = wlroots From 0d354519e8ed113dd880f965f555b5af90fa8c96 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 19 Mar 2024 01:05:35 +0100 Subject: [PATCH 045/232] chase: relative axis direction https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4003 --- src/input/cursor.c | 2 +- subprojects/wlroots.wrap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input/cursor.c b/src/input/cursor.c index abc2cd74..9e0a72a6 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -1333,7 +1333,7 @@ cursor_axis(struct wl_listener *listener, void *data) wlr_seat_pointer_notify_axis(seat->seat, event->time_msec, event->orientation, rc.scroll_factor * event->delta, round(rc.scroll_factor * event->delta_discrete), - event->source); + event->source, event->relative_direction); } } diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index cd493b39..4c83edb0 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = ab924064f230cce2aea2e45bd113adea9d37f286 +revision = 98c708618ec09907748082850b2d4340fc63055e [provide] dependency_names = wlroots From 36b0dc2db4557709ddfeefeeaa5bcc6cd2a5fe3b Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:53:38 +0100 Subject: [PATCH 046/232] chase: output->pending https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4202 --- include/labwc.h | 1 + include/output-state.h | 34 ++++++++++++++ src/common/scene-helpers.c | 6 ++- src/meson.build | 1 + src/output-state.c | 92 ++++++++++++++++++++++++++++++++++++++ src/output.c | 24 +++++----- src/server.c | 1 + src/view.c | 1 + subprojects/wlroots.wrap | 2 +- 9 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 include/output-state.h create mode 100644 src/output-state.c diff --git a/include/labwc.h b/include/labwc.h index cdb4e42f..ef7ec75d 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -369,6 +369,7 @@ struct output { struct wl_list link; /* server.outputs */ struct server *server; struct wlr_output *wlr_output; + struct wlr_output_state pending; struct wlr_scene_output *scene_output; struct wlr_scene_tree *layer_tree[LAB_NR_LAYERS]; struct wlr_scene_tree *layer_popup_tree; diff --git a/include/output-state.h b/include/output-state.h new file mode 100644 index 00000000..0b90081a --- /dev/null +++ b/include/output-state.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_OUTPUT_STATE_H +#define LABWC_OUTPUT_STATE_H + +#include + +struct output; +struct wlr_output; + +void output_state_init(struct output *output); + +/* Forward port of removed functions */ + +bool wlr_output_test(struct wlr_output *wlr_output); + +bool wlr_output_commit(struct wlr_output *wlr_output); + +void wlr_output_enable(struct wlr_output *wlr_output, bool enabled); + +void wlr_output_set_mode(struct wlr_output *wlr_output, + struct wlr_output_mode *mode); + +void wlr_output_set_custom_mode(struct wlr_output *wlr_output, + int32_t width, int32_t height, int32_t refresh); + +void wlr_output_set_scale(struct wlr_output *wlr_output, float scale); + +void wlr_output_set_transform(struct wlr_output *wlr_output, + enum wl_output_transform transform); + +void wlr_output_enable_adaptive_sync(struct wlr_output *wlr_output, + bool enabled); + +#endif // LABWC_OUTPUT_STATE_H diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c index 55b44930..fb627719 100644 --- a/src/common/scene-helpers.c +++ b/src/common/scene-helpers.c @@ -5,7 +5,9 @@ #include #include #include "common/scene-helpers.h" +#include "labwc.h" #include "magnifier.h" +#include "output-state.h" struct wlr_surface * lab_wlr_surface_from_node(struct wlr_scene_node *node) @@ -57,7 +59,7 @@ lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output, */ if (!wlr_output->needs_frame && !pixman_region32_not_empty( &scene_output->damage_ring.current) && !wants_magnification) { - return false; + return true; } if (!wlr_scene_output_build_state(scene_output, state, NULL)) { @@ -71,7 +73,7 @@ lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output, magnify(output, state->buffer, &additional_damage); } - if (state == &wlr_output->pending) { + if (state == &output->pending) { if (!wlr_output_commit(wlr_output)) { wlr_log(WLR_INFO, "Failed to commit output %s", wlr_output->name); diff --git a/src/meson.build b/src/meson.build index bc69eec4..9676bec1 100644 --- a/src/meson.build +++ b/src/meson.build @@ -15,6 +15,7 @@ labwc_sources = files( 'osd.c', 'osd-field.c', 'output.c', + 'output-state.c', 'output-virtual.c', 'overlay.c', 'placement.c', diff --git a/src/output-state.c b/src/output-state.c new file mode 100644 index 00000000..602149a2 --- /dev/null +++ b/src/output-state.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include "labwc.h" +#include "output-state.h" + +void +output_state_init(struct output *output) +{ + wlr_output_state_init(&output->pending); + + /* + * As there is no direct way to convert an existing output + * configuration to an output_state we first convert it + * to a temporary output-management config and then apply + * it to an empty wlr_output_state. + */ + struct wlr_output_configuration_v1 *backup_config = + wlr_output_configuration_v1_create(); + struct wlr_output_configuration_head_v1 *backup_head = + wlr_output_configuration_head_v1_create( + backup_config, output->wlr_output); + + wlr_output_head_v1_state_apply(&backup_head->state, &output->pending); + wlr_output_configuration_v1_destroy(backup_config); +} + +bool +wlr_output_test(struct wlr_output *wlr_output) +{ + struct output *output = wlr_output->data; + return wlr_output_test_state(wlr_output, &output->pending); +} + +bool +wlr_output_commit(struct wlr_output *wlr_output) +{ + struct output *output = wlr_output->data; + bool committed = wlr_output_commit_state(wlr_output, &output->pending); + if (committed) { + wlr_output_state_finish(&output->pending); + wlr_output_state_init(&output->pending); + } else { + wlr_log(WLR_ERROR, "Failed to commit frame"); + } + return committed; +} + +void +wlr_output_enable(struct wlr_output *wlr_output, bool enabled) +{ + struct output *output = wlr_output->data; + wlr_output_state_set_enabled(&output->pending, enabled); +} + +void +wlr_output_set_mode(struct wlr_output *wlr_output, struct wlr_output_mode *mode) +{ + struct output *output = wlr_output->data; + wlr_output_state_set_mode(&output->pending, mode); +} + +void +wlr_output_set_custom_mode(struct wlr_output *wlr_output, + int32_t width, int32_t height, int32_t refresh) +{ + struct output *output = wlr_output->data; + wlr_output_state_set_custom_mode(&output->pending, width, height, refresh); +} + +void +wlr_output_set_scale(struct wlr_output *wlr_output, float scale) +{ + struct output *output = wlr_output->data; + wlr_output_state_set_scale(&output->pending, scale); +} + +void +wlr_output_set_transform(struct wlr_output *wlr_output, + enum wl_output_transform transform) +{ + struct output *output = wlr_output->data; + wlr_output_state_set_transform(&output->pending, transform); +} + +void +wlr_output_enable_adaptive_sync(struct wlr_output *wlr_output, bool enabled) +{ + struct output *output = wlr_output->data; + wlr_output_state_set_adaptive_sync_enabled(&output->pending, enabled); +} diff --git a/src/output.c b/src/output.c index 38de88de..8ca93c47 100644 --- a/src/output.c +++ b/src/output.c @@ -24,6 +24,7 @@ #include "labwc.h" #include "layers.h" #include "node.h" +#include "output-state.h" #include "output-virtual.h" #include "regions.h" #include "view.h" @@ -111,11 +112,11 @@ output_frame_notify(struct wl_listener *listener, void *data) */ output_apply_gamma(output); } else { - output->wlr_output->pending.tearing_page_flip = + output->pending.tearing_page_flip = get_tearing_preference(output); lab_wlr_scene_output_commit(output->scene_output, - &output->wlr_output->pending); + &output->pending); } struct timespec now = { 0 }; @@ -158,6 +159,8 @@ output_destroy_notify(struct wl_listener *listener, void *data) } } + wlr_output_state_finish(&output->pending); + /* * Ensure that we don't accidentally try to dereference * the output pointer in some output event handler like @@ -299,6 +302,12 @@ new_output_notify(struct wl_listener *listener, void *data) return; } + output = znew(*output); + output->wlr_output = wlr_output; + wlr_output->data = output; + output->server = server; + output_state_init(output); + wlr_log(WLR_DEBUG, "enable output"); wlr_output_enable(wlr_output, true); @@ -311,7 +320,9 @@ new_output_notify(struct wl_listener *listener, void *data) wlr_log(WLR_DEBUG, "set preferred mode"); /* The mode is a tuple of (width, height, refresh rate). */ preferred_mode = wlr_output_preferred_mode(wlr_output); - wlr_output_set_mode(wlr_output, preferred_mode); + if (preferred_mode) { + wlr_output_set_mode(wlr_output, preferred_mode); + } } /* @@ -341,10 +352,6 @@ new_output_notify(struct wl_listener *listener, void *data) wlr_output_commit(wlr_output); - output = znew(*output); - output->wlr_output = wlr_output; - wlr_output->data = output; - output->server = server; wlr_output_effective_resolution(wlr_output, &output->usable_area.width, &output->usable_area.height); wl_list_insert(&server->outputs, &output->link); @@ -923,9 +930,6 @@ handle_output_power_manager_set_mode(struct wl_listener *listener, void *data) break; case ZWLR_OUTPUT_POWER_V1_MODE_ON: wlr_output_enable(event->output, true); - if (!wlr_output_test(event->output)) { - wlr_output_rollback(event->output); - } wlr_output_commit(event->output); /* * Re-set the cursor image so that the cursor diff --git a/src/server.c b/src/server.c index f784774d..f380b9cd 100644 --- a/src/server.c +++ b/src/server.c @@ -29,6 +29,7 @@ #include "labwc.h" #include "layers.h" #include "menu/menu.h" +#include "output-state.h" #include "output-virtual.h" #include "regions.h" #include "resize-indicator.h" diff --git a/src/view.c b/src/view.c index 481ec195..3486b19e 100644 --- a/src/view.c +++ b/src/view.c @@ -12,6 +12,7 @@ #include "labwc.h" #include "menu/menu.h" #include "osd.h" +#include "output-state.h" #include "placement.h" #include "regions.h" #include "resize-indicator.h" diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 4c83edb0..e967b9d3 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 98c708618ec09907748082850b2d4340fc63055e +revision = cca2bfbe92205260c75a82ad6b6a7c5bae1599de [provide] dependency_names = wlroots From 0a03c00b8ba5321d356d01ad6df26c1bef4c4973 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 19 Mar 2024 01:47:23 +0100 Subject: [PATCH 047/232] chase: backend create now requires event_loop arg https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4443 --- src/server.c | 5 +++-- subprojects/wlroots.wrap | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/server.c b/src/server.c index f380b9cd..4721e160 100644 --- a/src/server.c +++ b/src/server.c @@ -291,7 +291,7 @@ server_init(struct server *server) * window if an x11 server is running. */ server->backend = wlr_backend_autocreate( - server->wl_display, &server->session); + server->wl_event_loop, &server->session); if (!server->backend) { wlr_log(WLR_ERROR, "unable to create backend"); fprintf(stderr, helpful_seat_error_message); @@ -304,7 +304,8 @@ server_init(struct server *server) if (!server->headless.backend) { wlr_log(WLR_DEBUG, "manually creating headless backend"); - server->headless.backend = wlr_headless_backend_create(server->wl_display); + server->headless.backend = wlr_headless_backend_create( + server->wl_event_loop); } else { wlr_log(WLR_DEBUG, "headless backend already exists"); } diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index e967b9d3..28c2046e 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = cca2bfbe92205260c75a82ad6b6a7c5bae1599de +revision = d1b39b58432c471c16e09103fd2c7850e3c41950 [provide] dependency_names = wlroots From d8d45dc2cb4124a45bcbfb7a71752c71640962fa Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 23 Apr 2024 23:37:08 +0200 Subject: [PATCH 048/232] chase: request initial configure https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4396 --- src/decorations/xdg-deco.c | 37 +++++++++++++++++++++++++++++++++++-- src/xdg.c | 16 ++++++++++------ subprojects/wlroots.wrap | 2 +- 3 files changed, 46 insertions(+), 9 deletions(-) diff --git a/src/decorations/xdg-deco.c b/src/decorations/xdg-deco.c index c79964dd..97257ed4 100644 --- a/src/decorations/xdg-deco.c +++ b/src/decorations/xdg-deco.c @@ -7,9 +7,11 @@ struct xdg_deco { struct wlr_xdg_toplevel_decoration_v1 *wlr_xdg_decoration; + enum wlr_xdg_toplevel_decoration_v1_mode client_mode; struct view *view; struct wl_listener destroy; struct wl_listener request_mode; + struct wl_listener surface_commit; }; static void @@ -18,9 +20,26 @@ xdg_deco_destroy(struct wl_listener *listener, void *data) struct xdg_deco *xdg_deco = wl_container_of(listener, xdg_deco, destroy); wl_list_remove(&xdg_deco->destroy.link); wl_list_remove(&xdg_deco->request_mode.link); + if (xdg_deco->surface_commit.notify) { + wl_list_remove(&xdg_deco->surface_commit.link); + xdg_deco->surface_commit.notify = NULL; + } free(xdg_deco); } +static void +handle_surface_commit(struct wl_listener *listener, void *data) +{ + struct xdg_deco *xdg_deco = wl_container_of(listener, xdg_deco, surface_commit); + struct wlr_xdg_toplevel_decoration_v1 *deco = xdg_deco->wlr_xdg_decoration; + + if (deco->toplevel->base->initial_commit) { + wlr_xdg_toplevel_decoration_v1_set_mode(deco, xdg_deco->client_mode); + wl_list_remove(&xdg_deco->surface_commit.link); + xdg_deco->surface_commit.notify = NULL; + } +} + static void xdg_deco_request_mode(struct wl_listener *listener, void *data) { @@ -46,8 +65,22 @@ xdg_deco_request_mode(struct wl_listener *listener, void *data) "requested: %u", client_mode); } - wlr_xdg_toplevel_decoration_v1_set_mode(xdg_deco->wlr_xdg_decoration, - client_mode); + /* + * We may get multiple request_mode calls in an unitialized state. + * Just update the last requested mode and only add the commit + * handler on the first uninitialized state call. + */ + xdg_deco->client_mode = client_mode; + + if (xdg_deco->wlr_xdg_decoration->toplevel->base->initialized) { + wlr_xdg_toplevel_decoration_v1_set_mode(xdg_deco->wlr_xdg_decoration, + client_mode); + } else if (!xdg_deco->surface_commit.notify) { + xdg_deco->surface_commit.notify = handle_surface_commit; + wl_signal_add( + &xdg_deco->wlr_xdg_decoration->toplevel->base->surface->events.commit, + &xdg_deco->surface_commit); + } if (client_mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) { view_set_ssd_mode(xdg_deco->view, LAB_SSD_MODE_FULL); } else { diff --git a/src/xdg.c b/src/xdg.c index 14b209ae..84d03bd3 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -82,6 +82,12 @@ handle_commit(struct wl_listener *listener, void *data) struct wlr_xdg_surface *xdg_surface = xdg_surface_from_view(view); assert(view->surface); + if (xdg_surface->initial_commit) { + wlr_log(WLR_DEBUG, "scheduling configure"); + wlr_xdg_surface_schedule_configure(xdg_surface); + return; + } + struct wlr_box size; wlr_xdg_surface_get_geometry(xdg_surface, &size); @@ -214,6 +220,7 @@ handle_destroy(struct wl_listener *listener, void *data) /* Remove xdg-shell view specific listeners */ wl_list_remove(&xdg_toplevel_view->set_app_id.link); wl_list_remove(&xdg_toplevel_view->new_popup.link); + wl_list_remove(&view->commit.link); if (view->pending_configure_timeout) { wl_event_source_remove(view->pending_configure_timeout); @@ -560,7 +567,6 @@ xdg_toplevel_view_map(struct view *view) view_set_output(view, output_nearest_to_cursor(view->server)); } struct wlr_xdg_surface *xdg_surface = xdg_surface_from_view(view); - view->surface = xdg_surface->surface; wlr_scene_node_set_enabled(&view->scene_tree->node, true); if (!view->been_mapped) { struct wlr_xdg_toplevel_requested *requested = @@ -614,9 +620,6 @@ xdg_toplevel_view_map(struct view *view) view_moved(view); } - view->commit.notify = handle_commit; - wl_signal_add(&xdg_surface->surface->events.commit, &view->commit); - view_impl_map(view); view->been_mapped = true; } @@ -627,7 +630,6 @@ xdg_toplevel_view_unmap(struct view *view, bool client_request) if (view->mapped) { view->mapped = false; wlr_scene_node_set_enabled(&view->scene_tree->node, false); - wl_list_remove(&view->commit.link); view_impl_unmap(view); } } @@ -816,7 +818,8 @@ xdg_toplevel_new(struct wl_listener *listener, void *data) kde_server_decoration_set_view(view, xdg_surface->surface); /* In support of xdg popups and IME popup */ - xdg_surface->surface->data = tree; + view->surface = xdg_surface->surface; + view->surface->data = tree; view_connect_map(view, xdg_surface->surface); @@ -828,6 +831,7 @@ xdg_toplevel_new(struct wl_listener *listener, void *data) CONNECT_SIGNAL(toplevel, view, request_maximize); CONNECT_SIGNAL(toplevel, view, request_fullscreen); CONNECT_SIGNAL(toplevel, view, set_title); + CONNECT_SIGNAL(view->surface, view, commit); /* Events specific to XDG toplevel views */ CONNECT_SIGNAL(toplevel, xdg_toplevel_view, set_app_id); diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 28c2046e..62a0f0ba 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = d1b39b58432c471c16e09103fd2c7850e3c41950 +revision = 811ca199c444525dc4d846d38f76554f1a9b48b0 [provide] dependency_names = wlroots From 5c6e1ed8786e1b4c7130058a8cd74dda85baccef Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 19 Mar 2024 01:32:05 +0100 Subject: [PATCH 049/232] chase: use wayland pointer enums rather than wlr ones https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4575 --- include/input/cursor.h | 4 ++-- src/input/cursor.c | 14 +++++++------- src/input/tablet-pad.c | 6 +++++- src/input/tablet.c | 10 +++++++--- src/input/touch.c | 6 +++--- subprojects/wlroots.wrap | 2 +- 6 files changed, 25 insertions(+), 17 deletions(-) diff --git a/include/input/cursor.h b/include/input/cursor.h index 24f7326d..e9ad4fa2 100644 --- a/include/input/cursor.h +++ b/include/input/cursor.h @@ -11,7 +11,7 @@ struct seat; struct server; struct wlr_surface; struct wlr_scene_node; -enum wlr_button_state; +enum wl_pointer_button_state; /* Cursors used internally by labwc */ enum lab_cursors { @@ -150,7 +150,7 @@ void cursor_emulate_move_absolute(struct seat *seat, struct wlr_input_device *device, double x, double y, uint32_t time_msec); void cursor_emulate_button(struct seat *seat, - uint32_t button, enum wlr_button_state state, uint32_t time_msec); + uint32_t button, enum wl_pointer_button_state state, uint32_t time_msec); void cursor_finish(struct seat *seat); #endif /* LABWC_CURSOR_H */ diff --git a/src/input/cursor.c b/src/input/cursor.c index 9e0a72a6..6bda5a30 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -1153,7 +1153,7 @@ cursor_button(struct wl_listener *listener, void *data) bool notify; switch (event->state) { - case WLR_BUTTON_PRESSED: + case WL_POINTER_BUTTON_STATE_PRESSED: notify = cursor_process_button_press(seat, event->button, event->time_msec); if (notify) { @@ -1161,7 +1161,7 @@ cursor_button(struct wl_listener *listener, void *data) event->button, event->state); } break; - case WLR_BUTTON_RELEASED: + case WL_POINTER_BUTTON_STATE_RELEASED: notify = cursor_process_button_release(seat, event->button, event->time_msec); if (notify) { @@ -1207,19 +1207,19 @@ cursor_emulate_move_absolute(struct seat *seat, struct wlr_input_device *device, void cursor_emulate_button(struct seat *seat, uint32_t button, - enum wlr_button_state state, uint32_t time_msec) + enum wl_pointer_button_state state, uint32_t time_msec) { idle_manager_notify_activity(seat->seat); bool notify; switch (state) { - case WLR_BUTTON_PRESSED: + case WL_POINTER_BUTTON_STATE_PRESSED: notify = cursor_process_button_press(seat, button, time_msec); if (notify) { wlr_seat_pointer_notify_button(seat->seat, time_msec, button, state); } break; - case WLR_BUTTON_RELEASED: + case WL_POINTER_BUTTON_STATE_RELEASED: notify = cursor_process_button_release(seat, button, time_msec); if (notify) { wlr_seat_pointer_notify_button(seat->seat, time_msec, button, state); @@ -1272,14 +1272,14 @@ handle_cursor_axis(struct server *server, struct cursor_context *ctx, &server->seat.keyboard_group->keyboard); enum direction direction = LAB_DIRECTION_INVALID; - if (event->orientation == WLR_AXIS_ORIENTATION_HORIZONTAL) { + if (event->orientation == WL_POINTER_AXIS_HORIZONTAL_SCROLL) { int rel = compare_delta(event, &server->seat.smooth_scroll_offset.x); if (rel < 0) { direction = LAB_DIRECTION_LEFT; } else if (rel > 0) { direction = LAB_DIRECTION_RIGHT; } - } else if (event->orientation == WLR_AXIS_ORIENTATION_VERTICAL) { + } else if (event->orientation == WL_POINTER_AXIS_VERTICAL_SCROLL) { int rel = compare_delta(event, &server->seat.smooth_scroll_offset.y); if (rel < 0) { direction = LAB_DIRECTION_UP; diff --git a/src/input/tablet-pad.c b/src/input/tablet-pad.c index 37f597ff..0b1fa9b4 100644 --- a/src/input/tablet-pad.c +++ b/src/input/tablet-pad.c @@ -116,7 +116,11 @@ handle_button(struct wl_listener *listener, void *data) } else { uint32_t button = tablet_get_mapped_button(ev->button); if (button) { - cursor_emulate_button(pad->seat, button, ev->state, ev->time_msec); + cursor_emulate_button(pad->seat, button, + ev->state == WLR_BUTTON_PRESSED + ? WL_POINTER_BUTTON_STATE_PRESSED + : WL_POINTER_BUTTON_STATE_RELEASED, + ev->time_msec); } } } diff --git a/src/input/tablet.c b/src/input/tablet.c index 3d8caa61..d75eea0e 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -441,8 +441,8 @@ handle_tip(struct wl_listener *listener, void *data) cursor_emulate_button(tablet->seat, button, ev->state == WLR_TABLET_TOOL_TIP_DOWN - ? WLR_BUTTON_PRESSED - : WLR_BUTTON_RELEASED, + ? WL_POINTER_BUTTON_STATE_PRESSED + : WL_POINTER_BUTTON_STATE_RELEASED, ev->time_msec); } } @@ -500,7 +500,11 @@ handle_button(struct wl_listener *listener, void *data) } else { if (button) { is_down_mouse_emulation = ev->state == WLR_BUTTON_PRESSED; - cursor_emulate_button(tablet->seat, button, ev->state, ev->time_msec); + cursor_emulate_button(tablet->seat, button, + ev->state == WLR_BUTTON_PRESSED + ? WL_POINTER_BUTTON_STATE_PRESSED + : WL_POINTER_BUTTON_STATE_RELEASED, + ev->time_msec); } } } diff --git a/src/input/touch.c b/src/input/touch.c index 048aaf2d..d2419d1a 100644 --- a/src/input/touch.c +++ b/src/input/touch.c @@ -127,7 +127,7 @@ touch_down(struct wl_listener *listener, void *data) } else { cursor_emulate_move_absolute(seat, &event->touch->base, event->x, event->y, event->time_msec); - cursor_emulate_button(seat, BTN_LEFT, WLR_BUTTON_PRESSED, + cursor_emulate_button(seat, BTN_LEFT, WL_POINTER_BUTTON_STATE_PRESSED, event->time_msec); } } @@ -146,8 +146,8 @@ touch_up(struct wl_listener *listener, void *data) wlr_seat_touch_notify_up(seat->seat, event->time_msec, event->touch_id); } else { - cursor_emulate_button(seat, BTN_LEFT, WLR_BUTTON_RELEASED, - event->time_msec); + cursor_emulate_button(seat, BTN_LEFT, + WL_POINTER_BUTTON_STATE_RELEASED, event->time_msec); } wl_list_remove(&touch_point->link); zfree(touch_point); diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 62a0f0ba..f2a1724b 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 811ca199c444525dc4d846d38f76554f1a9b48b0 +revision = 488a23c16908a83041cf28e134a6f149d831598d [provide] dependency_names = wlroots From d16c6dd63af33f664610982691c8dd8048081d83 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 19 Mar 2024 00:22:44 +0100 Subject: [PATCH 050/232] chase: tablet_pad to tablet https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4585 --- src/seat.c | 10 +++++----- subprojects/wlroots.wrap | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/seat.c b/src/seat.c index 0e7f87f4..7987b5a3 100644 --- a/src/seat.c +++ b/src/seat.c @@ -40,7 +40,7 @@ device_type_from_wlr_device(struct wlr_input_device *wlr_input_device) { switch (wlr_input_device->type) { case WLR_INPUT_DEVICE_TOUCH: - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: return LAB_LIBINPUT_DEVICE_TOUCH; default: break; @@ -397,7 +397,7 @@ seat_update_capabilities(struct seat *seat) caps |= WL_SEAT_CAPABILITY_KEYBOARD; break; case WLR_INPUT_DEVICE_POINTER: - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: caps |= WL_SEAT_CAPABILITY_POINTER; break; case WLR_INPUT_DEVICE_TOUCH: @@ -438,7 +438,7 @@ new_input_notify(struct wl_listener *listener, void *data) case WLR_INPUT_DEVICE_TOUCH: input = new_touch(seat, device); break; - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: input = new_tablet(seat, device); break; case WLR_INPUT_DEVICE_TABLET_PAD: @@ -609,7 +609,7 @@ seat_reconfigure(struct server *server) configure_libinput(input->wlr_input_device); map_touch_to_output(seat, input->wlr_input_device); break; - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: map_input_to_output(seat, input->wlr_input_device, rc.tablet.output_name); break; default: @@ -759,7 +759,7 @@ seat_output_layout_changed(struct seat *seat) case WLR_INPUT_DEVICE_TOUCH: map_touch_to_output(seat, input->wlr_input_device); break; - case WLR_INPUT_DEVICE_TABLET_TOOL: + case WLR_INPUT_DEVICE_TABLET: map_input_to_output(seat, input->wlr_input_device, rc.tablet.output_name); break; default: diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index f2a1724b..db4faf0a 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 488a23c16908a83041cf28e134a6f149d831598d +revision = b821be5749061b0b73d777cb2fc74204cbf78278 [provide] dependency_names = wlroots From 924e31f089fab04b2f261bf0537a54887af4cec7 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 28 May 2024 01:52:04 +0200 Subject: [PATCH 051/232] chase: version pkgconfig, headers, and library https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4614 --- meson.build | 2 +- subprojects/wlroots.wrap | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 629447b3..3ec6eeb1 100644 --- a/meson.build +++ b/meson.build @@ -49,7 +49,7 @@ endif add_project_arguments('-DLABWC_VERSION=@0@'.format(version), language: 'c') wlroots = dependency( - 'wlroots', + 'wlroots-0.18', default_options: ['default_library=static', 'examples=false'], version: ['>=0.18.0', '<0.19.0'], ) diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index db4faf0a..5ac4b36f 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,7 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = b821be5749061b0b73d777cb2fc74204cbf78278 +revision = 4b4f76cc13574544216b59b3a9902b468b82108b [provide] -dependency_names = wlroots +dependency_names = wlroots-0.18 +wlroots-0.18=wlroots From 0d00e9a23288c35473c0ad52b3a8aebd642a34ac Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sat, 29 Jun 2024 21:05:02 +0200 Subject: [PATCH 052/232] chase: move to stable tablet protocol and require w-p >= 1.35 https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4645 --- meson.build | 2 +- protocols/meson.build | 2 +- subprojects/wlroots.wrap | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 3ec6eeb1..267b91b3 100644 --- a/meson.build +++ b/meson.build @@ -57,7 +57,7 @@ wlroots = dependency( wlroots_has_xwayland = wlroots.get_variable('have_xwayland') == 'true' wayland_server = dependency('wayland-server', version: '>=1.19.0') -wayland_protos = dependency('wayland-protocols') +wayland_protos = dependency('wayland-protocols', version: '>=1.35') xkbcommon = dependency('xkbcommon') xcb = dependency('xcb', required: get_option('xwayland')) xcb_icccm = dependency('xcb-icccm', required: get_option('xwayland')) diff --git a/protocols/meson.build b/protocols/meson.build index b0c2f5ab..0ee40bf4 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -16,7 +16,7 @@ wayland_scanner_server = generator( server_protocols = [ wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', - wl_protocol_dir / 'unstable/tablet/tablet-unstable-v2.xml', + wl_protocol_dir / 'stable/tablet/tablet-v2.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', diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index 5ac4b36f..ac960c58 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = 4b4f76cc13574544216b59b3a9902b468b82108b +revision = fe429b2463f279a6274bb6f42a3e23525a3c97aa [provide] dependency_names = wlroots-0.18 From 93f817e20e8e3e3c146c03165946530a2e5b006c Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sat, 13 Jul 2024 17:46:39 +0200 Subject: [PATCH 053/232] chase: wlroots.wrap: track the wlroots 0.18 branch --- subprojects/wlroots.wrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subprojects/wlroots.wrap b/subprojects/wlroots.wrap index ac960c58..838b2633 100644 --- a/subprojects/wlroots.wrap +++ b/subprojects/wlroots.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://gitlab.freedesktop.org/wlroots/wlroots.git -revision = fe429b2463f279a6274bb6f42a3e23525a3c97aa +revision = 0.18 [provide] dependency_names = wlroots-0.18 From 8ddfcb80e51d2a85fcd535362011367c70949308 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 17 Jul 2024 17:49:01 +0200 Subject: [PATCH 054/232] CI: switch Arch and FreeBSD to wlroots 0.18 Also add wlroots0.18-devel to Void, assuming that is what the package will be called once available. For previous packaging see https://github.com/void-linux/void-packages/pull/48323/files --- .github/workflows/build.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 78912600..88f83f52 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,6 @@ jobs: container: archlinux:base-devel env: TARGET: 'sh -xe' - PKG_CONFIG_PATH: '/usr/lib/wlroots0.17/pkgconfig' - name: Debian os: ubuntu-latest @@ -70,7 +69,7 @@ jobs: run: | pacman-key --init pacman -Syu --noconfirm - pacman -S --noconfirm git meson clang wlroots0.17 libdrm libinput \ + pacman -S --noconfirm git meson clang wlroots libdrm libinput \ wayland-protocols cairo pango libxml2 xorg-xwayland librsvg \ libdisplay-info @@ -92,7 +91,7 @@ jobs: sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf pkg set -yn pkg:mesa-dri # hack to skip llvm dependency pkg install -y git meson gcc pkgconf cairo pango evdev-proto \ - hwdata wayland-protocols wlroots017 libdisplay-info + hwdata wayland-protocols wlroots libdisplay-info run: echo "setup done" - name: Install Void Linux dependencies @@ -106,7 +105,7 @@ jobs: xbps-install -Syu xbps-install -y git meson gcc clang pkg-config scdoc \ cairo-devel glib-devel libpng-devel librsvg-devel libxml2-devel \ - pango-devel wlroots0.17-devel gdb bash xorg-server-xwayland \ + pango-devel wlroots0.18-devel gdb bash xorg-server-xwayland \ dejavu-fonts-ttf # These build are executed on all runners From 40684bad9d402a91bfce90b2f45f99d09f47d75b Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 16 Jul 2024 20:39:48 +0200 Subject: [PATCH 055/232] magnifier: disable direct scanout when active Fixes: #1980 --- include/labwc.h | 1 + src/magnifier.c | 24 +++++++++++++----------- src/server.c | 1 + 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index ef7ec75d..0aa4526f 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -251,6 +251,7 @@ struct server { struct seat seat; struct wlr_scene *scene; struct wlr_scene_output_layout *scene_layout; + bool direct_scanout_enabled; /* cursor interactive */ enum input_mode input_mode; diff --git a/src/magnifier.c b/src/magnifier.c index 29b16154..243455aa 100644 --- a/src/magnifier.c +++ b/src/magnifier.c @@ -112,7 +112,6 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box return; } - /* Paste the magnified result back into the output buffer */ if (!tmp_texture) { tmp_texture = wlr_texture_from_buffer(server->renderer, tmp_buffer); } @@ -124,7 +123,6 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box } /* Extract source region into temporary buffer */ - struct wlr_render_pass *tmp_render_pass = wlr_renderer_begin_buffer_pass( server->renderer, tmp_buffer, NULL); @@ -198,6 +196,7 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box dst_box.y = oy - (height / 2); } + /* Paste the magnified result back into the output buffer */ opts = (struct wlr_render_texture_options) { .texture = tmp_texture, .src_box = src_box, @@ -241,18 +240,21 @@ output_wants_magnification(struct output *output) return output_nearest_to_cursor(output->server) == output; } +static void +enable_magnifier(struct server *server, bool enable) +{ + magnify_on = enable; + server->scene->direct_scanout = enable ? false + : server->direct_scanout_enabled; +} + /* Toggles magnification on and off */ void magnify_toggle(struct server *server) { + enable_magnifier(server, !magnify_on); + struct output *output = output_nearest_to_cursor(server); - - if (magnify_on) { - magnify_on = false; - } else { - magnify_on = true; - } - if (output) { wlr_output_schedule_frame(output->wlr_output); } @@ -268,14 +270,14 @@ magnify_set_scale(struct server *server, enum magnify_dir dir) if (magnify_on) { mag_scale += rc.mag_increment; } else { - magnify_on = true; + enable_magnifier(server, true); mag_scale = 1.0 + rc.mag_increment; } } else { if (magnify_on && mag_scale > 1.0 + rc.mag_increment) { mag_scale -= rc.mag_increment; } else { - magnify_on = false; + enable_magnifier(server, false); } } diff --git a/src/server.c b/src/server.c index 4721e160..b8beee2c 100644 --- a/src/server.c +++ b/src/server.c @@ -360,6 +360,7 @@ server_init(struct server *server) wlr_log(WLR_ERROR, "unable to create scene"); exit(EXIT_FAILURE); } + server->direct_scanout_enabled = server->scene->direct_scanout; /* * The order in which the scene-trees below are created determines the From 9e9c29d7f53359c85d5c294c2844b46f02dafaf6 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 17 Jul 2024 18:01:15 -0700 Subject: [PATCH 056/232] config: initialize some missing defaults --- src/config/rcxml.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index d263cb9d..84c117b6 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1235,6 +1235,12 @@ rcxml_init(void) rc.corner_radius = 8; rc.shadows_enabled = false; + rc.gap = 0; + rc.adaptive_sync = LAB_ADAPTIVE_SYNC_DISABLED; + rc.allow_tearing = false; + rc.reuse_output_mode = false; + rc.xwayland_persistence = false; + init_font_defaults(&rc.font_activewindow); init_font_defaults(&rc.font_inactivewindow); init_font_defaults(&rc.font_menuitem); From c20c3991c75f177680f5a77b67f243ffd439a97d Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Fri, 19 Jul 2024 21:45:24 +0200 Subject: [PATCH 057/232] ssd: make borders respect snapped state on Reconfigure Fixes: #1862 --- src/ssd/ssd-border.c | 8 ++++++++ src/ssd/ssd.c | 8 +++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index 63604244..06ce55c3 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -56,6 +56,14 @@ ssd_border_create(struct ssd *ssd) if (view->maximized == VIEW_AXIS_BOTH) { wlr_scene_node_set_enabled(&ssd->border.tree->node, false); } + + if (view->current.width > 0 && view->current.height > 0) { + /* + * The SSD is recreated by a Reconfigure request + * thus we may need to handle squared corners. + */ + ssd_border_update(ssd); + } } void diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 86ac1272..70b1b0db 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -188,8 +188,14 @@ ssd_create(struct view *view, bool active) ssd->titlebar.height = view->server->theme->title_height; ssd_shadow_create(ssd); ssd_extents_create(ssd); - ssd_border_create(ssd); + /* + * We need to create the borders after the titlebar because it sets + * ssd->state.was_tiled_not_maximized which ssd_border_create() + * reacts to. TODO: Set the state here instead so the order does + * not matter anymore. + */ ssd_titlebar_create(ssd); + ssd_border_create(ssd); if (view->ssd_titlebar_hidden) { /* Ensure we keep the old state on Reconfigure or when exiting fullscreen */ ssd_set_titlebar(ssd, false); From c3ea956837c31492b956cd9ab4b34ec6ac512628 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sun, 14 Jul 2024 08:55:30 +0100 Subject: [PATCH 058/232] Disable pango glyph position rounding Chase https://github.com/swaywm/sway/commit/8c5b23e592d2334b3324227dd9d1311e46c5fd69 Pango rounds glyph position and widths to nearest integer, which leads to font dimensions jumping around when rendering with a scale, causing text geometry to jump around when changing scale. Disable this rounding to make the geometry stable. --- src/common/font.c | 2 ++ src/osd.c | 1 + src/workspaces.c | 1 + 3 files changed, 4 insertions(+) diff --git a/src/common/font.c b/src/common/font.c index c9e6166c..6e12e780 100644 --- a/src/common/font.c +++ b/src/common/font.c @@ -40,6 +40,7 @@ font_extents(struct font *font, const char *string) surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 1, 1); c = cairo_create(surface); layout = pango_cairo_create_layout(c); + pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); PangoFontDescription *desc = font_to_pango_desc(font); pango_layout_set_font_description(layout, desc); @@ -137,6 +138,7 @@ font_buffer_create(struct lab_data_buffer **buffer, int max_width, cairo_move_to(cairo, 0, 0); PangoLayout *layout = pango_cairo_create_layout(cairo); + pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); pango_layout_set_width(layout, text_extents.width * PANGO_SCALE); pango_layout_set_text(layout, text, -1); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); diff --git a/src/osd.c b/src/osd.c index 9555d913..91dbd270 100644 --- a/src/osd.c +++ b/src/osd.c @@ -225,6 +225,7 @@ render_osd(struct server *server, cairo_t *cairo, int w, int h, /* Set up text rendering */ set_cairo_color(cairo, theme->osd_label_text_color); PangoLayout *layout = pango_cairo_create_layout(cairo); + pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); PangoFontDescription *desc = font_to_pango_desc(&rc.font_osd); diff --git a/src/workspaces.c b/src/workspaces.c index 0fcef01a..76dc93d6 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -124,6 +124,7 @@ _osd_update(struct server *server) /* Text */ set_cairo_color(cairo, server->theme->osd_label_text_color); PangoLayout *layout = pango_cairo_create_layout(cairo); + pango_context_set_round_glyph_positions(pango_layout_get_context(layout), false); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); /* Center workspace indicator on the x axis */ From c9018da4c4d9539341e4592caa48e94a5e47c6ea Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Mon, 12 Feb 2024 19:52:36 -0500 Subject: [PATCH 059/232] xwayland: set initial geometry in map_request handler Set the initial geometry of maximized/fullscreen views before actually mapping them, so that they can do their initial layout and drawing with the correct geometry. This avoids visual glitches and also avoids undesired layout changes with some apps (e.g. HomeBank). Fixes: #1320 v2: ensure valid geometry for unmanaged->managed case --- include/xwayland.h | 1 + src/xwayland.c | 122 ++++++++++++++++++++++++++++++--------------- 2 files changed, 82 insertions(+), 41 deletions(-) diff --git a/include/xwayland.h b/include/xwayland.h index 99867e3e..1db3c367 100644 --- a/include/xwayland.h +++ b/include/xwayland.h @@ -69,6 +69,7 @@ struct xwayland_view { struct wl_listener set_override_redirect; struct wl_listener set_strut_partial; struct wl_listener set_window_type; + struct wl_listener map_request; /* Not (yet) implemented */ /* struct wl_listener set_role; */ diff --git a/src/xwayland.c b/src/xwayland.c index 0ed4da1f..0c6a6c14 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -340,6 +340,7 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&xwayland_view->set_override_redirect.link); wl_list_remove(&xwayland_view->set_strut_partial.link); wl_list_remove(&xwayland_view->set_window_type.link); + wl_list_remove(&xwayland_view->map_request.link); view_destroy(view); } @@ -555,6 +556,63 @@ handle_set_strut_partial(struct wl_listener *listener, void *data) } } +/* + * Sets the initial geometry of maximized/fullscreen views before + * actually mapping them, so that they can do their initial layout and + * drawing with the correct geometry. This avoids visual glitches and + * also avoids undesired layout changes with some apps (e.g. HomeBank). + */ +static void +handle_map_request(struct wl_listener *listener, void *data) +{ + struct xwayland_view *xwayland_view = + wl_container_of(listener, xwayland_view, map_request); + struct view *view = &xwayland_view->base; + struct wlr_xwayland_surface *xsurface = xwayland_view->xwayland_surface; + + if (view->mapped) { + /* Probably shouldn't happen, but be sure */ + return; + } + + /* Keep the view invisible until actually mapped */ + wlr_scene_node_set_enabled(&view->scene_tree->node, false); + ensure_initial_geometry_and_output(view); + + /* + * Per the Extended Window Manager Hints (EWMH) spec: "The Window + * Manager SHOULD honor _NET_WM_STATE whenever a withdrawn window + * requests to be mapped." + * + * The following order of operations is intended to reduce the + * number of resize (Configure) events: + * 1. set fullscreen state + * 2. set decorations (depends on fullscreen state) + * 3. set maximized (geometry depends on decorations) + */ + view_set_fullscreen(view, xsurface->fullscreen); + if (!view->been_mapped) { + if (want_deco(xsurface)) { + view_set_ssd_mode(view, LAB_SSD_MODE_FULL); + } else { + view_set_ssd_mode(view, LAB_SSD_MODE_NONE); + } + } + enum view_axis axis = VIEW_AXIS_NONE; + if (xsurface->maximized_horz) { + axis |= VIEW_AXIS_HORIZONTAL; + } + if (xsurface->maximized_vert) { + axis |= VIEW_AXIS_VERTICAL; + } + view_maximize(view, axis, /*store_natural_geometry*/ true); + /* + * We could also call set_initial_position() here, but it's not + * really necessary until the view is actually mapped (and at + * that point the output layout is known for sure). + */ +} + static void set_initial_position(struct view *view, struct wlr_xwayland_surface *xwayland_surface) @@ -565,17 +623,7 @@ set_initial_position(struct view *view, XCB_ICCCM_SIZE_HINT_US_POSITION | XCB_ICCCM_SIZE_HINT_P_POSITION)); - if (has_position) { - /* - * Make sure a floating view is onscreen. For a - * maximized/fullscreen view, do nothing; if it is - * unmaximized/leaves fullscreen later, we will make - * sure it is on-screen at that point. - */ - if (view_is_floating(view)) { - view_adjust_for_layout_change(view); - } - } else { + if (!has_position) { view_constrain_size_to_that_of_usable_area(view); if (view_is_floating(view)) { @@ -594,6 +642,13 @@ set_initial_position(struct view *view, &view->natural_geometry.y); } } + + /* + * Always make sure the view is onscreen and adjusted for any + * layout changes that could have occurred between map_request + * and the actual map event. + */ + view_adjust_for_layout_change(view); } static void @@ -615,7 +670,11 @@ init_foreign_toplevel(struct view *view) static void xwayland_view_map(struct view *view) { - struct wlr_xwayland_surface *xwayland_surface = xwayland_surface_from_view(view); + struct xwayland_view *xwayland_view = xwayland_view_from_view(view); + struct wlr_xwayland_surface *xwayland_surface = + xwayland_view->xwayland_surface; + assert(xwayland_surface); + if (view->mapped) { return; } @@ -629,8 +688,16 @@ xwayland_view_map(struct view *view) wlr_log(WLR_DEBUG, "Cannot map view without wlr_surface"); return; } + + /* + * The map_request event may not be received when an unmanaged + * (override-redirect) surface becomes managed. To make sure we + * have valid geometry in that case, call handle_map_request() + * explicitly (calling it twice is harmless). + */ + handle_map_request(&xwayland_view->map_request, NULL); + view->mapped = true; - ensure_initial_geometry_and_output(view); wlr_scene_node_set_enabled(&view->scene_tree->node, true); if (view->surface != xwayland_surface->surface) { @@ -654,34 +721,6 @@ xwayland_view_map(struct view *view) view->scene_node = &tree->node; } - /* - * Per the Extended Window Manager Hints (EWMH) spec: "The Window - * Manager SHOULD honor _NET_WM_STATE whenever a withdrawn window - * requests to be mapped." - * - * The following order of operations is intended to reduce the - * number of resize (Configure) events: - * 1. set fullscreen state - * 2. set decorations (depends on fullscreen state) - * 3. set maximized (geometry depends on decorations) - */ - view_set_fullscreen(view, xwayland_surface->fullscreen); - if (!view->been_mapped) { - if (want_deco(xwayland_surface)) { - view_set_ssd_mode(view, LAB_SSD_MODE_FULL); - } else { - view_set_ssd_mode(view, LAB_SSD_MODE_NONE); - } - } - enum view_axis axis = VIEW_AXIS_NONE; - if (xwayland_surface->maximized_horz) { - axis |= VIEW_AXIS_HORIZONTAL; - } - if (xwayland_surface->maximized_vert) { - axis |= VIEW_AXIS_VERTICAL; - } - view_maximize(view, axis, /*store_natural_geometry*/ true); - /* * Exclude unfocusable views from wlr-foreign-toplevel. These * views (notifications, floating toolbars, etc.) should not be @@ -950,6 +989,7 @@ xwayland_view_create(struct server *server, CONNECT_SIGNAL(xsurface, xwayland_view, set_override_redirect); CONNECT_SIGNAL(xsurface, xwayland_view, set_strut_partial); CONNECT_SIGNAL(xsurface, xwayland_view, set_window_type); + CONNECT_SIGNAL(xsurface, xwayland_view, map_request); wl_list_insert(&view->server->views, &view->link); From ba9de228e06e81b457dfdd52860173317c8f5773 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Wed, 3 Jul 2024 21:14:43 +0200 Subject: [PATCH 060/232] input: add relative cursor move emulation --- include/input/cursor.h | 3 +++ src/input/cursor.c | 29 ++++++++++++++++++----------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/include/input/cursor.h b/include/input/cursor.h index e9ad4fa2..8087c49a 100644 --- a/include/input/cursor.h +++ b/include/input/cursor.h @@ -146,6 +146,9 @@ bool cursor_finish_button_release(struct seat *seat); void cursor_init(struct seat *seat); void cursor_reload(struct seat *seat); +void cursor_emulate_move(struct seat *seat, + struct wlr_input_device *device, + double dx, double dy, uint32_t time_msec); void cursor_emulate_move_absolute(struct seat *seat, struct wlr_input_device *device, double x, double y, uint32_t time_msec); diff --git a/src/input/cursor.c b/src/input/cursor.c index 6bda5a30..0a705068 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -1174,18 +1174,9 @@ cursor_button(struct wl_listener *listener, void *data) } void -cursor_emulate_move_absolute(struct seat *seat, struct wlr_input_device *device, - double x, double y, uint32_t time_msec) +cursor_emulate_move(struct seat *seat, struct wlr_input_device *device, + double dx, double dy, uint32_t time_msec) { - idle_manager_notify_activity(seat->seat); - - double lx, ly; - wlr_cursor_absolute_to_layout_coords(seat->cursor, - device, x, y, &lx, &ly); - - double dx = lx - seat->cursor->x; - double dy = ly - seat->cursor->y; - if (!dx && !dy) { wlr_log(WLR_DEBUG, "dropping useless cursor_emulate: %.10f,%.10f", dx, dy); return; @@ -1205,6 +1196,22 @@ cursor_emulate_move_absolute(struct seat *seat, struct wlr_input_device *device, wlr_seat_pointer_notify_frame(seat->seat); } +void +cursor_emulate_move_absolute(struct seat *seat, struct wlr_input_device *device, + double x, double y, uint32_t time_msec) +{ + idle_manager_notify_activity(seat->seat); + + double lx, ly; + wlr_cursor_absolute_to_layout_coords(seat->cursor, + device, x, y, &lx, &ly); + + double dx = lx - seat->cursor->x; + double dy = ly - seat->cursor->y; + + cursor_emulate_move(seat, device, dx, dy, time_msec); +} + void cursor_emulate_button(struct seat *seat, uint32_t button, enum wl_pointer_button_state state, uint32_t time_msec) From b663c9ea46a8c07ba0cf5b7fcf62c15f5af9529a Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 5 Jul 2024 06:05:35 +0200 Subject: [PATCH 061/232] input: fix rotation for tilt The transformation for relative coordinates is different than for absolute coordinates. --- src/input/tablet.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/input/tablet.c b/src/input/tablet.c index d75eea0e..3475fa6e 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -83,6 +83,30 @@ adjust_for_rotation(enum rotation rotation, double *x, double *y) } } +static void +adjust_for_rotation_relative(enum rotation rotation, double *dx, double *dy) +{ + double tmp; + switch (rotation) { + case LAB_ROTATE_NONE: + break; + case LAB_ROTATE_90: + tmp = *dx; + *dx = -*dy; + *dy = tmp; + break; + case LAB_ROTATE_180: + *dx = -*dx; + *dy = -*dy; + break; + case LAB_ROTATE_270: + tmp = *dx; + *dx = *dy; + *dy = -tmp; + break; + } +} + static struct wlr_surface* tablet_get_coords(struct drawing_tablet *tablet, double *x, double *y) { @@ -228,6 +252,9 @@ handle_axis(struct wl_listener *listener, void *data) return; } + tablet->tilt_x = 0; + tablet->tilt_y = 0; + if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_X) { tablet->x = ev->x; } @@ -299,7 +326,7 @@ handle_axis(struct wl_listener *listener, void *data) */ double tilt_x = tablet->tilt_x; double tilt_y = tablet->tilt_y; - adjust_for_rotation(rc.tablet.rotation, &tilt_x, &tilt_y); + adjust_for_rotation_relative(rc.tablet.rotation, &tilt_x, &tilt_y); wlr_tablet_v2_tablet_tool_notify_tilt(tool->tool_v2, tilt_x, tilt_y); From 8501a1281cf7ede32ca98062f790b87fd10efcc0 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 5 Jul 2024 06:09:37 +0200 Subject: [PATCH 062/232] input: support tablet tools with relative motion --- include/input/tablet.h | 8 ++- src/input/tablet.c | 118 +++++++++++++++++++++++++++++------------ 2 files changed, 91 insertions(+), 35 deletions(-) diff --git a/include/input/tablet.h b/include/input/tablet.h index e7ed395a..1603598e 100644 --- a/include/input/tablet.h +++ b/include/input/tablet.h @@ -9,12 +9,18 @@ struct seat; struct wlr_device; struct wlr_input_device; +enum lab_tablet_motion_mode { + LAB_TABLET_MOTION_ABSOLUTE = 0, + LAB_TABLET_MOTION_RELATIVE, +}; + struct drawing_tablet { struct wlr_input_device *wlr_input_device; struct seat *seat; struct wlr_tablet *tablet; struct wlr_tablet_v2_tablet *tablet_v2; - double x, y; + enum lab_tablet_motion_mode motion_mode; + double x, y, dx, dy; double distance; double pressure; double tilt_x, tilt_y; diff --git a/src/input/tablet.c b/src/input/tablet.c index 3475fa6e..e5335334 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -19,15 +19,19 @@ #include "idle.h" #include "action.h" -static bool -tool_supports_absolute_motion(struct wlr_tablet_tool *tool) +static enum lab_tablet_motion_mode +tool_motion_mode(struct wlr_tablet_tool *tool) { + /* + * Absolute positioning doesn't make sense + * for tablet mouses and lenses. + */ switch (tool->type) { case WLR_TABLET_TOOL_TYPE_MOUSE: case WLR_TABLET_TOOL_TYPE_LENS: - return false; + return LAB_TABLET_MOTION_RELATIVE; default: - return true; + return LAB_TABLET_MOTION_ABSOLUTE; } } @@ -108,29 +112,47 @@ adjust_for_rotation_relative(enum rotation rotation, double *dx, double *dy) } static struct wlr_surface* -tablet_get_coords(struct drawing_tablet *tablet, double *x, double *y) +tablet_get_coords(struct drawing_tablet *tablet, double *x, double *y, double *dx, double *dy) { *x = tablet->x; *y = tablet->y; + *dx = tablet->dx; + *dy = tablet->dy; adjust_for_tablet_area(tablet->tablet->width_mm, tablet->tablet->height_mm, rc.tablet.box, x, y); adjust_for_rotation(rc.tablet.rotation, x, y); + adjust_for_rotation_relative(rc.tablet.rotation, dx, dy); if (rc.tablet.force_mouse_emulation || !tablet->tablet_v2) { return NULL; } - /* Convert coordinates: first [0, 1] => layout, then layout => surface */ - double lx, ly; - wlr_cursor_absolute_to_layout_coords(tablet->seat->cursor, - tablet->wlr_input_device, *x, *y, &lx, &ly); + /* convert coordinates: first [0, 1] => layout, then layout => surface */ + + /* initialize here to avoid a maybe-uninitialized compiler warning */ + double lx = -1, ly = -1; + switch (tablet->motion_mode) { + case LAB_TABLET_MOTION_ABSOLUTE: + wlr_cursor_absolute_to_layout_coords(tablet->seat->cursor, + tablet->wlr_input_device, *x, *y, &lx, &ly); + break; + case LAB_TABLET_MOTION_RELATIVE: + /* + * Deltas dx,dy will be directly passed into wlr_cursor_move, + * so we can add those directly here to determine our future + * position. + */ + lx = tablet->seat->cursor->x + *dx; + ly = tablet->seat->cursor->y + *dy; + break; + } double sx, sy; struct wlr_scene_node *node = wlr_scene_node_at(&tablet->seat->server->scene->tree.node, lx, ly, &sx, &sy); - /* Find the surface and return it if it accepts tablet events */ + /* find the surface and return it if it accepts tablet events */ struct wlr_surface *surface = lab_wlr_surface_from_node(node); if (surface && !wlr_surface_accepts_tablet_v2(tablet->tablet_v2, surface)) { @@ -141,7 +163,8 @@ tablet_get_coords(struct drawing_tablet *tablet, double *x, double *y) static void notify_motion(struct drawing_tablet *tablet, struct drawing_tablet_tool *tool, - struct wlr_surface *surface, double x, double y, uint32_t time) + struct wlr_surface *surface, double x, double y, double dx, double dy, + uint32_t time) { idle_manager_notify_activity(tool->seat->seat); @@ -153,8 +176,16 @@ notify_motion(struct drawing_tablet *tablet, struct drawing_tablet_tool *tool, tablet->tablet_v2, surface); } - wlr_cursor_warp_absolute(tablet->seat->cursor, - tablet->wlr_input_device, x, y); + switch (tablet->motion_mode) { + case LAB_TABLET_MOTION_ABSOLUTE: + wlr_cursor_warp_absolute(tablet->seat->cursor, + tablet->wlr_input_device, x, y); + break; + case LAB_TABLET_MOTION_RELATIVE: + wlr_cursor_move(tablet->seat->cursor, + tablet->wlr_input_device, dx, dy); + break; + } double sx, sy; bool notify = cursor_process_motion(tablet->seat->server, time, &sx, &sy); @@ -203,18 +234,22 @@ handle_proximity(struct wl_listener *listener, void *data) struct drawing_tablet *tablet = ev->tablet->data; struct drawing_tablet_tool *tool = ev->tool->data; - if (!tool_supports_absolute_motion(ev->tool)) { - if (ev->state == WLR_TABLET_TOOL_PROXIMITY_IN) { - wlr_log(WLR_INFO, "ignoring not supporting tablet tool"); - } - return; + if (ev->state == WLR_TABLET_TOOL_PROXIMITY_IN) { + tablet->motion_mode = tool_motion_mode(ev->tool); } + /* + * Reset relative coordinates, we don't want to move the + * cursor on proximity-in for relative positioning. + */ + tablet->dx = 0; + tablet->dy = 0; + tablet->x = ev->x; tablet->y = ev->y; - double x, y; - struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y); + double x, y, dx, dy; + struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y, &dx, &dy); if (!rc.tablet.force_mouse_emulation && tablet->seat->server->tablet_manager && !tool) { @@ -231,7 +266,7 @@ handle_proximity(struct wl_listener *listener, void *data) */ if (tool && surface) { if (tool->tool_v2 && ev->state == WLR_TABLET_TOOL_PROXIMITY_IN) { - notify_motion(tablet, tool, surface, x, y, ev->time_msec); + notify_motion(tablet, tool, surface, x, y, dx, dy, ev->time_msec); } if (tool->tool_v2 && ev->state == WLR_TABLET_TOOL_PROXIMITY_OUT) { wlr_tablet_v2_tablet_tool_notify_proximity_out(tool->tool_v2); @@ -248,18 +283,22 @@ handle_axis(struct wl_listener *listener, void *data) struct drawing_tablet *tablet = ev->tablet->data; struct drawing_tablet_tool *tool = ev->tool->data; - if (!tool_supports_absolute_motion(ev->tool)) { - return; - } - + /* + * Reset relative coordinates. If those axes aren't updated, + * the delta is zero. + */ + tablet->dx = 0; + tablet->dy = 0; tablet->tilt_x = 0; tablet->tilt_y = 0; if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_X) { tablet->x = ev->x; + tablet->dx = ev->dx; } if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_Y) { tablet->y = ev->y; + tablet->dy = ev->dy; } if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE) { tablet->distance = ev->distance; @@ -283,8 +322,8 @@ handle_axis(struct wl_listener *listener, void *data) tablet->wheel_delta = ev->wheel_delta; } - double x, y; - struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y); + double x, y, dx, dy; + struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y, &dx, &dy); /* * We are sending tablet notifications on the following conditions: @@ -301,7 +340,7 @@ handle_axis(struct wl_listener *listener, void *data) && tablet->seat->server->input_mode == LAB_INPUT_STATE_PASSTHROUGH) || wlr_tablet_tool_v2_has_implicit_grab(tool->tool_v2))) { /* motion seems to be supported by all tools */ - notify_motion(tablet, tool, surface, x, y, ev->time_msec); + notify_motion(tablet, tool, surface, x, y, dx, dy, ev->time_msec); /* notify about other axis based on tool capabilities */ if (ev->tool->distance) { @@ -349,8 +388,19 @@ handle_axis(struct wl_listener *listener, void *data) wlr_tablet_v2_tablet_tool_notify_proximity_out( tool->tool_v2); } - cursor_emulate_move_absolute(tablet->seat, &ev->tablet->base, - x, y, ev->time_msec); + + switch (tablet->motion_mode) { + case LAB_TABLET_MOTION_ABSOLUTE: + cursor_emulate_move_absolute(tablet->seat, + &ev->tablet->base, + x, y, ev->time_msec); + break; + case LAB_TABLET_MOTION_RELATIVE: + cursor_emulate_move(tablet->seat, + &ev->tablet->base, + dx, dy, ev->time_msec); + break; + } } } } @@ -407,8 +457,8 @@ handle_tip(struct wl_listener *listener, void *data) struct drawing_tablet *tablet = ev->tablet->data; struct drawing_tablet_tool *tool = ev->tool->data; - double x, y; - struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y); + double x, y, dx, dy; + struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y, &dx, &dy); uint32_t button = tablet_get_mapped_button(BTN_TOOL_PEN); @@ -482,8 +532,8 @@ handle_button(struct wl_listener *listener, void *data) struct drawing_tablet *tablet = ev->tablet->data; struct drawing_tablet_tool *tool = ev->tool->data; - double x, y; - struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y); + double x, y, dx, dy; + struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y, &dx, &dy); uint32_t button = tablet_get_mapped_button(ev->button); From a2f25bea1a799f60ca6226383321dfdeac7bc2b0 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 5 Jul 2024 06:17:29 +0200 Subject: [PATCH 063/232] config: add configuration for tablet tool motion --- include/config/rcxml.h | 4 ++++ include/config/tablet-tool.h | 14 ++++++++++++++ include/input/tablet.h | 7 +------ src/config/meson.build | 1 + src/config/rcxml.c | 3 +++ src/config/tablet-tool.c | 17 +++++++++++++++++ src/input/tablet.c | 2 +- 7 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 include/config/tablet-tool.h create mode 100644 src/config/tablet-tool.c diff --git a/include/config/rcxml.h b/include/config/rcxml.h index a9e89589..a6172d83 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -11,6 +11,7 @@ #include "common/font.h" #include "config/touch.h" #include "config/tablet.h" +#include "config/tablet-tool.h" #include "config/libinput.h" #include "resize-indicator.h" #include "theme.h" @@ -102,6 +103,9 @@ struct rcxml { uint16_t button_map_count; struct button_map_entry button_map[BUTTON_MAP_MAX]; } tablet; + struct tablet_tool_config { + enum motion motion; + } tablet_tool; /* libinput */ struct wl_list libinput_categories; diff --git a/include/config/tablet-tool.h b/include/config/tablet-tool.h new file mode 100644 index 00000000..bd1943af --- /dev/null +++ b/include/config/tablet-tool.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_TABLET_TOOL_CONFIG_H +#define LABWC_TABLET_TOOL_CONFIG_H + +#include + +enum motion { + LAB_TABLET_MOTION_ABSOLUTE = 0, + LAB_TABLET_MOTION_RELATIVE, +}; + +enum motion tablet_parse_motion(const char *name); + +#endif /* LABWC_TABLET_TOOL_CONFIG_H */ diff --git a/include/input/tablet.h b/include/input/tablet.h index 1603598e..93fdda5a 100644 --- a/include/input/tablet.h +++ b/include/input/tablet.h @@ -9,17 +9,12 @@ struct seat; struct wlr_device; struct wlr_input_device; -enum lab_tablet_motion_mode { - LAB_TABLET_MOTION_ABSOLUTE = 0, - LAB_TABLET_MOTION_RELATIVE, -}; - struct drawing_tablet { struct wlr_input_device *wlr_input_device; struct seat *seat; struct wlr_tablet *tablet; struct wlr_tablet_v2_tablet *tablet_v2; - enum lab_tablet_motion_mode motion_mode; + enum motion motion_mode; double x, y, dx, dy; double distance; double pressure; diff --git a/src/config/meson.build b/src/config/meson.build index de08f6d0..70b1baa3 100644 --- a/src/config/meson.build +++ b/src/config/meson.build @@ -5,5 +5,6 @@ labwc_sources += files( 'mousebind.c', 'touch.c', 'tablet.c', + 'tablet-tool.c', 'libinput.c', ) diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 84c117b6..fac2cbf1 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1061,6 +1061,8 @@ entry(xmlNode *node, char *nodename, char *content) } else { wlr_log(WLR_ERROR, "Missing 'button' argument for tablet button mapping"); } + } else if (!strcasecmp(nodename, "motion.tabletTool")) { + rc.tablet_tool.motion = tablet_parse_motion(content); } else if (!strcasecmp(nodename, "ignoreButtonReleasePeriod.menu")) { rc.menu_ignore_button_release_period = atoi(content); } else if (!strcasecmp(nodename, "width.magnifier")) { @@ -1258,6 +1260,7 @@ rcxml_init(void) rc.tablet.rotation = 0; rc.tablet.box = (struct wlr_fbox){0}; tablet_load_default_button_mappings(); + rc.tablet_tool.motion = LAB_TABLET_MOTION_ABSOLUTE; rc.repeat_rate = 25; rc.repeat_delay = 600; diff --git a/src/config/tablet-tool.c b/src/config/tablet-tool.c new file mode 100644 index 00000000..7e171410 --- /dev/null +++ b/src/config/tablet-tool.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _POSIX_C_SOURCE 200809L +#include +#include +#include "config/tablet-tool.h" + +enum motion +tablet_parse_motion(const char *name) +{ + if (!strcasecmp(name, "Absolute")) { + return LAB_TABLET_MOTION_ABSOLUTE; + } else if (!strcasecmp(name, "Relative")) { + return LAB_TABLET_MOTION_RELATIVE; + } + wlr_log(WLR_ERROR, "Invalid value for tablet motion: %s", name); + return LAB_TABLET_MOTION_ABSOLUTE; +} diff --git a/src/input/tablet.c b/src/input/tablet.c index e5335334..b417ea95 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -19,7 +19,7 @@ #include "idle.h" #include "action.h" -static enum lab_tablet_motion_mode +static enum motion tool_motion_mode(struct wlr_tablet_tool *tool) { /* From 96701f1e27f071fc2533591f3190623019d350d5 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 5 Jul 2024 06:17:39 +0200 Subject: [PATCH 064/232] input: support tablet tool motion configuration --- src/input/tablet.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/input/tablet.c b/src/input/tablet.c index b417ea95..75f9fd8c 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -20,7 +20,7 @@ #include "action.h" static enum motion -tool_motion_mode(struct wlr_tablet_tool *tool) +tool_motion_mode(enum motion motion, struct wlr_tablet_tool *tool) { /* * Absolute positioning doesn't make sense @@ -31,7 +31,7 @@ tool_motion_mode(struct wlr_tablet_tool *tool) case WLR_TABLET_TOOL_TYPE_LENS: return LAB_TABLET_MOTION_RELATIVE; default: - return LAB_TABLET_MOTION_ABSOLUTE; + return motion; } } @@ -235,7 +235,8 @@ handle_proximity(struct wl_listener *listener, void *data) struct drawing_tablet_tool *tool = ev->tool->data; if (ev->state == WLR_TABLET_TOOL_PROXIMITY_IN) { - tablet->motion_mode = tool_motion_mode(ev->tool); + tablet->motion_mode = + tool_motion_mode(rc.tablet_tool.motion, ev->tool); } /* From bcd5746a323b78490738e03bd25a7ee24638cd47 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 5 Jul 2024 06:32:28 +0200 Subject: [PATCH 065/232] config: add configuration for tablet tool motion sensitivity --- include/config/rcxml.h | 1 + src/config/rcxml.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/include/config/rcxml.h b/include/config/rcxml.h index a6172d83..1a2bb203 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -105,6 +105,7 @@ struct rcxml { } tablet; struct tablet_tool_config { enum motion motion; + double relative_motion_sensitivity; } tablet_tool; /* libinput */ diff --git a/src/config/rcxml.c b/src/config/rcxml.c index fac2cbf1..9ad0268a 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1063,6 +1063,9 @@ entry(xmlNode *node, char *nodename, char *content) } } else if (!strcasecmp(nodename, "motion.tabletTool")) { rc.tablet_tool.motion = tablet_parse_motion(content); + } else if (!strcasecmp(nodename, "relativeMotionSensitivity.tabletTool")) { + rc.tablet_tool.relative_motion_sensitivity = + tablet_get_dbl_if_positive(content, "relativeMotionSensitivity"); } else if (!strcasecmp(nodename, "ignoreButtonReleasePeriod.menu")) { rc.menu_ignore_button_release_period = atoi(content); } else if (!strcasecmp(nodename, "width.magnifier")) { @@ -1261,6 +1264,7 @@ rcxml_init(void) rc.tablet.box = (struct wlr_fbox){0}; tablet_load_default_button_mappings(); rc.tablet_tool.motion = LAB_TABLET_MOTION_ABSOLUTE; + rc.tablet_tool.relative_motion_sensitivity = 1.0; rc.repeat_rate = 25; rc.repeat_delay = 600; From f4d203f0cd2c40c895c31fbcd322eb1300839562 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 5 Jul 2024 06:33:26 +0200 Subject: [PATCH 066/232] input: support tablet tool motion sensitivity configuration --- src/input/tablet.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/input/tablet.c b/src/input/tablet.c index 75f9fd8c..743fa72e 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -111,6 +111,13 @@ adjust_for_rotation_relative(enum rotation rotation, double *dx, double *dy) } } +static void +adjust_for_motion_sensitivity(double motion_sensitivity, double *dx, double *dy) +{ + *dx = *dx * motion_sensitivity; + *dy = *dy * motion_sensitivity; +} + static struct wlr_surface* tablet_get_coords(struct drawing_tablet *tablet, double *x, double *y, double *dx, double *dy) { @@ -122,6 +129,7 @@ tablet_get_coords(struct drawing_tablet *tablet, double *x, double *y, double *d rc.tablet.box, x, y); adjust_for_rotation(rc.tablet.rotation, x, y); adjust_for_rotation_relative(rc.tablet.rotation, dx, dy); + adjust_for_motion_sensitivity(rc.tablet_tool.relative_motion_sensitivity, dx, dy); if (rc.tablet.force_mouse_emulation || !tablet->tablet_v2) { From 3f210828d7cafa0701b81e64c67eb27ec93cfe2b Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Mon, 8 Jul 2024 18:15:25 +0200 Subject: [PATCH 067/232] input: log tablet pad capabilities --- src/input/tablet-pad.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/input/tablet-pad.c b/src/input/tablet-pad.c index 0b1fa9b4..c5381ef1 100644 --- a/src/input/tablet-pad.c +++ b/src/input/tablet-pad.c @@ -187,6 +187,10 @@ tablet_pad_init(struct seat *seat, struct wlr_input_device *wlr_device) seat->server->tablet_manager, seat->seat, wlr_device); } pad->pad->data = pad; + wlr_log(WLR_INFO, "tablet pad capabilities: %zu button(s) %zu strip(s) %zu ring(s)", + pad->pad->button_count, + pad->pad->strip_count, + pad->pad->ring_count); CONNECT_SIGNAL(pad->pad, &pad->handlers, button); CONNECT_SIGNAL(pad->pad, &pad->handlers, ring); CONNECT_SIGNAL(pad->pad, &pad->handlers, strip); From e26ec472b22b26afc2c1ec563208592c58fa8449 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Mon, 8 Jul 2024 20:21:49 +0200 Subject: [PATCH 068/232] docs: add tablet tool section --- docs/labwc-config.5.scd | 18 ++++++++++++++++++ docs/rc.xml.all | 10 ++++++++++ 2 files changed, 28 insertions(+) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 1a5e3742..70f705c6 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -730,6 +730,24 @@ extending outward from the snapped edge. See mouse section above for all supported mouse buttons. +## TABLET TOOL + +``` + +``` + +** [absolute|relative] + All tablet tools, except of type mouse and lens, use "absolute" + positioning by default. The *motion* attribute allows to set tools + to relative motion instead. Positioning for a tablet mouse or + tablet lens cannot be changed, those tools always use relative mode. + +** + When using relative motion, *relativeMotionSensitivity* controls + the speed of the cursor. Using a value lower than 1.0 decreases the + speed, using a value greater than 1.0 increases the speed of the + cursor. The default is "1.0". + ## LIBINPUT ``` diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 1f4a0f69..f481c091 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -521,6 +521,16 @@ + + + diff --git a/include/common/box.h b/include/common/box.h new file mode 100644 index 00000000..105ff297 --- /dev/null +++ b/include/common/box.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_BOX_H +#define LABWC_BOX_H + +#include + +bool +box_contains(struct wlr_box *box_super, struct wlr_box *box_sub); + +bool +box_intersects(struct wlr_box *box_a, struct wlr_box *box_b); + +/* Returns the bounding box of 2 boxes */ +void +box_union(struct wlr_box *box_dest, struct wlr_box *box_a, struct wlr_box *box_b); + +#endif /* LABWC_BOX_H */ diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 1a2bb203..53263df7 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -20,7 +20,8 @@ enum view_placement_policy { LAB_PLACE_INVALID = 0, LAB_PLACE_CENTER, LAB_PLACE_CURSOR, - LAB_PLACE_AUTOMATIC + LAB_PLACE_AUTOMATIC, + LAB_PLACE_CASCADE, }; enum adaptive_sync_mode { @@ -57,6 +58,8 @@ struct rcxml { bool reuse_output_mode; enum view_placement_policy placement_policy; bool xwayland_persistence; + int placement_cascade_offset_x; + int placement_cascade_offset_y; /* focus */ bool focus_follow_mouse; diff --git a/src/common/box.c b/src/common/box.c new file mode 100644 index 00000000..1a56cfe2 --- /dev/null +++ b/src/common/box.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include "common/box.h" +#include "common/macros.h" + +bool +box_contains(struct wlr_box *box_super, struct wlr_box *box_sub) +{ + if (wlr_box_empty(box_super) || wlr_box_empty(box_sub)) { + return false; + } + return box_super->x <= box_sub->x + && box_super->x + box_super->width >= box_sub->x + box_sub->width + && box_super->y <= box_sub->y + && box_super->y + box_super->height >= box_sub->y + box_sub->height; +} + +bool +box_intersects(struct wlr_box *box_a, struct wlr_box *box_b) +{ + if (wlr_box_empty(box_a) || wlr_box_empty(box_b)) { + return false; + } + return box_a->x < box_b->x + box_b->width + && box_b->x < box_a->x + box_a->width + && box_a->y < box_b->y + box_b->height + && box_b->y < box_a->y + box_a->height; +} + +void +box_union(struct wlr_box *box_dest, struct wlr_box *box_a, struct wlr_box *box_b) +{ + if (wlr_box_empty(box_a)) { + *box_dest = *box_b; + return; + } + if (wlr_box_empty(box_b)) { + *box_dest = *box_a; + return; + } + int x1 = MIN(box_a->x, box_b->x); + int y1 = MIN(box_a->y, box_b->y); + int x2 = MAX(box_a->x + box_a->width, box_b->x + box_b->width); + int y2 = MAX(box_a->y + box_a->height, box_b->y + box_b->height); + box_dest->x = x1; + box_dest->y = y1; + box_dest->width = x2 - x1; + box_dest->height = y2 - y1; +} diff --git a/src/common/meson.build b/src/common/meson.build index 441e92c0..1569f775 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -1,4 +1,5 @@ labwc_sources += files( + 'box.c', 'buf.c', 'dir.c', 'fd-util.c', diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 9ad0268a..28eff198 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -901,6 +901,10 @@ entry(xmlNode *node, char *nodename, char *content) } } else if (!strcasecmp(nodename, "xwaylandPersistence.core")) { set_bool(content, &rc.xwayland_persistence); + } else if (!strcasecmp(nodename, "x.cascadeOffset.placement")) { + rc.placement_cascade_offset_x = atoi(content); + } else if (!strcasecmp(nodename, "y.cascadeOffset.placement")) { + rc.placement_cascade_offset_y = atoi(content); } else if (!strcmp(nodename, "name.theme")) { rc.theme_name = xstrdup(content); } else if (!strcmp(nodename, "cornerradius.theme")) { @@ -1234,6 +1238,8 @@ rcxml_init(void) has_run = true; rc.placement_policy = LAB_PLACE_CENTER; + rc.placement_cascade_offset_x = 0; + rc.placement_cascade_offset_y = 0; rc.xdg_shell_server_side_deco = true; rc.ssd_keep_border = true; diff --git a/src/edges.c b/src/edges.c index bd96c5a0..c9c88184 100644 --- a/src/edges.c +++ b/src/edges.c @@ -5,6 +5,7 @@ #include #include #include "common/border.h" +#include "common/box.h" #include "common/macros.h" #include "config/rcxml.h" #include "edges.h" @@ -466,9 +467,8 @@ edges_find_outputs(struct border *nearest_edges, struct view *view, struct wlr_box usable = output_usable_area_in_layout_coords(o); - struct wlr_box ol; - if (!wlr_box_intersection(&ol, &origin, &usable) && - !wlr_box_intersection(&ol, &target, &usable)) { + if (!box_intersects(&origin, &usable) + && !box_intersects(&target, &usable)) { continue; } diff --git a/src/view.c b/src/view.c index 6a0f9806..d06251aa 100644 --- a/src/view.c +++ b/src/view.c @@ -4,6 +4,7 @@ #include #include #include +#include "common/box.h" #include "common/macros.h" #include "common/match.h" #include "common/mem.h" @@ -873,6 +874,94 @@ view_center(struct view *view, const struct wlr_box *ref) } } +/* + * Algorithm based on KWin's implementation: + * https://github.com/KDE/kwin/blob/df9f8f8346b5b7645578e37365dabb1a7b02ca5a/src/placement.cpp#L589 + */ +static void +view_cascade(struct view *view) +{ + /* "cascade" policy places a new view at center by default */ + struct wlr_box center = view->pending; + view_compute_centered_position(view, NULL, + center.width, center.height, ¢er.x, ¢er.y); + struct border margin = ssd_get_margin(view->ssd); + center.x -= margin.left; + center.y -= margin.top; + center.width += margin.left + margin.right; + center.height += margin.top + margin.bottom; + + /* Candidate geometry to which the view is moved */ + struct wlr_box candidate = center; + + struct wlr_box usable = output_usable_area_in_layout_coords(view->output); + + /* TODO: move this logic to rcxml.c */ + int offset_x = rc.placement_cascade_offset_x; + int offset_y = rc.placement_cascade_offset_y; + struct theme *theme = view->server->theme; + int default_offset = theme->title_height + theme->border_width + 5; + if (offset_x <= 0) { + offset_x = default_offset; + } + if (offset_y <= 0) { + offset_y = default_offset; + } + + /* + * Keep updating the candidate until it doesn't cover any existing views + * or doesn't fit within the usable area. + */ + bool candidate_updated = true; + while (candidate_updated) { + candidate_updated = false; + struct wlr_box covered = {0}; + + /* Iterate over views from top to bottom */ + struct view *other_view; + for_each_view(other_view, &view->server->views, + LAB_VIEW_CRITERIA_CURRENT_WORKSPACE) { + struct wlr_box other = ssd_max_extents(other_view); + if (other_view == view + || view->minimized + || !box_intersects(&candidate, &other)) { + continue; + } + /* + * If the candidate covers an existing view whose + * top-left corner is not covered by other views, + * shift the candidate to bottom-right. + */ + if (box_contains(&candidate, &other) + && !wlr_box_contains_point( + &covered, other.x, other.y)) { + candidate.x = other.x + offset_x; + candidate.y = other.y + offset_y; + if (!box_contains(&usable, &candidate)) { + /* + * If the candidate doesn't fit within + * the usable area, fall back to center + * and finish updating the candidate. + */ + candidate = center; + break; + } else { + /* Repeat with the new candidate */ + candidate_updated = true; + break; + } + } + /* + * We use just a bounding box to represent the covered + * area, which would be fine for our use-case. + */ + box_union(&covered, &covered, &other); + } + } + + view_move(view, candidate.x + margin.left, candidate.y + margin.top); +} + void view_place_by_policy(struct view *view, bool allow_cursor, enum view_placement_policy policy) @@ -886,6 +975,9 @@ view_place_by_policy(struct view *view, bool allow_cursor, view_move(view, geometry.x, geometry.y); return; } + } else if (policy == LAB_PLACE_CASCADE) { + view_cascade(view); + return; } view_center(view, NULL); @@ -1075,14 +1167,11 @@ view_apply_maximized_geometry(struct view *view) * center the unmaximized axis. */ struct wlr_box natural = view->natural_geometry; - if (view->maximized != VIEW_AXIS_BOTH) { - struct wlr_box intersect; - wlr_box_intersection(&intersect, &box, &natural); - if (wlr_box_empty(&intersect)) { - view_compute_centered_position(view, NULL, - natural.width, natural.height, - &natural.x, &natural.y); - } + if (view->maximized != VIEW_AXIS_BOTH + && !box_intersects(&box, &natural)) { + view_compute_centered_position(view, NULL, + natural.width, natural.height, + &natural.x, &natural.y); } if (view->ssd_enabled) { @@ -2004,6 +2093,8 @@ view_placement_parse(const char *policy) return LAB_PLACE_CURSOR; } else if (!strcasecmp(policy, "center")) { return LAB_PLACE_CENTER; + } else if (!strcasecmp(policy, "cascade")) { + return LAB_PLACE_CASCADE; } return LAB_PLACE_INVALID; From e4afa10fe42ad0716f75ec47f4b3051f65cf59f0 Mon Sep 17 00:00:00 2001 From: Consus Date: Sat, 20 Jul 2024 11:33:57 +0300 Subject: [PATCH 071/232] theme: allow to set window button size (#1965) This commit introduces new option "window.button.width". Despite the name it currently affects both width and height. --- docs/labwc-theme.5.scd | 4 ++++ docs/themerc | 3 +++ include/ssd.h | 1 - include/theme.h | 3 +++ include/view.h | 1 - src/snap.c | 5 +++-- src/ssd/ssd-border.c | 8 ++++---- src/ssd/ssd-extents.c | 6 ++++-- src/ssd/ssd-part.c | 11 ++++++----- src/ssd/ssd-titlebar.c | 26 +++++++++++++------------- src/ssd/ssd.c | 3 ++- src/theme.c | 15 +++++++++++++-- src/view.c | 3 ++- src/xwayland.c | 3 ++- 14 files changed, 59 insertions(+), 33 deletions(-) diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index 72599523..5b90a962 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -114,6 +114,10 @@ labwc-config(5). Specifies how window titles are aligned in the titlebar for both focused and unfocused windows. Type justification. Default Left. +*window.button.width* + Width of a titlebar button, in pixels. + Default is 26. + *window.active.button.unpressed.image.color* Color of the images in titlebar buttons in their default, unpressed, state. This element is for the focused window. diff --git a/docs/themerc b/docs/themerc index 7602da9d..b2dcb2fe 100644 --- a/docs/themerc +++ b/docs/themerc @@ -29,6 +29,9 @@ window.active.label.text.color: #000000 window.inactive.label.text.color: #000000 window.label.text.justify: center +# window button width +window.button.width: 26 + # window buttons window.active.button.unpressed.image.color: #000000 window.inactive.button.unpressed.image.color: #000000 diff --git a/include/ssd.h b/include/ssd.h index a0c520c6..19b22450 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -6,7 +6,6 @@ #include "common/border.h" #define SSD_BUTTON_COUNT 4 -#define SSD_BUTTON_WIDTH 26 #define SSD_EXTENDED_AREA 8 /* diff --git a/include/theme.h b/include/theme.h index 50a69f6f..39931840 100644 --- a/include/theme.h +++ b/include/theme.h @@ -45,6 +45,9 @@ struct theme { float window_inactive_label_text_color[4]; enum lab_justification window_label_text_justify; + /* button width */ + int window_button_width; + /* button colors */ float window_active_button_menu_unpressed_image_color[4]; float window_active_button_iconify_unpressed_image_color[4]; diff --git a/include/view.h b/include/view.h index 637b00df..9dc70aa8 100644 --- a/include/view.h +++ b/include/view.h @@ -10,7 +10,6 @@ #include #include -#define LAB_MIN_VIEW_WIDTH (SSD_BUTTON_WIDTH * SSD_BUTTON_COUNT) #define LAB_MIN_VIEW_HEIGHT 60 /* diff --git a/src/snap.c b/src/snap.c index ca5186cf..5f1dbff8 100644 --- a/src/snap.c +++ b/src/snap.c @@ -220,6 +220,7 @@ snap_shrink_to_next_edge(struct view *view, *geo = view->pending; uint32_t resize_edges; + int min_view_width = rc.theme->window_button_width * SSD_BUTTON_COUNT; /* * First shrink the view along the relevant edge. The maximum shrink @@ -228,12 +229,12 @@ snap_shrink_to_next_edge(struct view *view, */ switch (direction) { case VIEW_EDGE_RIGHT: - geo->width = MAX(geo->width / 2, LAB_MIN_VIEW_WIDTH); + geo->width = MAX(geo->width / 2, min_view_width); geo->x = view->pending.x + view->pending.width - geo->width; resize_edges = WLR_EDGE_LEFT; break; case VIEW_EDGE_LEFT: - geo->width = MAX(geo->width / 2, LAB_MIN_VIEW_WIDTH); + geo->width = MAX(geo->width / 2, min_view_width); resize_edges = WLR_EDGE_RIGHT; break; case VIEW_EDGE_DOWN: diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index 06ce55c3..fb1ac283 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -48,8 +48,8 @@ ssd_border_create(struct ssd *ssd) add_scene_rect(&subtree->parts, LAB_SSD_PART_BOTTOM, parent, full_width, theme->border_width, 0, height, color); add_scene_rect(&subtree->parts, LAB_SSD_PART_TOP, parent, - width - 2 * SSD_BUTTON_WIDTH, theme->border_width, - theme->border_width + SSD_BUTTON_WIDTH, + width - 2 * theme->window_button_width, theme->border_width, + theme->border_width + theme->window_button_width, -(ssd->titlebar.height + theme->border_width), color); } FOR_EACH_END @@ -121,10 +121,10 @@ ssd_border_update(struct ssd *ssd) : 0; int top_width = ssd->titlebar.height <= 0 || ssd->state.was_tiled_not_maximized ? full_width - : width - 2 * SSD_BUTTON_WIDTH; + : width - 2 * theme->window_button_width; int top_x = ssd->titlebar.height <= 0 || ssd->state.was_tiled_not_maximized ? 0 - : theme->border_width + SSD_BUTTON_WIDTH; + : theme->border_width + theme->window_button_width; struct ssd_part *part; struct wlr_scene_rect *rect; diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index 4bb5b69d..274b5f50 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -32,7 +32,8 @@ ssd_extents_create(struct ssd *ssd) struct theme *theme = view->server->theme; struct wl_list *part_list = &ssd->extents.parts; int extended_area = SSD_EXTENDED_AREA; - int corner_size = extended_area + theme->border_width + SSD_BUTTON_WIDTH / 2; + int corner_size = extended_area + theme->border_width + + theme->window_button_width / 2; ssd->extents.tree = wlr_scene_tree_create(ssd->tree); struct wlr_scene_tree *parent = ssd->extents.tree; @@ -109,7 +110,8 @@ ssd_extents_update(struct ssd *ssd) int full_height = height + theme->border_width * 2 + ssd->titlebar.height; int full_width = width + 2 * theme->border_width; int extended_area = SSD_EXTENDED_AREA; - int corner_size = extended_area + theme->border_width + SSD_BUTTON_WIDTH / 2; + int corner_size = extended_area + theme->border_width + + theme->window_button_width / 2; int side_width = full_width + extended_area * 2 - corner_size * 2; int side_height = full_height + extended_area * 2 - corner_size * 2; diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c index 3933cd17..5c903609 100644 --- a/src/ssd/ssd-part.c +++ b/src/ssd/ssd-part.c @@ -153,12 +153,13 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type, /* Background */ struct ssd_part *bg_rect = add_scene_rect(part_list, type, parent, - SSD_BUTTON_WIDTH, rc.theme->title_height, 0, 0, bg_color); + rc.theme->window_button_width, rc.theme->title_height, 0, 0, + bg_color); /* Icon */ struct wlr_scene_tree *icon_tree = wlr_scene_tree_create(parent); struct wlr_box icon_geo = get_scale_box(icon_buffer, - SSD_BUTTON_WIDTH, rc.theme->title_height); + rc.theme->window_button_width, rc.theme->title_height); struct ssd_part *icon_part = add_scene_buffer(part_list, type, icon_tree, icon_buffer, icon_geo.x, icon_geo.y); @@ -171,7 +172,7 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type, struct wlr_scene_tree *hover_tree = wlr_scene_tree_create(parent); wlr_scene_node_set_enabled(&hover_tree->node, false); struct wlr_box hover_geo = get_scale_box(hover_buffer, - SSD_BUTTON_WIDTH, rc.theme->title_height); + rc.theme->window_button_width, rc.theme->title_height); struct ssd_part *hover_part = add_scene_buffer(part_list, type, hover_tree, hover_buffer, hover_geo.x, hover_geo.y); @@ -200,7 +201,7 @@ add_toggled_icon(struct ssd_button *button, struct wl_list *part_list, { /* Alternate icon */ struct wlr_box icon_geo = get_scale_box(icon_buffer, - SSD_BUTTON_WIDTH, rc.theme->title_height); + rc.theme->window_button_width, rc.theme->title_height); struct ssd_part *alticon_part = add_scene_buffer(part_list, type, button->icon_tree, icon_buffer, icon_geo.x, icon_geo.y); @@ -212,7 +213,7 @@ add_toggled_icon(struct ssd_button *button, struct wl_list *part_list, wlr_scene_node_set_enabled(alticon_part->node, false); struct wlr_box hover_geo = get_scale_box(hover_buffer, - SSD_BUTTON_WIDTH, rc.theme->title_height); + rc.theme->window_button_width, rc.theme->title_height); struct ssd_part *althover_part = add_scene_buffer(part_list, type, button->hover_tree, hover_buffer, hover_geo.x, hover_geo.y); diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 32d6131a..70d9ca6b 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -90,21 +90,21 @@ ssd_titlebar_create(struct ssd *ssd) /* Title */ add_scene_rect(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent, - width - SSD_BUTTON_WIDTH * SSD_BUTTON_COUNT, theme->title_height, - SSD_BUTTON_WIDTH, 0, color); + width - theme->window_button_width * SSD_BUTTON_COUNT, + theme->title_height, theme->window_button_width, 0, color); /* Buttons */ add_scene_button_corner(&subtree->parts, LAB_SSD_BUTTON_WINDOW_MENU, LAB_SSD_PART_CORNER_TOP_LEFT, parent, corner_top_left, menu_button_unpressed, menu_button_hover, 0, view); add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent, color, iconify_button_unpressed, iconify_button_hover, - width - SSD_BUTTON_WIDTH * 3, view); + width - theme->window_button_width * 3, view); /* Maximize button has an alternate state when maximized */ struct ssd_part *btn_max_root = add_scene_button( &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, parent, color, maximize_button_unpressed, maximize_button_hover, - width - SSD_BUTTON_WIDTH * 2, view); + width - theme->window_button_width * 2, view); struct ssd_button *btn_max = node_ssd_button_from_node(btn_max_root->node); add_toggled_icon(btn_max, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, restore_button_unpressed, restore_button_hover); @@ -112,7 +112,7 @@ ssd_titlebar_create(struct ssd *ssd) add_scene_button_corner(&subtree->parts, LAB_SSD_BUTTON_CLOSE, LAB_SSD_PART_CORNER_TOP_RIGHT, parent, corner_top_right, close_button_unpressed, close_button_hover, - width - SSD_BUTTON_WIDTH * 1, view); + width - theme->window_button_width * 1, view); } FOR_EACH_END ssd_update_title(ssd); @@ -218,25 +218,25 @@ ssd_titlebar_update(struct ssd *ssd) case LAB_SSD_PART_TITLEBAR: wlr_scene_rect_set_size( wlr_scene_rect_from_node(part->node), - width - SSD_BUTTON_WIDTH * SSD_BUTTON_COUNT, + width - theme->window_button_width * SSD_BUTTON_COUNT, theme->title_height); continue; case LAB_SSD_BUTTON_ICONIFY: if (is_direct_child(part->node, subtree)) { wlr_scene_node_set_position(part->node, - width - SSD_BUTTON_WIDTH * 3, 0); + width - theme->window_button_width * 3, 0); } continue; case LAB_SSD_BUTTON_MAXIMIZE: if (is_direct_child(part->node, subtree)) { wlr_scene_node_set_position(part->node, - width - SSD_BUTTON_WIDTH * 2, 0); + width - theme->window_button_width * 2, 0); } continue; case LAB_SSD_PART_CORNER_TOP_RIGHT: if (is_direct_child(part->node, subtree)) { wlr_scene_node_set_position(part->node, - width - SSD_BUTTON_WIDTH * 1, 0); + width - theme->window_button_width * 1, 0); } continue; default: @@ -288,7 +288,7 @@ ssd_update_title_positions(struct ssd *ssd) struct view *view = ssd->view; struct theme *theme = view->server->theme; int width = view->current.width; - int title_bg_width = width - SSD_BUTTON_WIDTH * SSD_BUTTON_COUNT; + int title_bg_width = width - theme->window_button_width * SSD_BUTTON_COUNT; int x, y; int buffer_height, buffer_width; @@ -304,7 +304,7 @@ ssd_update_title_positions(struct ssd *ssd) buffer_width = part->buffer ? part->buffer->width : 0; buffer_height = part->buffer ? part->buffer->height : 0; - x = SSD_BUTTON_WIDTH; + x = theme->window_button_width; y = (theme->title_height - buffer_height) / 2; if (title_bg_width <= 0) { @@ -314,7 +314,7 @@ ssd_update_title_positions(struct ssd *ssd) wlr_scene_node_set_enabled(part->node, true); if (theme->window_label_text_justify == LAB_JUSTIFY_CENTER) { - if (buffer_width + SSD_BUTTON_WIDTH * 2 <= title_bg_width) { + if (buffer_width + theme->window_button_width * 2 <= title_bg_width) { /* Center based on the full width */ x = (width - buffer_width) / 2; } else { @@ -358,7 +358,7 @@ ssd_update_title(struct ssd *ssd) struct ssd_sub_tree *subtree; struct ssd_state_title_width *dstate; int title_bg_width = view->current.width - - SSD_BUTTON_WIDTH * SSD_BUTTON_COUNT; + - theme->window_button_width * SSD_BUTTON_COUNT; FOR_EACH_STATE(ssd, subtree) { if (subtree == &ssd->titlebar.active) { diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 70b1b0db..6382983d 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -233,10 +233,11 @@ ssd_update_geometry(struct ssd *ssd) struct wlr_box cached = ssd->state.geometry; struct wlr_box current = ssd->view->current; + int min_view_width = rc.theme->window_button_width * SSD_BUTTON_COUNT; int eff_width = current.width; int eff_height = view_effective_height(ssd->view, /* use_pending */ false); - if (eff_width > 0 && eff_width < LAB_MIN_VIEW_WIDTH) { + if (eff_width > 0 && eff_width < min_view_width) { /* * Prevent negative values in calculations like * `width - SSD_BUTTON_WIDTH * SSD_BUTTON_COUNT` diff --git a/src/theme.c b/src/theme.c index 248a352a..d699773e 100644 --- a/src/theme.c +++ b/src/theme.c @@ -110,7 +110,7 @@ create_hover_fallback(struct theme *theme, const char *icon_name, int icon_width = cairo_image_surface_get_width(icon.surface); int icon_height = cairo_image_surface_get_height(icon.surface); - int width = SSD_BUTTON_WIDTH; + int width = theme->window_button_width; int height = theme->title_height; if (width && height) { @@ -488,6 +488,8 @@ theme_builtin(struct theme *theme, struct server *server) parse_hexstr("#000000", theme->window_inactive_label_text_color); theme->window_label_text_justify = parse_justification("Center"); + theme->window_button_width = 26; + parse_hexstr("#000000", theme->window_active_button_menu_unpressed_image_color); parse_hexstr("#000000", @@ -642,6 +644,15 @@ entry(struct theme *theme, const char *key, const char *value) theme->window_label_text_justify = parse_justification(value); } + if (match_glob(key, "window.button.width")) { + theme->window_button_width = atoi(value); + if (theme->window_button_width < 1) { + wlr_log(WLR_ERROR, "window.button.width cannot " + "be less than 1, clamping it to 1."); + theme->window_button_width = 1; + } + } + /* universal button */ if (match_glob(key, "window.active.button.unpressed.image.color")) { parse_hexstr(value, @@ -1065,7 +1076,7 @@ create_corners(struct theme *theme) struct wlr_box box = { .x = 0, .y = 0, - .width = SSD_BUTTON_WIDTH + theme->border_width, + .width = theme->window_button_width + theme->border_width, .height = theme->title_height + theme->border_width, }; diff --git a/src/view.c b/src/view.c index d06251aa..8dd5e796 100644 --- a/src/view.c +++ b/src/view.c @@ -611,6 +611,7 @@ view_adjust_size(struct view *view, int *w, int *h) { assert(view); struct view_size_hints hints = view_get_size_hints(view); + int min_view_width = rc.theme->window_button_width * SSD_BUTTON_COUNT; /* * "If a base size is not provided, the minimum size is to be @@ -633,7 +634,7 @@ view_adjust_size(struct view *view, int *w, int *h) * This is currently always the case for xdg-shell views. */ if (hints.min_width < 1) { - hints.min_width = LAB_MIN_VIEW_WIDTH; + hints.min_width = min_view_width; } if (hints.min_height < 1) { hints.min_height = LAB_MIN_VIEW_HEIGHT; diff --git a/src/xwayland.c b/src/xwayland.c index 9332f31d..87792d0f 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -621,8 +621,9 @@ check_natural_geometry(struct view *view) * un-maximized size when started maximized. Try to detect this * and set a fallback size. */ + int min_view_width = rc.theme->window_button_width * SSD_BUTTON_COUNT; if (!view_is_floating(view) - && (view->natural_geometry.width < LAB_MIN_VIEW_WIDTH + && (view->natural_geometry.width < min_view_width || view->natural_geometry.height < LAB_MIN_VIEW_HEIGHT)) { view_set_fallback_natural_geometry(view); } From 14d9bbab9019f8b550ec4fcf508a25807c899712 Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" Date: Sat, 20 Jul 2024 04:40:11 -0400 Subject: [PATCH 072/232] xwayland: support xinitrc scripts to configure server on launch (#1963) --- docs/README | 1 + docs/labwc-config.5.scd | 6 +++++- docs/xinitrc | 28 ++++++++++++++++++++++++++++ include/config/session.h | 6 ++++++ src/config/session.c | 8 ++++---- src/xwayland.c | 4 ++++ 6 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 docs/xinitrc diff --git a/docs/README b/docs/README index b5f022fc..c90c047a 100644 --- a/docs/README +++ b/docs/README @@ -5,6 +5,7 @@ Config layout for ~/.config/labwc/ - rc.xml - shutdown - themerc-override +- xinitrc See `man labwc-config and `man labwc-theme` for further details. diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index f2d6e925..25e1f0cf 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, shutdown and environment. +configuration: rc.xml, menu.xml, autostart, shutdown, environment and xinitrc. No configuration files are needed to start and run labwc. @@ -78,6 +78,10 @@ in labwc-theme(5). *rc.xml* is the main configuration file and all its options are described in detail below. +The *xinitrc* file is executed as a shell script whenever labwc launches the +Xwayland X11 server. This may happen multiple times throughout the session if +Xwayland is not configured to persist when no X11 clients are connected. + # CONFIGURATION This section describes *rc.xml* configuration options. diff --git a/docs/xinitrc b/docs/xinitrc new file mode 100644 index 00000000..5e6b0516 --- /dev/null +++ b/docs/xinitrc @@ -0,0 +1,28 @@ +#!/bin/sh + +## This file is run every time labwc launches Xwayland. +## +## In the default configuration, Xwayland will be launched lazily, and will +## terminate after several seconds when no X11 clients are connected. Thus, +## this script may run repeatedly throughout a single labwc session. + +# Configure the X resource database if a file is provided +# +# NOTE: when Xwayland is launched lazily, an X11 client that triggers its +# launch may attempt to read the resource database before this command can be +# run. In that case, it is recommended to make a symlink to .Xdefaults: +# +# ln -s .Xresources "${HOME}/.Xdefaults" +# +# With this link in place, X11 applications will fall back to reading +# the .Xdefaults file directly when no resource database can be read from the +# server's root window properties. +# +# Invoking xrdb is still useful to pre-load the resource database for +# subsequent clients, because any additional clients launched while the X +# server remains alive will be able to query the database without resorting to +# filesystem access. + +if [ -r "${HOME}/.Xresources" ] && command -v xrdb >/dev/null 2>&1; then + xrdb -merge "${HOME}/.Xresources" +fi diff --git a/include/config/session.h b/include/config/session.h index 867d6bd6..61a95cf9 100644 --- a/include/config/session.h +++ b/include/config/session.h @@ -4,6 +4,12 @@ struct server; +/** + * session_run_script - run a named session script (or, in merge-config mode, + * all named session scripts) from the XDG path. + */ +void session_run_script(const char *script); + /** * session_environment_init - set environment variables based on = * pairs in `${XDG_CONFIG_DIRS:-/etc/xdg}/labwc/environment` with user override diff --git a/src/config/session.c b/src/config/session.c index db589434..b52b717a 100644 --- a/src/config/session.c +++ b/src/config/session.c @@ -261,8 +261,8 @@ session_environment_init(void) paths_destroy(&paths); } -static void -run_session_script(const char *script) +void +session_run_script(const char *script) { struct wl_list paths; paths_config_create(&paths, script); @@ -293,13 +293,13 @@ 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"); + session_run_script("autostart"); } void session_shutdown(struct server *server) { - run_session_script("shutdown"); + session_run_script("shutdown"); /* Clear the dbus and systemd user environment, each may fail gracefully */ update_activation_env(server, /* initialize */ false); diff --git a/src/xwayland.c b/src/xwayland.c index 87792d0f..738c2b17 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -7,6 +7,7 @@ #include "common/macros.h" #include "common/mem.h" #include "config/rcxml.h" +#include "config/session.h" #include "labwc.h" #include "node.h" #include "ssd.h" @@ -1072,6 +1073,9 @@ sync_atoms(xcb_connection_t *xcb_conn) static void handle_server_ready(struct wl_listener *listener, void *data) { + /* Fire an Xwayland startup script if one (or many) can be found */ + session_run_script("xinitrc"); + xcb_connection_t *xcb_conn = xcb_connect(NULL, NULL); if (xcb_connection_has_error(xcb_conn)) { wlr_log(WLR_ERROR, "Failed to create xcb connection"); From d033a2fbf68064a56652a9277bc0ecfc9ec7bba5 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 17 Jul 2024 17:10:04 -0700 Subject: [PATCH 073/232] output: allow tearing with atomic mode setting Additionally, track errors and abandon the tearing allowance when it cannot be set for two-seconds' worth of consecutive frames. --- docs/labwc-config.5.scd | 11 +++++++---- include/labwc.h | 2 ++ src/config/rcxml.c | 7 ------- src/output.c | 37 +++++++++++++++++++++++++++++++++---- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 25e1f0cf..746904d4 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -186,10 +186,13 @@ this is for compatibility with Openbox. mode. ** [yes|no] - Allow tearing to reduce input lag. Default is no. - This option requires setting the environment variable - WLR_DRM_NO_ATOMIC=1. - *yes* allow tearing if requested by the active window. + Allow tearing, if requested by the active window, to reduce input lag. + Default is no. + + Note: Enabling this option with atomic mode setting is experimental. If + you experience undesirable side effects when tearing is allowed, + consider setting the environment variable WLR_DRM_NO_ATOMIC=1 when + launching labwc. ** [yes|no] Try to re-use the existing output mode (resolution / refresh rate). diff --git a/include/labwc.h b/include/labwc.h index 21cab83b..468da5b5 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -389,6 +389,8 @@ struct output { bool leased; bool gamma_lut_changed; + + uint32_t nr_tearing_failures; }; #undef LAB_NR_LAYERS diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 28eff198..da4bb637 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -885,13 +885,6 @@ entry(xmlNode *node, char *nodename, char *content) set_adaptive_sync_mode(content, &rc.adaptive_sync); } else if (!strcasecmp(nodename, "allowTearing.core")) { set_bool(content, &rc.allow_tearing); - if (rc.allow_tearing) { - char *no_atomic_env = getenv("WLR_DRM_NO_ATOMIC"); - if (!no_atomic_env || strcmp(no_atomic_env, "1") != 0) { - rc.allow_tearing = false; - wlr_log(WLR_ERROR, "tearing requires WLR_DRM_NO_ATOMIC=1"); - } - } } else if (!strcasecmp(nodename, "reuseOutputMode.core")) { set_bool(content, &rc.reuse_output_mode); } else if (!strcmp(nodename, "policy.placement")) { diff --git a/src/output.c b/src/output.c index 8ca93c47..8ba8225a 100644 --- a/src/output.c +++ b/src/output.c @@ -30,6 +30,14 @@ #include "view.h" #include "xwayland.h" +static unsigned int +get_tearing_retry_count(struct output *output) +{ + /* Two seconds worth of frames, guessing 60Hz if refresh is invalid */ + int refresh = output->wlr_output->refresh; + return refresh > 0 ? refresh / 500 : 120; +} + static bool get_tearing_preference(struct output *output) { @@ -45,6 +53,11 @@ get_tearing_preference(struct output *output) return false; } + /* Tearing should not have failed too many times */ + if (output->nr_tearing_failures >= get_tearing_retry_count(output)) { + return false; + } + /* If the active view requests tearing, or it is toggled on with action, allow it */ return server->active_view->tearing_hint; } @@ -112,11 +125,27 @@ output_frame_notify(struct wl_listener *listener, void *data) */ output_apply_gamma(output); } else { - output->pending.tearing_page_flip = - get_tearing_preference(output); + struct wlr_scene_output *scene_output = output->scene_output; + struct wlr_output_state *pending = &output->pending; - lab_wlr_scene_output_commit(output->scene_output, - &output->pending); + pending->tearing_page_flip = get_tearing_preference(output); + + bool committed = + lab_wlr_scene_output_commit(scene_output, pending); + + if (pending->tearing_page_flip) { + if (committed) { + output->nr_tearing_failures = 0; + } else { + if (++output->nr_tearing_failures >= + get_tearing_retry_count(output)) { + wlr_log(WLR_INFO, "setting tearing allowance failed " + "for two consecutive seconds, disabling"); + } + pending->tearing_page_flip = false; + lab_wlr_scene_output_commit(scene_output, pending); + } + } } struct timespec now = { 0 }; From 2346cfd33f5dc073038adb27e2042936e0e1cac1 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 24 Apr 2024 02:01:07 +0900 Subject: [PATCH 074/232] edges.c: fix flicker of snapped windows in nested session Fixes: #1621 --- src/edges.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/edges.c b/src/edges.c index c9c88184..e0c6b295 100644 --- a/src/edges.c +++ b/src/edges.c @@ -515,7 +515,7 @@ edges_adjust_move_coords(struct view *view, struct border edges, if (view_geom->x != *x) { int lshift = border.left + rc.gap; - int rshift = border.right + rc.gap + view->pending.width; + int rshift = border.right + rc.gap + view_geom->width; adjust_move_coords_1d(x, edges.left, lshift, edges.right, rshift, *x < view_geom->x); @@ -524,7 +524,7 @@ edges_adjust_move_coords(struct view *view, struct border edges, if (view_geom->y != *y) { int tshift = border.top + rc.gap; int bshift = border.bottom + rc.gap - + view_effective_height(view, /* use_pending */ true); + + view_effective_height(view, use_pending); adjust_move_coords_1d(y, edges.top, tshift, edges.bottom, bshift, *y < view_geom->y); From 2b7d0e17fcb3d3abe01d44903062c24812532a5f Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 19 Jul 2024 19:22:56 -0400 Subject: [PATCH 075/232] xdg: update initial maximize logic for wlroots 0.18 The initial configure event is now sent explicitly by labwc rather than by wlroots. We need to move the maximize/fullscreen logic to the initial commit handling accordingly. Updates #1956, fixes #1994, replaces #1995. --- src/xdg.c | 81 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/src/xdg.c b/src/xdg.c index fc4b4d24..8ab88c78 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -75,6 +75,18 @@ handle_new_popup(struct wl_listener *listener, void *data) xdg_popup_create(view, wlr_popup); } +static void +set_fullscreen_from_request(struct view *view, + struct wlr_xdg_toplevel_requested *requested) +{ + if (!view->fullscreen && requested->fullscreen + && requested->fullscreen_output) { + view_set_output(view, output_from_wlr_output(view->server, + requested->fullscreen_output)); + } + view_set_fullscreen(view, requested->fullscreen); +} + static void do_late_positioning(struct view *view) { @@ -100,11 +112,28 @@ handle_commit(struct wl_listener *listener, void *data) { struct view *view = wl_container_of(listener, view, commit); struct wlr_xdg_surface *xdg_surface = xdg_surface_from_view(view); + struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); assert(view->surface); if (xdg_surface->initial_commit) { wlr_log(WLR_DEBUG, "scheduling configure"); wlr_xdg_surface_schedule_configure(xdg_surface); + /* + * Handle initial fullscreen/maximize requests immediately after + * scheduling the initial configure event (before it is sent) in + * order to send the correct size and avoid flicker. + * + * In normal (non-fullscreen/maximized) cases, the initial + * configure event is sent with a zero size, which requests the + * application to choose its own size. + */ + if (toplevel->requested.fullscreen) { + set_fullscreen_from_request(view, &toplevel->requested); + } + if (toplevel->requested.maximized) { + view_maximize(view, VIEW_AXIS_BOTH, + /*store_natural_geometry*/ true); + } return; } @@ -311,30 +340,38 @@ static void handle_request_maximize(struct wl_listener *listener, void *data) { struct view *view = wl_container_of(listener, view, request_maximize); + struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); + + if (!toplevel->base->initialized) { + /* + * Do nothing if we have not received the initial commit yet. + * We will maximize the view in the commit handler. + */ + return; + } + if (!view->mapped && !view->output) { view_set_output(view, output_nearest_to_cursor(view->server)); } - bool maximized = xdg_toplevel_from_view(view)->requested.maximized; + bool maximized = toplevel->requested.maximized; view_maximize(view, maximized ? VIEW_AXIS_BOTH : VIEW_AXIS_NONE, /*store_natural_geometry*/ true); } -static void -set_fullscreen_from_request(struct view *view, - struct wlr_xdg_toplevel_requested *requested) -{ - if (!view->fullscreen && requested->fullscreen - && requested->fullscreen_output) { - view_set_output(view, output_from_wlr_output(view->server, - requested->fullscreen_output)); - } - view_set_fullscreen(view, requested->fullscreen); -} - static void handle_request_fullscreen(struct wl_listener *listener, void *data) { struct view *view = wl_container_of(listener, view, request_fullscreen); + struct wlr_xdg_toplevel *toplevel = xdg_toplevel_from_view(view); + + if (!toplevel->base->initialized) { + /* + * Do nothing if we have not received the initial commit yet. + * We will fullscreen the view in the commit handler. + */ + return; + } + if (!view->mapped && !view->output) { view_set_output(view, output_nearest_to_cursor(view->server)); } @@ -877,24 +914,6 @@ xdg_toplevel_new(struct wl_listener *listener, void *data) } else { view_set_ssd_mode(view, LAB_SSD_MODE_NONE); } - - /* - * Handle initial fullscreen/maximize requests. This needs to be - * done early (before map) in order to send the correct size to - * the initial configure event and avoid flicker. - * - * Note that at this point, wlroots has already scheduled (but - * not yet sent) the initial configure event with a size of 0x0. - * In normal (non-fullscreen/maximized) cases, the zero size - * requests the application to choose its own size. - */ - if (toplevel->requested.fullscreen) { - set_fullscreen_from_request(view, &toplevel->requested); - } - if (toplevel->requested.maximized) { - view_maximize(view, VIEW_AXIS_BOTH, - /*store_natural_geometry*/ true); - } } void From a75301dae506c940b6eeec3042e8b05c3160351d Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 19 Jul 2024 22:10:24 -0400 Subject: [PATCH 076/232] xdg: remove useless view_set_ssd_mode() in xdg_toplevel_new() The desired SSD mode is not known at this point. When it is known, kde-deco/xdg-deco will call view_set_ssd_mode() themselves. --- src/xdg.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/xdg.c b/src/xdg.c index 8ab88c78..f67796da 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -651,10 +651,6 @@ xdg_toplevel_view_map(struct view *view) if (!view->been_mapped) { init_foreign_toplevel(view); - /* - * FIXME: is this needed or is the earlier logic in - * xdg_surface_new() enough? - */ if (view_wants_decorations(view)) { view_set_ssd_mode(view, LAB_SSD_MODE_FULL); } else { @@ -907,13 +903,6 @@ xdg_toplevel_new(struct wl_listener *listener, void *data) CONNECT_SIGNAL(xdg_surface, xdg_toplevel_view, new_popup); wl_list_insert(&server->views, &view->link); - - /* FIXME: is view_wants_decorations() reliable this early? */ - if (view_wants_decorations(view)) { - view_set_ssd_mode(view, LAB_SSD_MODE_FULL); - } else { - view_set_ssd_mode(view, LAB_SSD_MODE_NONE); - } } void From e934c7a417a425b67078968e5acf82afbe66f8b0 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 23 Jul 2024 17:52:42 +0900 Subject: [PATCH 077/232] src/ssd/ssd.c: fix incorrect condition in ssd_part_contains() --- src/ssd/ssd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 6382983d..a8c3e173 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -342,7 +342,7 @@ ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate) } if (whole == LAB_SSD_PART_TOP) { return candidate == LAB_SSD_PART_CORNER_TOP_LEFT - || candidate == LAB_SSD_PART_CORNER_BOTTOM_LEFT; + || candidate == LAB_SSD_PART_CORNER_TOP_RIGHT; } if (whole == LAB_SSD_PART_RIGHT) { return candidate == LAB_SSD_PART_CORNER_TOP_RIGHT From 3879f1f080c470a053f234f67c989f4f268c3b73 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 17 Jul 2024 17:13:51 -0700 Subject: [PATCH 078/232] backend/drm: Implement support for renderer loss recovery This implementation is nearly identical to Sway's, except that it also reloads the configuration, to spur on reloading the server-side decorations. v2: Fix style. v3: Add a reset to the magnifier. v4: Oops, restructure reset handler a bit. v5: Commit the magnifier reset immediately, before freeing the lost allocator and renderer. v6: Also check for failed render pass, which may return NULL. v7: Add a second NULL test, just in case. --- include/labwc.h | 2 ++ include/magnifier.h | 1 + src/magnifier.c | 28 ++++++++++++++++++++++---- src/server.c | 49 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 4 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 468da5b5..77bb41d4 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -318,6 +318,8 @@ struct server { */ int pending_output_layout_change; + struct wl_listener renderer_lost; + struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; struct wl_listener gamma_control_set_gamma; diff --git a/include/magnifier.h b/include/magnifier.h index 60005458..daa6d7a3 100644 --- a/include/magnifier.h +++ b/include/magnifier.h @@ -20,5 +20,6 @@ bool output_wants_magnification(struct output *output); void magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box *damage); bool is_magnify_on(void); +void magnify_reset(void); #endif /* LABWC_MAGNIFIER_H */ diff --git a/src/magnifier.c b/src/magnifier.c index 243455aa..fded35fd 100644 --- a/src/magnifier.c +++ b/src/magnifier.c @@ -10,6 +10,10 @@ static bool magnify_on; static double mag_scale = 0.0; +/* Reuse a single scratch buffer */ +static struct wlr_buffer *tmp_buffer = NULL; +static struct wlr_texture *tmp_texture = NULL; + #define CLAMP(in, lower, upper) MAX(MIN((in), (upper)), (lower)) void @@ -21,10 +25,6 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box struct wlr_fbox src_box; bool fullscreen = false; - /* Reuse a single scratch buffer */ - static struct wlr_buffer *tmp_buffer = NULL; - static struct wlr_texture *tmp_texture = NULL; - /* TODO: This looks way too complicated to just get the used format */ struct wlr_drm_format wlr_drm_format = {0}; struct wlr_shm_attributes shm_attribs = {0}; @@ -125,6 +125,10 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box /* Extract source region into temporary buffer */ struct wlr_render_pass *tmp_render_pass = wlr_renderer_begin_buffer_pass( server->renderer, tmp_buffer, NULL); + if (!tmp_render_pass) { + wlr_log(WLR_ERROR, "Failed to begin magnifier render pass"); + return; + } wlr_buffer_lock(output_buffer); struct wlr_texture *output_texture = wlr_texture_from_buffer( @@ -152,6 +156,10 @@ magnify(struct output *output, struct wlr_buffer *output_buffer, struct wlr_box /* Render to the output buffer itself */ tmp_render_pass = wlr_renderer_begin_buffer_pass( server->renderer, output_buffer, NULL); + if (!tmp_render_pass) { + wlr_log(WLR_ERROR, "Failed to begin second magnifier render pass"); + goto cleanup; + } /* Borders */ if (fullscreen) { @@ -286,6 +294,18 @@ magnify_set_scale(struct server *server, enum magnify_dir dir) } } +/* Reset any buffers held by the magnifier */ +void +magnify_reset(void) +{ + if (tmp_texture && tmp_buffer) { + wlr_texture_destroy(tmp_texture); + wlr_buffer_drop(tmp_buffer); + tmp_buffer = NULL; + tmp_texture = NULL; + } +} + /* Report whether magnification is enabled */ bool is_magnify_on(void) diff --git a/src/server.c b/src/server.c index b8beee2c..7f1caa30 100644 --- a/src/server.c +++ b/src/server.c @@ -28,6 +28,7 @@ #include "idle.h" #include "labwc.h" #include "layers.h" +#include "magnifier.h" #include "menu/menu.h" #include "output-state.h" #include "output-virtual.h" @@ -249,6 +250,51 @@ get_headless_backend(struct wlr_backend *backend, void *data) } } +static void +handle_renderer_lost(struct wl_listener *listener, void *data) +{ + struct server *server = wl_container_of(listener, server, renderer_lost); + + wlr_log(WLR_INFO, "Re-creating renderer after GPU reset"); + + struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); + if (!renderer) { + wlr_log(WLR_ERROR, "Unable to create renderer"); + return; + } + + struct wlr_allocator *allocator = + wlr_allocator_autocreate(server->backend, renderer); + if (!allocator) { + wlr_log(WLR_ERROR, "Unable to create allocator"); + wlr_renderer_destroy(renderer); + return; + } + + struct wlr_renderer *old_renderer = server->renderer; + struct wlr_allocator *old_allocator = server->allocator; + server->renderer = renderer; + server->allocator = allocator; + + wl_list_remove(&server->renderer_lost.link); + wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + + wlr_compositor_set_renderer(compositor, renderer); + + struct output *output; + wl_list_for_each(output, &server->outputs, link) { + wlr_output_init_render(output->wlr_output, + server->allocator, server->renderer); + } + + reload_config_and_theme(server); + + magnify_reset(); + + wlr_allocator_destroy(old_allocator); + wlr_renderer_destroy(old_renderer); +} + void server_init(struct server *server) { @@ -336,6 +382,9 @@ server_init(struct server *server) exit(EXIT_FAILURE); } + server->renderer_lost.notify = handle_renderer_lost; + wl_signal_add(&server->renderer->events.lost, &server->renderer_lost); + wlr_renderer_init_wl_display(server->renderer, server->wl_display); /* From 1765bf8cc2adc8fb2218a02e01652d6659402ac1 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 23 Jul 2024 19:09:48 +0900 Subject: [PATCH 079/232] layer-shell: stop sending configure events on surface creation With wlroots 0.18, layer-shell's new_surface event is emitted on zwlr_layer_shell_v1.get_layer_surface request rather than the first commit. This change layer-shell clients like fuzzel to flicker on launch because labwc was sending a configure event with fullscreen geometry due to the absence of geometry hints on get_layer_surface requests. This commit removes the code that updates the usable area from new_surface handler, preventing unintended configure events with fullscreen geometry. --- src/layers.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/layers.c b/src/layers.c index 849e8d34..a5ca7315 100644 --- a/src/layers.c +++ b/src/layers.c @@ -579,15 +579,6 @@ handle_new_layer_surface(struct wl_listener *listener, void *data) surface->node_destroy.notify = handle_node_destroy; wl_signal_add(&surface->scene_layer_surface->tree->node.events.destroy, &surface->node_destroy); - - /* - * Temporarily set the layer's current state to pending so that - * it can easily be arranged. - */ - struct wlr_layer_surface_v1_state old_state = layer_surface->current; - layer_surface->current = layer_surface->pending; - output_update_usable_area(output); - layer_surface->current = old_state; } void From e15bde328dd77e86079fa66348d7d17ef31ba79a Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 21 Jul 2024 02:33:51 +0900 Subject: [PATCH 080/232] src/xdg-popup.c: choose output depending on xdg-positioner wlr_popup->current.geometry.{x,y} are usually zero on initial commit, so xdg-popups were always unconstrained against the output which contains the top-left of the parent toplevel. This commit changes xdg-popups to be unconstrained against the output which contains the top-left of preliminary popup geometry designated by xdg-positioner given as an argument of "get_popup" or "reposition" requests. --- src/xdg-popup.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/xdg-popup.c b/src/xdg-popup.c index c17d195a..a4b2295b 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -27,10 +27,15 @@ popup_unconstrain(struct xdg_popup *popup) { struct view *view = popup->parent_view; struct server *server = view->server; - struct wlr_box *popup_box = &popup->wlr_popup->current.geometry; + + int parent_lx, parent_ly; + struct wlr_scene_tree *parent_tree = popup->wlr_popup->parent->data; + wlr_scene_node_coords(&parent_tree->node, &parent_lx, &parent_ly); + + struct wlr_box *popup_box = &popup->wlr_popup->scheduled.geometry; struct output *output = output_nearest_to(server, - view->current.x + popup_box->x, - view->current.y + popup_box->y); + parent_lx + popup_box->x, + parent_ly + popup_box->y); struct wlr_box usable = output_usable_area_in_layout_coords(output); struct wlr_box output_toplevel_box = { @@ -116,7 +121,7 @@ xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup) * 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 * provide the proper parent scene node of the xdg popup. To enable - * this, we always set the user data field of xdg_surfaces to the + * this, we always set the user data field of wlr_surfaces to the * corresponding scene node. * * xdg-popups live in server->xdg_popup_tree so that they can be From a7024dd22481baaa8945a0700d1a3e603c864c4f Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 22 Jul 2024 16:20:17 +0900 Subject: [PATCH 081/232] src/xdg-popup.c: take into account CSD borders for unconstraining wlr_xdg_popup_unconstrain_from_box() takes a constraint box relative to toplevel surface, not window. --- src/xdg-popup.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/xdg-popup.c b/src/xdg-popup.c index a4b2295b..dd17b990 100644 --- a/src/xdg-popup.c +++ b/src/xdg-popup.c @@ -28,19 +28,33 @@ popup_unconstrain(struct xdg_popup *popup) struct view *view = popup->parent_view; struct server *server = view->server; + /* Get position of parent toplevel/popup */ int parent_lx, parent_ly; struct wlr_scene_tree *parent_tree = popup->wlr_popup->parent->data; wlr_scene_node_coords(&parent_tree->node, &parent_lx, &parent_ly); + /* Get usable area to constrain by */ struct wlr_box *popup_box = &popup->wlr_popup->scheduled.geometry; struct output *output = output_nearest_to(server, parent_lx + popup_box->x, parent_ly + popup_box->y); struct wlr_box usable = output_usable_area_in_layout_coords(output); + /* Get offset of toplevel window from its surface */ + int toplevel_dx = 0; + int toplevel_dy = 0; + struct wlr_xdg_surface *toplevel_surface = xdg_surface_from_view(view); + if (toplevel_surface) { + toplevel_dx = toplevel_surface->current.geometry.x; + toplevel_dy = toplevel_surface->current.geometry.y; + } else { + wlr_log(WLR_ERROR, "toplevel is not valid XDG surface"); + } + + /* Geometry of usable area relative to toplevel surface */ struct wlr_box output_toplevel_box = { - .x = usable.x - view->current.x, - .y = usable.y - view->current.y, + .x = usable.x - (view->current.x - toplevel_dx), + .y = usable.y - (view->current.y - toplevel_dy), .width = usable.width, .height = usable.height, }; From 92db067e8643c97801c7fcfed521469614bcce40 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 19 Jul 2024 21:59:13 +0100 Subject: [PATCH 082/232] NEWS.md: update for 0.7.4 --- NEWS.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 6c0e28f2..ec82c33f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog] | Date | All Changes | wlroots version | lines-of-code | |------------|---------------|-----------------|---------------| +| 2024-06-19 | [0.7.4] | 0.17.4 | 22746 | | 2024-06-12 | [0.7.3] | 0.17.4 | 22731 | | 2024-05-10 | [0.7.2] | 0.17.3 | 21368 | | 2024-03-01 | [0.7.1] | 0.17.1 | 18624 | @@ -29,6 +30,14 @@ The format is based on [Keep a Changelog] | 2021-04-15 | [0.2.0] | 0.13.0 | 5011 | | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | +## [0.7.4] + +### Fixed + +- Make SSD borders respect snapped state on Reconfigure. PR #2003 +- Fix magnifier by disabling direct scanout when active. PR #1989 +- Fix crash triggered by pipemenu without parent `` element. PR #1988 + ## [0.7.3] Following a couple of big releases, this one feels like more steady with lots of @@ -1434,7 +1443,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.3...HEAD +[unreleased]: https://github.com/labwc/labwc/compare/0.7.4...HEAD +[0.7.4]: https://github.com/labwc/labwc/compare/0.7.3...0.7.4 [0.7.3]: https://github.com/labwc/labwc/compare/0.7.2...0.7.3 [0.7.2]: https://github.com/labwc/labwc/compare/0.7.1...0.7.2 [0.7.1]: https://github.com/labwc/labwc/compare/0.7.0...0.7.1 From 750d37b16c8e0dd5478f94aa26ab07b84dacef93 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 24 Jul 2024 18:09:53 +0200 Subject: [PATCH 083/232] multirect: remove destroy listener before freeing Detected by `-Db_sanitize=address,undefined` for libwayland --- src/common/graphic-helpers.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/common/graphic-helpers.c b/src/common/graphic-helpers.c index 5c754b30..0ecdd697 100644 --- a/src/common/graphic-helpers.c +++ b/src/common/graphic-helpers.c @@ -14,6 +14,7 @@ static void multi_rect_destroy_notify(struct wl_listener *listener, void *data) { struct multi_rect *rect = wl_container_of(listener, rect, destroy); + wl_list_remove(&rect->destroy.link); free(rect); } From d9866aafa59e073ebf4bd45b07c7bc245593710f Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Thu, 25 Jul 2024 21:56:05 +0200 Subject: [PATCH 084/232] workspaces: slight struct reordering --- include/labwc.h | 8 +++++--- src/action.c | 2 +- src/debug.c | 2 +- src/desktop.c | 4 ++-- src/osd.c | 2 +- src/view.c | 6 +++--- src/workspaces.c | 50 ++++++++++++++++++++++++------------------------ src/xdg.c | 2 +- src/xwayland.c | 2 +- 9 files changed, 40 insertions(+), 38 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 77bb41d4..3439a099 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -297,9 +297,11 @@ struct server { struct wlr_scene_tree *menu_tree; /* Workspaces */ - struct wl_list workspaces; /* struct workspace.link */ - struct workspace *workspace_current; - struct workspace *workspace_last; + struct { + struct wl_list all; /* struct workspace.link */ + struct workspace *current; + struct workspace *last; + } workspaces; struct wl_list outputs; struct wl_listener new_output; diff --git a/src/action.c b/src/action.c index 0aa45d7a..e976a446 100644 --- a/src/action.c +++ b/src/action.c @@ -982,7 +982,7 @@ actions_run(struct view *activator, struct server *server, * a required argument for both SendToDesktop and GoToDesktop. */ struct workspace *target = workspaces_find( - server->workspace_current, to, wrap); + server->workspaces.current, to, wrap); if (!target) { break; } diff --git a/src/debug.c b/src/debug.c index d5750fea..7fb2672d 100644 --- a/src/debug.c +++ b/src/debug.c @@ -106,7 +106,7 @@ get_special(struct server *server, struct wlr_scene_node *node) } if (node->parent == server->view_tree) { struct workspace *workspace; - wl_list_for_each(workspace, &server->workspaces, link) { + wl_list_for_each(workspace, &server->workspaces.all, link) { if (&workspace->tree->node == node) { return workspace->name; } diff --git a/src/desktop.c b/src/desktop.c index 2c0db207..353132ef 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -145,7 +145,7 @@ desktop_topmost_focusable_view(struct server *server) struct view *view; struct wl_list *node_list; struct wlr_scene_node *node; - node_list = &server->workspace_current->tree->children; + node_list = &server->workspaces.current->tree->children; wl_list_for_each_reverse(node, node_list, link) { if (!node->data) { /* We found some non-view, most likely the region overlay */ @@ -185,7 +185,7 @@ desktop_focus_output(struct output *output) struct wlr_scene_node *node; struct wlr_output_layout *layout = output->server->output_layout; struct wl_list *list_head = - &output->server->workspace_current->tree->children; + &output->server->workspaces.current->tree->children; wl_list_for_each_reverse(node, list_head, link) { if (!node->data) { continue; diff --git a/src/osd.c b/src/osd.c index 91dbd270..7e2aedab 100644 --- a/src/osd.c +++ b/src/osd.c @@ -331,7 +331,7 @@ display_osd(struct output *output, struct wl_array *views) struct server *server = output->server; struct theme *theme = server->theme; bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; - const char *workspace_name = server->workspace_current->name; + const char *workspace_name = server->workspaces.current->name; float scale = output->wlr_output->scale; int w = theme->osd_window_switcher_width; diff --git a/src/view.c b/src/view.c index 8dd5e796..f68db65e 100644 --- a/src/view.c +++ b/src/view.c @@ -140,7 +140,7 @@ matches_criteria(struct view *view, enum lab_view_criteria criteria) * special in that they live in a different tree. */ struct server *server = view->server; - if (view->scene_tree->node.parent != server->workspace_current->tree + if (view->scene_tree->node.parent != server->workspaces.current->tree && !view_is_always_on_top(view)) { return false; } @@ -1451,7 +1451,7 @@ view_toggle_always_on_top(struct view *view) { assert(view); if (view_is_always_on_top(view)) { - view->workspace = view->server->workspace_current; + view->workspace = view->server->workspaces.current; wlr_scene_node_reparent(&view->scene_tree->node, view->workspace->tree); } else { @@ -1473,7 +1473,7 @@ view_toggle_always_on_bottom(struct view *view) { assert(view); if (view_is_always_on_bottom(view)) { - view->workspace = view->server->workspace_current; + view->workspace = view->server->workspaces.current; wlr_scene_node_reparent(&view->scene_tree->node, view->workspace->tree); } else { diff --git a/src/workspaces.c b/src/workspaces.c index 76dc93d6..8e9b912f 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -66,7 +66,7 @@ _osd_update(struct server *server) theme->osd_workspace_switcher_boxes_height == 0; /* Dimensions */ - size_t workspace_count = wl_list_length(&server->workspaces); + size_t workspace_count = wl_list_length(&server->workspaces.all); uint16_t marker_width = workspace_count * (rect_width + padding) - padding; uint16_t width = margin * 2 + (marker_width < 200 ? 200 : marker_width); uint16_t height = margin * (hide_boxes ? 2 : 3) + rect_height + font_height(&rc.font_osd); @@ -106,8 +106,8 @@ _osd_update(struct server *server) uint16_t x; if (!hide_boxes) { x = (width - marker_width) / 2; - wl_list_for_each(workspace, &server->workspaces, link) { - bool active = workspace == server->workspace_current; + wl_list_for_each(workspace, &server->workspaces.all, link) { + bool active = workspace == server->workspaces.current; set_cairo_color(cairo, server->theme->osd_label_text_color); cairo_rectangle(cairo, x, margin, rect_width - padding, rect_height); @@ -128,7 +128,7 @@ _osd_update(struct server *server) pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); /* Center workspace indicator on the x axis */ - int req_width = font_width(&rc.font_osd, server->workspace_current->name); + int req_width = font_width(&rc.font_osd, server->workspaces.current->name); req_width = MIN(req_width, width - 2 * margin); x = (width - req_width) / 2; if (!hide_boxes) { @@ -141,7 +141,7 @@ _osd_update(struct server *server) pango_layout_set_font_description(layout, desc); pango_layout_set_width(layout, req_width * PANGO_SCALE); pango_font_description_free(desc); - pango_layout_set_text(layout, server->workspace_current->name, -1); + pango_layout_set_text(layout, server->workspaces.current->name, -1); pango_cairo_show_layout(cairo, layout); g_object_unref(layout); @@ -180,9 +180,9 @@ add_workspace(struct server *server, const char *name) workspace->server = server; workspace->name = xstrdup(name); workspace->tree = wlr_scene_tree_create(server->view_tree); - wl_list_append(&server->workspaces, &workspace->link); - if (!server->workspace_current) { - server->workspace_current = workspace; + wl_list_append(&server->workspaces.all, &workspace->link); + if (!server->workspaces.current) { + server->workspaces.current = workspace; } else { wlr_scene_node_set_enabled(&workspace->tree->node, false); } @@ -260,7 +260,7 @@ _osd_show(struct server *server) void workspaces_init(struct server *server) { - wl_list_init(&server->workspaces); + wl_list_init(&server->workspaces.all); struct workspace *conf; wl_list_for_each(conf, &rc.workspace_config.workspaces, link) { @@ -278,13 +278,13 @@ workspaces_switch_to(struct workspace *target, bool update_focus) { assert(target); struct server *server = target->server; - if (target == server->workspace_current) { + if (target == server->workspaces.current) { return; } /* Disable the old workspace */ wlr_scene_node_set_enabled( - &server->workspace_current->tree->node, false); + &server->workspaces.current->tree->node, false); /* Move Omnipresent views to new workspace */ struct view *view; @@ -300,10 +300,10 @@ workspaces_switch_to(struct workspace *target, bool update_focus) wlr_scene_node_set_enabled(&target->tree->node, true); /* Save the last visited workspace */ - server->workspace_last = server->workspace_current; + server->workspaces.last = server->workspaces.current; /* Make sure new views will spawn on the new workspace */ - server->workspace_current = target; + server->workspaces.current = target; /* * Make sure we are focusing what the user sees. @@ -362,7 +362,7 @@ workspaces_find(struct workspace *anchor, const char *name, bool wrap) size_t index = 0; struct workspace *target; size_t wants_index = parse_workspace_index(name); - struct wl_list *workspaces = &anchor->server->workspaces; + struct wl_list *workspaces = &anchor->server->workspaces.all; if (wants_index) { wl_list_for_each(target, workspaces, link) { @@ -373,7 +373,7 @@ workspaces_find(struct workspace *anchor, const char *name, bool wrap) } else if (!strcasecmp(name, "current")) { return anchor; } else if (!strcasecmp(name, "last")) { - return anchor->server->workspace_last; + return anchor->server->workspaces.last; } else if (!strcasecmp(name, "left")) { return get_prev(anchor, workspaces, wrap); } else if (!strcasecmp(name, "right")) { @@ -408,7 +408,7 @@ workspaces_reconfigure(struct server *server) * - Destroy workspaces if fewer workspace are desired */ - struct wl_list *actual_workspace_link = server->workspaces.next; + struct wl_list *actual_workspace_link = server->workspaces.all.next; struct workspace *configured_workspace; wl_list_for_each(configured_workspace, @@ -416,7 +416,7 @@ workspaces_reconfigure(struct server *server) struct workspace *actual_workspace = wl_container_of( actual_workspace_link, actual_workspace, link); - if (actual_workspace_link == &server->workspaces) { + if (actual_workspace_link == &server->workspaces.all) { /* # of configured workspaces increased */ wlr_log(WLR_DEBUG, "Adding workspace \"%s\"", configured_workspace->name); @@ -433,16 +433,16 @@ workspaces_reconfigure(struct server *server) actual_workspace_link = actual_workspace_link->next; } - if (actual_workspace_link == &server->workspaces) { + if (actual_workspace_link == &server->workspaces.all) { return; } /* # of configured workspaces decreased */ overlay_hide(&server->seat); struct workspace *first_workspace = - wl_container_of(server->workspaces.next, first_workspace, link); + wl_container_of(server->workspaces.all.next, first_workspace, link); - while (actual_workspace_link != &server->workspaces) { + while (actual_workspace_link != &server->workspaces.all) { struct workspace *actual_workspace = wl_container_of( actual_workspace_link, actual_workspace, link); @@ -456,12 +456,12 @@ workspaces_reconfigure(struct server *server) } } - if (server->workspace_current == actual_workspace) { + if (server->workspaces.current == actual_workspace) { workspaces_switch_to(first_workspace, /* update_focus */ true); } - if (server->workspace_last == actual_workspace) { - server->workspace_last = first_workspace; + if (server->workspaces.last == actual_workspace) { + server->workspaces.last = first_workspace; } actual_workspace_link = actual_workspace_link->next; @@ -473,8 +473,8 @@ void workspaces_destroy(struct server *server) { struct workspace *workspace, *tmp; - wl_list_for_each_safe(workspace, tmp, &server->workspaces, link) { + wl_list_for_each_safe(workspace, tmp, &server->workspaces.all, link) { destroy_workspace(workspace); } - assert(wl_list_empty(&server->workspaces)); + assert(wl_list_empty(&server->workspaces.all)); } diff --git a/src/xdg.c b/src/xdg.c index f67796da..ed48cbf0 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -840,7 +840,7 @@ xdg_toplevel_new(struct wl_listener *listener, void *data) view->output->wlr_output->scale); } - view->workspace = server->workspace_current; + view->workspace = server->workspaces.current; view->scene_tree = wlr_scene_tree_create(view->workspace->tree); wlr_scene_node_set_enabled(&view->scene_tree->node, false); diff --git a/src/xwayland.c b/src/xwayland.c index 738c2b17..8fc87d2b 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -985,7 +985,7 @@ xwayland_view_create(struct server *server, xwayland_view->xwayland_surface = xsurface; xsurface->data = view; - view->workspace = server->workspace_current; + view->workspace = server->workspaces.current; view->scene_tree = wlr_scene_tree_create(view->workspace->tree); node_descriptor_create(&view->scene_tree->node, LAB_NODE_DESC_VIEW, view); From c202d77c2d800abccff24b0d9a0be6b5b928bf09 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 20 Jul 2024 16:58:39 +0900 Subject: [PATCH 085/232] Add resistance when dragging tiled windows Adds a config option with default value 20. This prevents tiled/maximized windows from being unintentionally untiled. --- docs/labwc-config.5.scd | 4 ++++ docs/rc.xml.all | 1 + include/config/rcxml.h | 1 + include/labwc.h | 18 ++++++++++++++++ src/config/rcxml.c | 3 +++ src/input/cursor.c | 12 ++++++++++- src/interactive.c | 46 ++++++++++++++++++++++++++++++++++++++--- src/xdg.c | 1 + 8 files changed, 82 insertions(+), 4 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 746904d4..cfd46316 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -347,6 +347,10 @@ extending outward from the snapped edge. SnapToEdge action for that edge. A *range* of 0 disables snapping via interactive moves. Default is 1. +** + Sets the movement of cursor in pixel required for a tiled or maximized + window to be moved with an interactive move. Default is 20. + ** [yes|no] Show an overlay when snapping to a window to an edge. Default is yes. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index c537b039..ea331352 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -125,6 +125,7 @@ 1 + 20 diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 53263df7..62fb5e57 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -125,6 +125,7 @@ struct rcxml { int snap_overlay_delay_outer; bool snap_top_maximize; enum tiling_events_mode snap_tiling_events_mode; + int snap_drag_resistance; enum resize_indicator_mode resize_indicator; bool resize_draw_contents; diff --git a/include/labwc.h b/include/labwc.h index 77bb41d4..564f668c 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -256,7 +256,16 @@ struct server { /* cursor interactive */ enum input_mode input_mode; struct view *grabbed_view; + /* + * When an interactive move is requested for tiled/maximized views by CSD + * clients or by Drag actions, the actual motion and untiling of the view + * can be delayed to prevent the view from being unintentionally untiled. + * During this delay, move_pending is set. + */ + bool move_pending; + /* Cursor position when interactive move/resize is requested */ double grab_x, grab_y; + /* View geometry when interactive move/resize is requested */ struct wlr_box grab_box; uint32_t resize_edges; @@ -497,6 +506,15 @@ void seat_output_layout_changed(struct seat *seat); */ void interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry); +/** + * interactive_move_tiled_view_to() - Un-tile the tiled/maximized view at the + * start of an interactive move or when an interactive move is pending. + * Returns true if the distance of cursor motion exceeds the value of + * and the view is un-tiled. + */ +bool interactive_move_tiled_view_to(struct server *server, struct view *view, + struct wlr_box *geometry); + void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges); void interactive_finish(struct view *view); void interactive_cancel(struct view *view); diff --git a/src/config/rcxml.c b/src/config/rcxml.c index da4bb637..74388b10 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -971,6 +971,8 @@ entry(xmlNode *node, char *nodename, char *content) } else { wlr_log(WLR_ERROR, "ignoring invalid value for notifyClient"); } + } else if (!strcasecmp(nodename, "dragResistance.snapping")) { + rc.snap_drag_resistance = atoi(content); /* */ } else if (!strcasecmp(nodename, "show.windowSwitcher")) { @@ -1278,6 +1280,7 @@ rcxml_init(void) rc.snap_overlay_delay_outer = 500; rc.snap_top_maximize = true; rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS; + rc.snap_drag_resistance = 20; rc.window_switcher.show = true; rc.window_switcher.preview = true; diff --git a/src/input/cursor.c b/src/input/cursor.c index 0a705068..3df48983 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -232,9 +232,19 @@ request_set_primary_selection_notify(struct wl_listener *listener, void *data) static void process_cursor_move(struct server *server, uint32_t time) { + struct view *view = server->grabbed_view; + + /* + * Un-tile the view when interactive move is delayed and the distance + * of cursor movement exceeds . + */ + if (server->move_pending && !interactive_move_tiled_view_to( + server, server->grabbed_view, &server->grab_box)) { + return; + } + double dx = server->seat.cursor->x - server->grab_x; double dy = server->seat.cursor->y - server->grab_y; - struct view *view = server->grabbed_view; /* Move the grabbed view to the new position. */ dx += server->grab_box.x; diff --git a/src/interactive.c b/src/interactive.c index ae5a6972..bd9745a0 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include "edges.h" #include "input/keyboard.h" #include "labwc.h" @@ -31,6 +32,38 @@ interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry) view->current.height, geometry->height); } +bool +interactive_move_tiled_view_to(struct server *server, struct view *view, + struct wlr_box *geometry) +{ + assert(!view_is_floating(view)); + + int resistance = rc.snap_drag_resistance; + + if (server->input_mode == LAB_INPUT_STATE_MOVE) { + /* When called from cursor motion handler */ + assert(server->move_pending && server->grabbed_view == view); + + double dx = server->seat.cursor->x - server->grab_x; + double dy = server->seat.cursor->y - server->grab_y; + if (dx * dx + dy * dy < resistance * resistance) { + return false; + } + } else { + /* When called from interactive_begin() */ + if (resistance > 0) { + return false; + } + } + + view_set_shade(view, false); + view_set_untiled(view); + view_restore_to(view, *geometry); + server->move_pending = false; + + return true; +} + void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) { @@ -83,9 +116,15 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) interactive_anchor_to_cursor(view, &geometry); } - view_set_shade(view, false); - view_set_untiled(view); - view_restore_to(view, geometry); + /* + * If is non-zero, the + * tiled/maximized view is un-tiled later in cursor + * motion handler. + */ + if (!interactive_move_tiled_view_to( + server, view, &geometry)) { + server->move_pending = true; + } } else { /* Store natural geometry at start of move */ view_store_natural_geometry(view); @@ -261,6 +300,7 @@ interactive_cancel(struct view *view) view->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; view->server->grabbed_view = NULL; + view->server->move_pending = false; /* Update focus/cursor image */ cursor_update_focus(view->server); diff --git a/src/xdg.c b/src/xdg.c index f67796da..33d26645 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -94,6 +94,7 @@ do_late_positioning(struct view *view) if (server->input_mode == LAB_INPUT_STATE_MOVE && view == server->grabbed_view) { /* Keep view underneath cursor */ + /* TODO: resistance is not considered */ interactive_anchor_to_cursor(view, &view->pending); /* Update grab offsets */ server->grab_x = server->seat.cursor->x; From a9daaa05be2e3b3db329fb79ec7abf7a7ebbdc64 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Fri, 2 Aug 2024 21:57:19 +0200 Subject: [PATCH 086/232] common/array.h: add array_add() helper --- include/common/array.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/include/common/array.h b/include/common/array.h index f6c51fc9..a1ea7499 100644 --- a/include/common/array.h +++ b/include/common/array.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-only */ #ifndef LABWC_ARRAY_H #define LABWC_ARRAY_H +#include +#include #include /* @@ -41,4 +43,34 @@ wl_array_len(struct wl_array *array) pos && (const char *)pos >= (const char *)(array)->data; \ (pos)--) +/** + * array_add() - add item to wl_array and exit on allocation error + * @_arr: wl_array to add the item to + * @_val: the item to add to the array + * + * Let us illustrate the function of this macro by an example: + * + * uint32_t value = 5; + * array_add(array, value); + * + * ...is the equivalent of the code below which is how you would + * otherwise use the wl_array API: + * + * uint32_t *elm = wl_array_add(array, sizeof(uint32_t)); + * if (!elm) { + * perror("failed to allocate memory"); + * exit(EXIT_FAILURE); + * } + * *elm = value; + */ +#define array_add(_arr, _val) do { \ + __typeof__(_val) *_entry = wl_array_add( \ + (_arr), sizeof(__typeof__(_val))); \ + if (!_entry) { \ + perror("Failed to allocate memory"); \ + exit(EXIT_FAILURE); \ + } \ + *_entry = (_val); \ + } while (0) + #endif /* LABWC_ARRAY_H */ From 31f4336ed6b91ddff6c536490ac0e3870f83ec0a Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 24 Jul 2024 01:04:06 +0200 Subject: [PATCH 087/232] cosmic-workspaces: protocol implementation --- .../protocols/cosmic-workspaces-internal.h | 61 ++ include/protocols/cosmic-workspaces.h | 94 +++ protocols/cosmic-workspace-unstable-v1.xml | 364 ++++++++++ protocols/meson.build | 1 + src/meson.build | 1 + .../cosmic_workspaces/cosmic-workspaces.c | 651 ++++++++++++++++++ src/protocols/cosmic_workspaces/meson.build | 5 + src/protocols/cosmic_workspaces/output.c | 174 +++++ .../cosmic_workspaces/transactions.c | 93 +++ src/protocols/meson.build | 1 + 10 files changed, 1445 insertions(+) create mode 100644 include/protocols/cosmic-workspaces-internal.h create mode 100644 include/protocols/cosmic-workspaces.h create mode 100644 protocols/cosmic-workspace-unstable-v1.xml create mode 100644 src/protocols/cosmic_workspaces/cosmic-workspaces.c create mode 100644 src/protocols/cosmic_workspaces/meson.build create mode 100644 src/protocols/cosmic_workspaces/output.c create mode 100644 src/protocols/cosmic_workspaces/transactions.c create mode 100644 src/protocols/meson.build diff --git a/include/protocols/cosmic-workspaces-internal.h b/include/protocols/cosmic-workspaces-internal.h new file mode 100644 index 00000000..36556bc6 --- /dev/null +++ b/include/protocols/cosmic-workspaces-internal.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H +#define LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H + +struct lab_cosmic_workspace; +struct lab_cosmic_workspace_group; +struct lab_cosmic_workspace_manager; + +enum pending_change { + /* group events */ + CW_PENDING_WS_CREATE = 1 << 0, + + /* ws events*/ + CW_PENDING_WS_ACTIVATE = 1 << 1, + CW_PENDING_WS_DEACTIVATE = 1 << 2, + CW_PENDING_WS_REMOVE = 1 << 3, +}; + +struct transaction { + uint32_t change; + struct wl_list link; +}; + +struct transaction_workspace { + struct transaction base; + struct lab_cosmic_workspace *workspace; +}; + +struct transaction_group { + struct transaction base; + struct lab_cosmic_workspace_group *group; + char *new_workspace_name; +}; + +struct session_context { + int ref_count; + struct wl_list transactions; +}; + +struct wl_resource_addon { + struct session_context *ctx; + void *data; +}; + +struct wl_resource_addon *resource_addon_create(struct session_context *ctx); + +void transaction_add_workspace_ev(struct lab_cosmic_workspace *ws, + struct wl_resource *resource, enum pending_change change); + +void transaction_add_workspace_group_ev(struct lab_cosmic_workspace_group *group, + struct wl_resource *resource, enum pending_change change, + const char *new_workspace_name); + +void resource_addon_destroy(struct wl_resource_addon *addon); + +void group_output_send_initial_state(struct lab_cosmic_workspace_group *group, + struct wl_resource *group_resource); + +void manager_schedule_done_event(struct lab_cosmic_workspace_manager *manager); + +#endif /* LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H */ diff --git a/include/protocols/cosmic-workspaces.h b/include/protocols/cosmic-workspaces.h new file mode 100644 index 00000000..8776bfad --- /dev/null +++ b/include/protocols/cosmic-workspaces.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_PROTOCOLS_COSMIC_WORKSPACES_H +#define LABWC_PROTOCOLS_COSMIC_WORKSPACES_H + +#include +#include + +struct wlr_output; + +struct lab_cosmic_workspace_manager { + struct wl_global *global; + struct wl_list groups; + uint32_t caps; + struct wl_event_source *idle_source; + struct wl_event_loop *event_loop; + + struct { + struct wl_listener display_destroy; + } on; + + struct wl_list resources; +}; + +struct lab_cosmic_workspace_group { + struct lab_cosmic_workspace_manager *manager; + struct wl_list workspaces; + struct wl_array capabilities; + struct { + struct wl_signal create_workspace; + struct wl_signal destroy; + } events; + + struct wl_list link; + struct wl_list outputs; + struct wl_list resources; +}; + +struct lab_cosmic_workspace { + struct lab_cosmic_workspace_group *group; + char *name; + struct wl_array coordinates; + struct wl_array capabilities; + uint32_t state; /* enum lab_cosmic_workspace_state */ + uint32_t state_pending; /* enum lab_cosmic_workspace_state */ + + struct { + struct wl_signal activate; + struct wl_signal deactivate; + struct wl_signal remove; + struct wl_signal destroy; + } events; + + struct wl_list link; + struct wl_list resources; +}; + +enum lab_cosmic_workspace_caps { + CW_CAP_NONE = 0, + CW_CAP_GRP_ALL = 0x000000ff, + CW_CAP_WS_ALL = 0x0000ff00, + + /* group caps */ + CW_CAP_GRP_WS_CREATE = 1 << 0, + + /* workspace caps */ + CW_CAP_WS_ACTIVATE = 1 << 8, + CW_CAP_WS_DEACTIVATE = 1 << 9, + CW_CAP_WS_REMOVE = 1 << 10, +}; + +struct lab_cosmic_workspace_manager *lab_cosmic_workspace_manager_create( + struct wl_display *display, uint32_t caps, uint32_t version); + +struct lab_cosmic_workspace_group *lab_cosmic_workspace_group_create( + struct lab_cosmic_workspace_manager *manager); + +void lab_cosmic_workspace_group_output_enter( + struct lab_cosmic_workspace_group *group, struct wlr_output *output); + +void lab_cosmic_workspace_group_output_leave( + + struct lab_cosmic_workspace_group *group, struct wlr_output *output); +void lab_cosmic_workspace_group_destroy(struct lab_cosmic_workspace_group *group); + +struct lab_cosmic_workspace *lab_cosmic_workspace_create(struct lab_cosmic_workspace_group *group); +void lab_cosmic_workspace_set_name(struct lab_cosmic_workspace *workspace, const char *name); +void lab_cosmic_workspace_set_active(struct lab_cosmic_workspace *workspace, bool enabled); +void lab_cosmic_workspace_set_urgent(struct lab_cosmic_workspace *workspace, bool enabled); +void lab_cosmic_workspace_set_hidden(struct lab_cosmic_workspace *workspace, bool enabled); +void lab_cosmic_workspace_set_coordinates(struct lab_cosmic_workspace *workspace, + struct wl_array *coordinates); +void lab_cosmic_workspace_destroy(struct lab_cosmic_workspace *workspace); + +#endif /* LABWC_PROTOCOLS_COSMIC_WORKSPACES_H */ diff --git a/protocols/cosmic-workspace-unstable-v1.xml b/protocols/cosmic-workspace-unstable-v1.xml new file mode 100644 index 00000000..76adedd9 --- /dev/null +++ b/protocols/cosmic-workspace-unstable-v1.xml @@ -0,0 +1,364 @@ + + + + Copyright © 2019 Christopher Billington + Copyright © 2020 Ilia Bozhinov + Copyright © 2022 Victoria Brekenfeld + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Workspaces, also called virtual desktops, are groups of surfaces. A + compositor with a concept of workspaces may only show some such groups of + surfaces (those of 'active' workspaces) at a time. 'Activating' a + workspace is a request for the compositor to display that workspace's + surfaces as normal, whereas the compositor may hide or otherwise + de-emphasise surfaces that are associated only with 'inactive' workspaces. + Workspaces are grouped by which sets of outputs they correspond to, and + may contain surfaces only from those outputs. In this way, it is possible + for each output to have its own set of workspaces, or for all outputs (or + any other arbitrary grouping) to share workspaces. Compositors may + optionally conceptually arrange each group of workspaces in an + N-dimensional grid. + + The purpose of this protocol is to enable the creation of taskbars and + docks by providing them with a list of workspaces and their properties, + and allowing them to activate and deactivate workspaces. + + After a client binds the zcosmic_workspace_manager_v1, each workspace will be + sent via the workspace event. + + + + + This event is emitted whenever a new workspace group has been created. + + All initial details of the workspace group (workspaces, outputs) will be + sent immediately after this event via the corresponding events in + zcosmic_workspace_group_handle_v1. + + + + + + + The client must send this request after it has finished sending other + requests. The compositor must process a series of requests preceding a + commit request atomically. + + This allows changes to the workspace properties to be seen as atomic, + even if they happen via multiple events, and even if they involve + multiple zcosmic_workspace_handle_v1 objects, for example, deactivating one + workspace and activating another. + + + + + + This event is sent after all changes in all workspace groups have been + sent. + + This allows changes to one or more zcosmic_workspace_group_handle_v1 + properties and zcosmic_workspace_handle_v1 properties to be seen as atomic, + even if they happen via multiple events. + In particular, an output moving from one workspace group to + another sends an output_enter event and an output_leave event to the two + zcosmic_workspace_group_handle_v1 objects in question. The compositor sends + the done event only after updating the output information in both + workspace groups. + + + + + + This event indicates that the compositor is done sending events to the + zcosmic_workspace_manager_v1. The server will destroy the object + immediately after sending this request, so it will become invalid and + the client should free any resources associated with it. + + + + + + Indicates the client no longer wishes to receive events for new + workspace groups. However the compositor may emit further workspace + events, until the finished event is emitted. + + The client must not send any more requests after this one. + + + + + + + A zcosmic_workspace_group_handle_v1 object represents a a workspace group + that is assigned a set of outputs and contains a number of workspaces. + + The set of outputs assigned to the workspace group is conveyed to the client via + output_enter and output_leave events, and its workspaces are conveyed with + workspace events. + + For example, a compositor which has a set of workspaces for each output may + advertise a workspace group (and its workspaces) per output, whereas a compositor + where a workspace spans all outputs may advertise a single workspace group for all + outputs. + + + + + + + + + This event advertises the capabilities supported by the compositor. If + a capability isn't supported, clients should hide or disable the UI + elements that expose this functionality. For instance, if the + compositor doesn't advertise support for creating workspaces, a button + triggering the create_workspace request should not be displayed. + + The compositor will ignore requests it doesn't support. For instance, + a compositor which doesn't advertise support for creating workspaces will ignore + create_workspace requests. + + Compositors must send this event once after creation of an + zcosmic_workspace_group_handle_v1 . When the capabilities change, compositors + must send this event again. + + The capabilities are sent as an array of 32-bit unsigned integers in + native endianness. + + + + + + + This event is emitted whenever an output is assigned to the workspace + group. + + + + + + + This event is emitted whenever an output is removed from the workspace + group. + + + + + + + This event is emitted whenever a new workspace has been created. + A workspace can only be a member of a single workspace group and cannot + be re-assigned. + + All initial details of the workspace (name, coordinates, state) will + be sent immediately after this event via the corresponding events in + zcosmic_workspace_handle_v1. + + + + + + + This event means the zcosmic_workspace_group_handle_v1 has been destroyed. + It is guaranteed there won't be any more events for this + zcosmic_workspace_group_handle_v1. The zext_workspace_group_handle_v1 becomes + inert so any requests will be ignored except the destroy request. + + The compositor must remove all workspaces belonging to a workspace group + before removing the workspace group. + + + + + + Request that the compositor create a new workspace with the given name. + + There is no guarantee that the compositor will create a new workspace, + or that the created workspace will have the provided name. + + + + + + + Destroys the zcosmic_workspace_group_handle_v1 object. + + This request should be called either when the client does not want to + use the workspace object any more or after the remove event to finalize + the destruction of the object. + + + + + + + A zcosmic_workspace_handle_v1 object represents a a workspace that handles a + group of surfaces. + + Each workspace has a name, conveyed to the client with the name event; a + list of states, conveyed to the client with the state event; and + optionally a set of coordinates, conveyed to the client with the + coordinates event. The client may request that the compositor activate or + deactivate the workspace. + + Each workspace can belong to only a single workspace group. + Depepending on the compositor policy, there might be workspaces with + the same name in different workspace groups, but these workspaces are still + separate (e.g. one of them might be active while the other is not). + + + + + This event is emitted immediately after the zcosmic_workspace_handle_v1 is + created and whenever the name of the workspace changes. + + + + + + + This event is used to organize workspaces into an N-dimensional grid + within a workspace group, and if supported, is emitted immediately after + the zcosmic_workspace_handle_v1 is created and whenever the coordinates of + the workspace change. Compositors may not send this event if they do not + conceptually arrange workspaces in this way. If compositors simply + number workspaces, without any geometric interpretation, they may send + 1D coordinates, which clients should not interpret as implying any + geometry. Sending an empty array means that the compositor no longer + orders the workspace geometrically. + + Coordinates have an arbitrary number of dimensions N with an uint32 + position along each dimension. By convention if N > 1, the first + dimension is X, the second Y, the third Z, and so on. The compositor may + chose to utilize these events for a more novel workspace layout + convention, however. No guarantee is made about the grid being filled or + bounded; there may be a workspace at coordinate 1 and another at + coordinate 1000 and none in between. Within a workspace group, however, + workspaces must have unique coordinates of equal dimensionality. + + + + + + + This event is emitted immediately after the zcosmic_workspace_handle_v1 is + created and each time the workspace state changes, either because of a + compositor action or because of a request in this protocol. + + + + + + + The different states that a workspace can have. + + + + + + + The workspace is not visible in its workspace group, and clients + attempting to visualize the compositor workspace state should not + display such workspaces. + + + + + + + + + + + + + This event advertises the capabilities supported by the compositor. If + a capability isn't supported, clients should hide or disable the UI + elements that expose this functionality. For instance, if the + compositor doesn't advertise support for removing workspaces, a button + triggering the remove request should not be displayed. + + The compositor will ignore requests it doesn't support. For instance, + a compositor which doesn't advertise support for remove will ignore + remove requests. + + Compositors must send this event once after creation of an + zcosmic_workspace_handle_v1 . When the capabilities change, compositors + must send this event again. + + The capabilities are sent as an array of 32-bit unsigned integers in + native endianness. + + + + + + + This event means the zcosmic_workspace_handle_v1 has been destroyed. It is + guaranteed there won't be any more events for this + zcosmic_workspace_handle_v1. The zext_workspace_handle_v1 becomes inert so + any requests will be ignored except the destroy request. + + + + + + Destroys the zcosmic_workspace_handle_v1 object. + + This request should be called either when the client does not want to + use the workspace object any more or after the remove event to finalize + the destruction of the object. + + + + + + Request that this workspace be activated. + + There is no guarantee the workspace will be actually activated, and + behaviour may be compositor-dependent. For example, activating a + workspace may or may not deactivate all other workspaces in the same + group. + + + + + + Request that this workspace be deactivated. + + There is no guarantee the workspace will be actually deactivated. + + + + + + Request that this workspace be removed. + + There is no guarantee the workspace will be actually removed. + + + + diff --git a/protocols/meson.build b/protocols/meson.build index 0ee40bf4..711a9ec1 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -21,6 +21,7 @@ server_protocols = [ 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', + 'cosmic-workspace-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'wlr-input-inhibitor-unstable-v1.xml', 'wlr-output-power-management-unstable-v1.xml', diff --git a/src/meson.build b/src/meson.build index 9676bec1..60769176 100644 --- a/src/meson.build +++ b/src/meson.build @@ -52,3 +52,4 @@ subdir('decorations') subdir('input') subdir('menu') subdir('ssd') +subdir('protocols') diff --git a/src/protocols/cosmic_workspaces/cosmic-workspaces.c b/src/protocols/cosmic_workspaces/cosmic-workspaces.c new file mode 100644 index 00000000..e8d4cc6a --- /dev/null +++ b/src/protocols/cosmic_workspaces/cosmic-workspaces.c @@ -0,0 +1,651 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include "common/array.h" +#include "common/mem.h" +#include "common/list.h" +#include "cosmic-workspace-unstable-v1-protocol.h" +#include "protocols/cosmic-workspaces.h" +#include "protocols/cosmic-workspaces-internal.h" + +/* + * .--------------------. + * | TODO | + * |--------------------| + * | - prevent empty | + * | done events | + * | - go through xml | + * | and verify impl | + * | - assert pub API | + * `--------------------´ + * + */ + +/* Only used within an assert() */ +#ifndef NDEBUG + #define COSMIC_WORKSPACE_V1_VERSION 1 +#endif + +/* These are just *waaay* too long */ +#define ZCOSMIC_CAP_WS_CREATE \ + ZCOSMIC_WORKSPACE_GROUP_HANDLE_V1_ZCOSMIC_WORKSPACE_GROUP_CAPABILITIES_V1_CREATE_WORKSPACE +#define ZCOSMIC_CAP_WS_ACTIVATE \ + ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_ACTIVATE +#define ZCOSMIC_CAP_WS_DEACTIVATE \ + ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_DEACTIVATE +#define ZCOSMIC_CAP_WS_REMOVE \ + ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_REMOVE + +enum workspace_state { + CW_WS_STATE_ACTIVE = 1 << 0, + CW_WS_STATE_URGENT = 1 << 1, + CW_WS_STATE_HIDDEN = 1 << 2, + + /* + * Set when creating a new workspace so we + * don't end up having to send the state twice. + */ + CW_WS_STATE_INVALID = 1 << 31, +}; + +static void +add_caps(struct wl_array *caps_arr, uint32_t caps) +{ + if (caps == CW_CAP_NONE) { + return; + } + if (caps & CW_CAP_GRP_WS_CREATE) { + array_add(caps_arr, ZCOSMIC_CAP_WS_CREATE); + } + if (caps & CW_CAP_WS_ACTIVATE) { + array_add(caps_arr, ZCOSMIC_CAP_WS_ACTIVATE); + } + if (caps & CW_CAP_WS_DEACTIVATE) { + array_add(caps_arr, ZCOSMIC_CAP_WS_DEACTIVATE); + } + if (caps & CW_CAP_WS_REMOVE) { + array_add(caps_arr, ZCOSMIC_CAP_WS_REMOVE); + } +} + +/* Workspace */ +static void +workspace_handle_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +workspace_handle_activate(struct wl_client *client, struct wl_resource *resource) +{ + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (!addon) { + /* workspace was destroyed from the compositor side */ + return; + } + struct lab_cosmic_workspace *workspace = addon->data; + transaction_add_workspace_ev(workspace, resource, CW_PENDING_WS_ACTIVATE); +} + +static void +workspace_handle_deactivate(struct wl_client *client, struct wl_resource *resource) +{ + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (!addon) { + /* Workspace was destroyed from the compositor side */ + return; + } + struct lab_cosmic_workspace *workspace = addon->data; + transaction_add_workspace_ev(workspace, resource, CW_PENDING_WS_DEACTIVATE); +} + +static void +workspace_handle_remove(struct wl_client *client, struct wl_resource *resource) +{ + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (!addon) { + /* workspace was destroyed from the compositor side */ + return; + } + struct lab_cosmic_workspace *workspace = addon->data; + transaction_add_workspace_ev(workspace, resource, CW_PENDING_WS_REMOVE); +} + +static const struct zcosmic_workspace_handle_v1_interface workspace_impl = { + .destroy = workspace_handle_destroy, + .activate = workspace_handle_activate, + .deactivate = workspace_handle_deactivate, + .remove = workspace_handle_remove, +}; + +static void +workspace_instance_resource_destroy(struct wl_resource *resource) +{ + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (addon) { + resource_addon_destroy(addon); + wl_resource_set_user_data(resource, NULL); + } + + wl_list_remove(wl_resource_get_link(resource)); +} + +static struct wl_resource * +workspace_resource_create(struct lab_cosmic_workspace *workspace, + struct wl_resource *group_resource, struct session_context *ctx) +{ + struct wl_client *client = wl_resource_get_client(group_resource); + struct wl_resource *resource = wl_resource_create(client, + &zcosmic_workspace_handle_v1_interface, + wl_resource_get_version(group_resource), 0); + if (!resource) { + wl_client_post_no_memory(client); + return NULL; + } + + struct wl_resource_addon *addon = resource_addon_create(ctx); + addon->data = workspace; + + wl_resource_set_implementation(resource, &workspace_impl, addon, + workspace_instance_resource_destroy); + + wl_list_insert(&workspace->resources, wl_resource_get_link(resource)); + return resource; +} + +/* Workspace internal helpers */ +static void +workspace_send_state(struct lab_cosmic_workspace *workspace, struct wl_resource *target) +{ + struct wl_array state; + wl_array_init(&state); + + if (workspace->state & CW_WS_STATE_ACTIVE) { + array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_ACTIVE); + } + if (workspace->state & CW_WS_STATE_URGENT) { + array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_URGENT); + } + if (workspace->state & CW_WS_STATE_HIDDEN) { + array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_HIDDEN); + } + + if (target) { + zcosmic_workspace_handle_v1_send_state(target, &state); + } else { + struct wl_resource *resource; + wl_resource_for_each(resource, &workspace->resources) { + zcosmic_workspace_handle_v1_send_state(resource, &state); + } + } + + wl_array_release(&state); +} + +static void +workspace_send_initial_state(struct lab_cosmic_workspace *workspace, struct wl_resource *resource) +{ + zcosmic_workspace_handle_v1_send_capabilities(resource, &workspace->capabilities); + if (workspace->coordinates.size > 0) { + zcosmic_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates); + } + if (workspace->name) { + zcosmic_workspace_handle_v1_send_name(resource, workspace->name); + } +} + +static void +workspace_set_state(struct lab_cosmic_workspace *workspace, + enum workspace_state state, bool enabled) +{ + if (!!(workspace->state_pending & state) == enabled) { + return; + } + + if (enabled) { + workspace->state_pending |= state; + } else { + workspace->state_pending &= ~state; + } + manager_schedule_done_event(workspace->group->manager); +} + +/* Group */ +static void +group_handle_create_workspace(struct wl_client *client, + struct wl_resource *resource, const char *name) +{ + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (!addon) { + return; + } + + struct lab_cosmic_workspace_group *group = addon->data; + transaction_add_workspace_group_ev(group, resource, CW_PENDING_WS_CREATE, name); +} + +static void +group_handle_destroy(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct zcosmic_workspace_group_handle_v1_interface group_impl = { + .create_workspace = group_handle_create_workspace, + .destroy = group_handle_destroy, +}; + +static void +group_instance_resource_destroy(struct wl_resource *resource) +{ + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (addon) { + resource_addon_destroy(addon); + wl_resource_set_user_data(resource, NULL); + } + wl_list_remove(wl_resource_get_link(resource)); +} + +static struct wl_resource * +group_resource_create(struct lab_cosmic_workspace_group *group, + struct wl_resource *manager_resource, struct session_context *ctx) +{ + struct wl_client *client = wl_resource_get_client(manager_resource); + struct wl_resource *resource = wl_resource_create(client, + &zcosmic_workspace_group_handle_v1_interface, + wl_resource_get_version(manager_resource), 0); + if (!resource) { + wl_client_post_no_memory(client); + return NULL; + } + + struct wl_resource_addon *addon = resource_addon_create(ctx); + addon->data = group; + + wl_resource_set_implementation(resource, &group_impl, addon, + group_instance_resource_destroy); + + wl_list_insert(&group->resources, wl_resource_get_link(resource)); + return resource; +} + +/* Group internal helpers */ +static void +group_send_state(struct lab_cosmic_workspace_group *group, struct wl_resource *resource) +{ + zcosmic_workspace_group_handle_v1_send_capabilities( + resource, &group->capabilities); + + group_output_send_initial_state(group, resource); +} + +/* Manager itself */ +static void +manager_handle_commit(struct wl_client *client, struct wl_resource *resource) +{ + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (!addon) { + return; + } + + struct transaction_group *trans_grp; + struct transaction_workspace *trans_ws; + struct transaction *trans, *trans_tmp; + wl_list_for_each_safe(trans, trans_tmp, &addon->ctx->transactions, link) { + switch (trans->change) { + case CW_PENDING_WS_CREATE: + trans_grp = wl_container_of(trans, trans_grp, base); + wl_signal_emit_mutable( + &trans_grp->group->events.create_workspace, + trans_grp->new_workspace_name); + free(trans_grp->new_workspace_name); + break; + case CW_PENDING_WS_ACTIVATE: + trans_ws = wl_container_of(trans, trans_ws, base); + wl_signal_emit_mutable(&trans_ws->workspace->events.activate, NULL); + break; + case CW_PENDING_WS_DEACTIVATE: + trans_ws = wl_container_of(trans, trans_ws, base); + wl_signal_emit_mutable(&trans_ws->workspace->events.deactivate, NULL); + break; + case CW_PENDING_WS_REMOVE: + trans_ws = wl_container_of(trans, trans_ws, base); + wl_signal_emit_mutable(&trans_ws->workspace->events.remove, NULL); + break; + default: + wlr_log(WLR_ERROR, "Invalid transaction state: %u", trans->change); + } + wl_list_remove(&trans->link); + free(trans); + } +} + +static void +manager_handle_stop(struct wl_client *client, struct wl_resource *resource) +{ + zcosmic_workspace_manager_v1_send_finished(resource); + wl_resource_destroy(resource); +} + +static const struct zcosmic_workspace_manager_v1_interface manager_impl = { + .commit = manager_handle_commit, + .stop = manager_handle_stop, +}; + +static void +manager_instance_resource_destroy(struct wl_resource *resource) +{ + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (addon) { + resource_addon_destroy(addon); + wl_resource_set_user_data(resource, NULL); + } + + wl_list_remove(wl_resource_get_link(resource)); +} + +static void +manager_handle_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + struct lab_cosmic_workspace_manager *manager = data; + struct wl_resource *resource = wl_resource_create(client, + &zcosmic_workspace_manager_v1_interface, + version, id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + + struct wl_resource_addon *addon = resource_addon_create(/* session context*/ NULL); + addon->data = manager; + + wl_resource_set_implementation(resource, &manager_impl, + addon, manager_instance_resource_destroy); + + wl_list_insert(&manager->resources, wl_resource_get_link(resource)); + + struct lab_cosmic_workspace *workspace; + struct lab_cosmic_workspace_group *group; + wl_list_for_each(group, &manager->groups, link) { + /* Create group resource */ + struct wl_resource *group_resource = + group_resource_create(group, resource, addon->ctx); + zcosmic_workspace_manager_v1_send_workspace_group(resource, group_resource); + group_send_state(group, group_resource); + + /* Create workspace resource */ + wl_list_for_each(workspace, &group->workspaces, link) { + struct wl_resource *workspace_resource = + workspace_resource_create(workspace, group_resource, addon->ctx); + zcosmic_workspace_group_handle_v1_send_workspace( + group_resource, workspace_resource); + workspace_send_initial_state(workspace, workspace_resource); + /* Send the current workspace state manually */ + workspace_send_state(workspace, workspace_resource); + } + } + zcosmic_workspace_manager_v1_send_done(resource); +} + +static void +manager_handle_display_destroy(struct wl_listener *listener, void *data) +{ + struct lab_cosmic_workspace_manager *manager = + wl_container_of(listener, manager, on.display_destroy); + + wl_list_remove(&manager->on.display_destroy.link); + manager->event_loop = NULL; +} + +/* Manager internal helpers */ +static void +manager_idle_send_done(void *data) +{ + struct lab_cosmic_workspace_manager *manager = data; + + struct lab_cosmic_workspace *workspace; + struct lab_cosmic_workspace_group *group; + wl_list_for_each(group, &manager->groups, link) { + wl_list_for_each(workspace, &group->workspaces, link) { + if (workspace->state != workspace->state_pending) { + workspace->state = workspace->state_pending; + workspace_send_state(workspace, /*target*/ NULL); + } + } + } + + struct wl_resource *resource; + wl_resource_for_each(resource, &manager->resources) { + zcosmic_workspace_manager_v1_send_done(resource); + } + manager->idle_source = NULL; +} + +/* Internal API */ +void +manager_schedule_done_event(struct lab_cosmic_workspace_manager *manager) +{ + if (manager->idle_source) { + return; + } + if (!manager->event_loop) { + return; + } + manager->idle_source = wl_event_loop_add_idle( + manager->event_loop, manager_idle_send_done, manager); +} + +/* Public API */ +struct lab_cosmic_workspace_manager * +lab_cosmic_workspace_manager_create(struct wl_display *display, uint32_t caps, uint32_t version) +{ + assert(version <= COSMIC_WORKSPACE_V1_VERSION); + + struct lab_cosmic_workspace_manager *manager = znew(*manager); + manager->global = wl_global_create(display, + &zcosmic_workspace_manager_v1_interface, + version, manager, manager_handle_bind); + + if (!manager->global) { + free(manager); + return NULL; + } + + manager->caps = caps; + manager->event_loop = wl_display_get_event_loop(display); + + manager->on.display_destroy.notify = manager_handle_display_destroy; + wl_display_add_destroy_listener(display, &manager->on.display_destroy); + + wl_list_init(&manager->groups); + wl_list_init(&manager->resources); + return manager; +} + +struct lab_cosmic_workspace_group * +lab_cosmic_workspace_group_create(struct lab_cosmic_workspace_manager *manager) +{ + assert(manager); + + struct lab_cosmic_workspace_group *group = znew(*group); + group->manager = manager; + + wl_array_init(&group->capabilities); + add_caps(&group->capabilities, manager->caps & CW_CAP_GRP_ALL); + + wl_list_init(&group->outputs); + wl_list_init(&group->resources); + wl_list_init(&group->workspaces); + wl_signal_init(&group->events.create_workspace); + wl_signal_init(&group->events.destroy); + + wl_list_append(&manager->groups, &group->link); + + struct wl_resource *resource, *tmp; + wl_resource_for_each_safe(resource, tmp, &manager->resources) { + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + assert(addon && addon->ctx); + struct wl_resource *group_resource = + group_resource_create(group, resource, addon->ctx); + zcosmic_workspace_manager_v1_send_workspace_group(resource, group_resource); + group_send_state(group, group_resource); + } + manager_schedule_done_event(manager); + + return group; +} + +void +lab_cosmic_workspace_group_destroy(struct lab_cosmic_workspace_group *group) +{ + if (!group) { + return; + } + wl_signal_emit_mutable(&group->events.destroy, NULL); + + struct lab_cosmic_workspace *ws, *ws_tmp; + wl_list_for_each_safe(ws, ws_tmp, &group->workspaces, link) { + lab_cosmic_workspace_destroy(ws); + } + + struct wl_resource *resource, *res_tmp; + wl_resource_for_each_safe(resource, res_tmp, &group->resources) { + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (addon) { + resource_addon_destroy(addon); + wl_resource_set_user_data(resource, NULL); + } + zcosmic_workspace_group_handle_v1_send_remove(resource); + wl_list_remove(wl_resource_get_link(resource)); + wl_list_init(wl_resource_get_link(resource)); + } + + wl_list_remove(&group->link); + wl_array_release(&group->capabilities); + free(group); +} + +struct lab_cosmic_workspace * +lab_cosmic_workspace_create(struct lab_cosmic_workspace_group *group) +{ + assert(group); + + struct lab_cosmic_workspace *workspace = znew(*workspace); + workspace->group = group; + /* + * Ensures we are sending workspace->state_pending on the done event, + * regardless if the compositor has changed any state in between here + * and the scheduled done event or not. + * + * Without this we might have to send the state twice, first here and + * then again in the scheduled done event when there were any changes. + */ + workspace->state = CW_WS_STATE_INVALID; + + wl_array_init(&workspace->capabilities); + add_caps(&workspace->capabilities, group->manager->caps & CW_CAP_WS_ALL); + + wl_list_init(&workspace->resources); + wl_array_init(&workspace->coordinates); + wl_signal_init(&workspace->events.activate); + wl_signal_init(&workspace->events.deactivate); + wl_signal_init(&workspace->events.remove); + wl_signal_init(&workspace->events.destroy); + + wl_list_append(&group->workspaces, &workspace->link); + + /* Notify clients */ + struct wl_resource *group_resource; + wl_resource_for_each(group_resource, &group->resources) { + struct wl_resource_addon *addon = wl_resource_get_user_data(group_resource); + assert(addon && addon->ctx); + struct wl_resource *workspace_resource = + workspace_resource_create(workspace, group_resource, addon->ctx); + zcosmic_workspace_group_handle_v1_send_workspace( + group_resource, workspace_resource); + workspace_send_initial_state(workspace, workspace_resource); + } + manager_schedule_done_event(group->manager); + + return workspace; +} + +void +lab_cosmic_workspace_set_name(struct lab_cosmic_workspace *workspace, const char *name) +{ + assert(workspace); + assert(name); + + if (!workspace->name || strcmp(workspace->name, name)) { + free(workspace->name); + workspace->name = xstrdup(name); + struct wl_resource *resource; + wl_resource_for_each(resource, &workspace->resources) { + zcosmic_workspace_handle_v1_send_name(resource, workspace->name); + } + } + manager_schedule_done_event(workspace->group->manager); +} + +void +lab_cosmic_workspace_set_active(struct lab_cosmic_workspace *workspace, bool enabled) +{ + workspace_set_state(workspace, CW_WS_STATE_ACTIVE, enabled); +} + +void +lab_cosmic_workspace_set_urgent(struct lab_cosmic_workspace *workspace, bool enabled) +{ + workspace_set_state(workspace, CW_WS_STATE_URGENT, enabled); +} + +void +lab_cosmic_workspace_set_hidden(struct lab_cosmic_workspace *workspace, bool enabled) +{ + workspace_set_state(workspace, CW_WS_STATE_HIDDEN, enabled); +} + +void +lab_cosmic_workspace_set_coordinates(struct lab_cosmic_workspace *workspace, + struct wl_array *coordinates) +{ + wl_array_release(&workspace->coordinates); + wl_array_init(&workspace->coordinates); + wl_array_copy(&workspace->coordinates, coordinates); + + struct wl_resource *resource; + wl_resource_for_each(resource, &workspace->resources) { + zcosmic_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates); + } + manager_schedule_done_event(workspace->group->manager); +} + +void +lab_cosmic_workspace_destroy(struct lab_cosmic_workspace *workspace) +{ + if (!workspace) { + return; + } + wl_signal_emit_mutable(&workspace->events.destroy, NULL); + + struct wl_resource *resource, *tmp; + wl_resource_for_each_safe(resource, tmp, &workspace->resources) { + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (addon) { + resource_addon_destroy(addon); + wl_resource_set_user_data(resource, NULL); + } + zcosmic_workspace_handle_v1_send_remove(resource); + wl_list_remove(wl_resource_get_link(resource)); + wl_list_init(wl_resource_get_link(resource)); + } + manager_schedule_done_event(workspace->group->manager); + + wl_list_remove(&workspace->link); + wl_array_release(&workspace->coordinates); + wl_array_release(&workspace->capabilities); + zfree(workspace->name); + free(workspace); +} diff --git a/src/protocols/cosmic_workspaces/meson.build b/src/protocols/cosmic_workspaces/meson.build new file mode 100644 index 00000000..29bb80ca --- /dev/null +++ b/src/protocols/cosmic_workspaces/meson.build @@ -0,0 +1,5 @@ +labwc_sources += files( + 'cosmic-workspaces.c', + 'transactions.c', + 'output.c', +) diff --git a/src/protocols/cosmic_workspaces/output.c b/src/protocols/cosmic_workspaces/output.c new file mode 100644 index 00000000..3abaf83a --- /dev/null +++ b/src/protocols/cosmic_workspaces/output.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include "common/mem.h" +#include "cosmic-workspace-unstable-v1-protocol.h" +#include "protocols/cosmic-workspaces.h" +#include "protocols/cosmic-workspaces-internal.h" + +struct group_output { + struct wlr_output *wlr_output; + struct lab_cosmic_workspace_group *group; + struct { + struct wl_listener group_destroy; + struct wl_listener output_bind; + struct wl_listener output_destroy; + } on; + + struct wl_list link; +}; + +/* Internal helpers */ +static void +group_output_send_event(struct wl_list *group_resources, struct wl_list *output_resources, + void (*notifier)(struct wl_resource *group, struct wl_resource *output)) +{ + struct wl_client *client; + struct wl_resource *group_resource, *output_resource; + wl_resource_for_each(group_resource, group_resources) { + client = wl_resource_get_client(group_resource); + wl_resource_for_each(output_resource, output_resources) { + if (wl_resource_get_client(output_resource) == client) { + notifier(group_resource, output_resource); + } + } + } +} + +static void +group_output_destroy(struct group_output *group_output) +{ + group_output_send_event( + &group_output->group->resources, + &group_output->wlr_output->resources, + zcosmic_workspace_group_handle_v1_send_output_leave); + + manager_schedule_done_event(group_output->group->manager); + + wl_list_remove(&group_output->link); + wl_list_remove(&group_output->on.group_destroy.link); + wl_list_remove(&group_output->on.output_bind.link); + wl_list_remove(&group_output->on.output_destroy.link); + free(group_output); +} + +/* Event handlers */ +static void +handle_output_bind(struct wl_listener *listener, void *data) +{ + struct group_output *group_output = + wl_container_of(listener, group_output, on.output_bind); + + struct wlr_output_event_bind *event = data; + struct wl_client *client = wl_resource_get_client(event->resource); + + bool sent = false; + struct wl_resource *group_resource; + wl_resource_for_each(group_resource, &group_output->group->resources) { + if (wl_resource_get_client(group_resource) == client) { + zcosmic_workspace_group_handle_v1_send_output_enter( + group_resource, event->resource); + sent = true; + } + } + if (!sent) { + return; + } + + struct wl_resource *manager_resource; + struct wl_list *manager_resources = &group_output->group->manager->resources; + wl_resource_for_each(manager_resource, manager_resources) { + if (wl_resource_get_client(manager_resource) == client) { + zcosmic_workspace_manager_v1_send_done(manager_resource); + } + } +} + +static void +handle_output_destroy(struct wl_listener *listener, void *data) +{ + struct group_output *group_output = + wl_container_of(listener, group_output, on.output_destroy); + group_output_destroy(group_output); +} + +static void +handle_group_destroy(struct wl_listener *listener, void *data) +{ + struct group_output *group_output = + wl_container_of(listener, group_output, on.group_destroy); + group_output_destroy(group_output); +} + +/* Internal API*/ +void +group_output_send_initial_state(struct lab_cosmic_workspace_group *group, + struct wl_resource *group_resource) +{ + struct group_output *group_output; + struct wl_resource *output_resource; + struct wl_client *client = wl_resource_get_client(group_resource); + wl_list_for_each(group_output, &group->outputs, link) { + wl_resource_for_each(output_resource, &group_output->wlr_output->resources) { + if (wl_resource_get_client(output_resource) == client) { + zcosmic_workspace_group_handle_v1_send_output_enter( + group_resource, output_resource); + } + } + } +} + +/* Public API */ +void +lab_cosmic_workspace_group_output_enter(struct lab_cosmic_workspace_group *group, + struct wlr_output *wlr_output) +{ + struct group_output *group_output; + wl_list_for_each(group_output, &group->outputs, link) { + if (group_output->wlr_output == wlr_output) { + return; + } + } + group_output = znew(*group_output); + group_output->wlr_output = wlr_output; + group_output->group = group; + + group_output->on.group_destroy.notify = handle_group_destroy; + wl_signal_add(&group->events.destroy, &group_output->on.group_destroy); + + group_output->on.output_bind.notify = handle_output_bind; + wl_signal_add(&wlr_output->events.bind, &group_output->on.output_bind); + + group_output->on.output_destroy.notify = handle_output_destroy; + wl_signal_add(&wlr_output->events.destroy, &group_output->on.output_destroy); + + wl_list_insert(&group->outputs, &group_output->link); + + group_output_send_event( + &group_output->group->resources, + &group_output->wlr_output->resources, + zcosmic_workspace_group_handle_v1_send_output_enter); + + manager_schedule_done_event(group->manager); +} + +void +lab_cosmic_workspace_group_output_leave(struct lab_cosmic_workspace_group *group, + struct wlr_output *wlr_output) +{ + struct group_output *tmp; + struct group_output *group_output = NULL; + wl_list_for_each(tmp, &group->outputs, link) { + if (tmp->wlr_output == wlr_output) { + group_output = tmp; + break; + } + } + if (!group_output) { + wlr_log(WLR_ERROR, "output %s was never entered", wlr_output->name); + return; + } + + group_output_destroy(group_output); +} diff --git a/src/protocols/cosmic_workspaces/transactions.c b/src/protocols/cosmic_workspaces/transactions.c new file mode 100644 index 00000000..571b2c11 --- /dev/null +++ b/src/protocols/cosmic_workspaces/transactions.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include +#include "common/list.h" +#include "common/mem.h" +#include "protocols/cosmic-workspaces-internal.h" + +static void +transactions_destroy(struct wl_list *list) +{ + struct transaction_group *group; + struct transaction *trans, *trans_tmp; + wl_list_for_each_safe(trans, trans_tmp, list, link) { + if (trans->change == CW_PENDING_WS_CREATE) { + group = wl_container_of(trans, group, base); + free(group->new_workspace_name); + } + wl_list_remove(&trans->link); + free(trans); + } +} + +void +resource_addon_destroy(struct wl_resource_addon *addon) +{ + assert(addon); + assert(addon->ctx); + + addon->ctx->ref_count--; + assert(addon->ctx->ref_count >= 0); + + wlr_log(WLR_DEBUG, "New refcount for session %p: %d", + addon->ctx, addon->ctx->ref_count); + if (!addon->ctx->ref_count) { + wlr_log(WLR_DEBUG, "Destroying session context"); + transactions_destroy(&addon->ctx->transactions); + free(addon->ctx); + } + + free(addon); +} + +struct wl_resource_addon * +resource_addon_create(struct session_context *ctx) +{ + struct wl_resource_addon *addon = znew(*addon); + if (!ctx) { + ctx = znew(*ctx); + wl_list_init(&ctx->transactions); + } + addon->ctx = ctx; + addon->ctx->ref_count++; + return addon; +} + +void +transaction_add_workspace_ev(struct lab_cosmic_workspace *ws, + struct wl_resource *resource, enum pending_change change) +{ + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (!addon) { + wlr_log(WLR_ERROR, "Failed to find manager addon for workspace transaction"); + return; + } + + assert(change != CW_PENDING_WS_CREATE); + + struct transaction_workspace *trans_ws = znew(*trans_ws); + trans_ws->workspace = ws; + trans_ws->base.change = change; + wl_list_append(&addon->ctx->transactions, &trans_ws->base.link); +} + +void +transaction_add_workspace_group_ev(struct lab_cosmic_workspace_group *group, + struct wl_resource *resource, enum pending_change change, + const char *new_workspace_name) +{ + struct wl_resource_addon *addon = wl_resource_get_user_data(resource); + if (!addon) { + wlr_log(WLR_ERROR, "Failed to find manager addon for group transaction"); + return; + } + + assert(change == CW_PENDING_WS_CREATE); + + struct transaction_group *trans_grp = znew(*trans_grp); + trans_grp->group = group; + trans_grp->base.change = change; + trans_grp->new_workspace_name = xstrdup(new_workspace_name); + wl_list_append(&addon->ctx->transactions, &trans_grp->base.link); +} diff --git a/src/protocols/meson.build b/src/protocols/meson.build new file mode 100644 index 00000000..d4412119 --- /dev/null +++ b/src/protocols/meson.build @@ -0,0 +1 @@ +subdir('cosmic_workspaces') From 904e0d2e97a24b1a79ed5db3b06278021efb836d Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 24 Jul 2024 01:04:44 +0200 Subject: [PATCH 088/232] cosmic-workspaces: labwc integration --- include/labwc.h | 5 +++++ include/workspaces.h | 8 ++++++++ src/output.c | 8 ++++++++ src/workspaces.c | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+) diff --git a/include/labwc.h b/include/labwc.h index 3439a099..9b6d1a21 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -301,6 +301,11 @@ struct server { struct wl_list all; /* struct workspace.link */ struct workspace *current; struct workspace *last; + struct lab_cosmic_workspace_manager *cosmic_manager; + struct lab_cosmic_workspace_group *cosmic_group; + struct { + struct wl_listener layout_output_added; + } on; } workspaces; struct wl_list outputs; diff --git a/include/workspaces.h b/include/workspaces.h index a558a00e..9bae5190 100644 --- a/include/workspaces.h +++ b/include/workspaces.h @@ -4,6 +4,7 @@ #include #include +#include struct seat; struct server; @@ -19,6 +20,13 @@ struct workspace { char *name; struct wlr_scene_tree *tree; + + struct lab_cosmic_workspace *cosmic_workspace; + struct { + struct wl_listener activate; + struct wl_listener deactivate; + struct wl_listener remove; + } on; }; void workspaces_init(struct server *server); diff --git a/src/output.c b/src/output.c index 8ba8225a..d92f45b0 100644 --- a/src/output.c +++ b/src/output.c @@ -26,6 +26,7 @@ #include "node.h" #include "output-state.h" #include "output-virtual.h" +#include "protocols/cosmic-workspaces.h" #include "regions.h" #include "view.h" #include "xwayland.h" @@ -276,6 +277,9 @@ add_output_to_layout(struct server *server, struct output *output) wlr_scene_output_layout_add_output(server->scene_layout, layout_output, output->scene_output); } + + lab_cosmic_workspace_group_output_enter( + server->workspaces.cosmic_group, output->wlr_output); } static void @@ -567,6 +571,10 @@ output_config_apply(struct server *server, if (need_to_remove) { regions_evacuate_output(output); + + lab_cosmic_workspace_group_output_leave( + server->workspaces.cosmic_group, output->wlr_output); + /* * At time of writing, wlr_output_layout_remove() * indirectly destroys the wlr_scene_output, but diff --git a/src/workspaces.c b/src/workspaces.c index 8e9b912f..41336ce1 100644 --- a/src/workspaces.c +++ b/src/workspaces.c @@ -15,10 +15,13 @@ #include "common/mem.h" #include "input/keyboard.h" #include "labwc.h" +#include "protocols/cosmic-workspaces.h" #include "view.h" #include "workspaces.h" #include "xwayland.h" +#define COSMIC_WORKSPACES_VERSION 1 + /* Internal helpers */ static size_t parse_workspace_index(const char *name) @@ -172,6 +175,15 @@ _osd_update(struct server *server) } } +/* cosmic workspace handlers */ +static void +handle_workspace_activate(struct wl_listener *listener, void *data) +{ + struct workspace *workspace = wl_container_of(listener, workspace, on.activate); + workspaces_switch_to(workspace, /* update_focus */ true); + wlr_log(WLR_INFO, "activating workspace %s", workspace->name); +} + /* Internal API */ static void add_workspace(struct server *server, const char *name) @@ -186,6 +198,15 @@ add_workspace(struct server *server, const char *name) } else { wlr_scene_node_set_enabled(&workspace->tree->node, false); } + + bool active = server->workspaces.current == workspace; + workspace->cosmic_workspace = lab_cosmic_workspace_create(server->workspaces.cosmic_group); + lab_cosmic_workspace_set_name(workspace->cosmic_workspace, name); + lab_cosmic_workspace_set_active(workspace->cosmic_workspace, active); + lab_cosmic_workspace_set_hidden(workspace->cosmic_workspace, !active); + + workspace->on.activate.notify = handle_workspace_activate; + wl_signal_add(&workspace->cosmic_workspace->events.activate, &workspace->on.activate); } static struct workspace * @@ -260,6 +281,13 @@ _osd_show(struct server *server) void workspaces_init(struct server *server) { + server->workspaces.cosmic_manager = lab_cosmic_workspace_manager_create( + server->wl_display, /* capabilities */ CW_CAP_WS_ACTIVATE, + COSMIC_WORKSPACES_VERSION); + + server->workspaces.cosmic_group = lab_cosmic_workspace_group_create( + server->workspaces.cosmic_manager); + wl_list_init(&server->workspaces.all); struct workspace *conf; @@ -286,6 +314,11 @@ workspaces_switch_to(struct workspace *target, bool update_focus) wlr_scene_node_set_enabled( &server->workspaces.current->tree->node, false); + lab_cosmic_workspace_set_active( + server->workspaces.current->cosmic_workspace, false); + lab_cosmic_workspace_set_hidden( + server->workspaces.current->cosmic_workspace, true); + /* Move Omnipresent views to new workspace */ struct view *view; enum lab_view_criteria criteria = @@ -331,6 +364,9 @@ workspaces_switch_to(struct workspace *target, bool update_focus) /* Ensure that only currently visible fullscreen windows hide the top layer */ desktop_update_top_layer_visiblity(server); + + lab_cosmic_workspace_set_active(target->cosmic_workspace, true); + lab_cosmic_workspace_set_hidden(target->cosmic_workspace, false); } void @@ -395,6 +431,8 @@ destroy_workspace(struct workspace *workspace) wlr_scene_node_destroy(&workspace->tree->node); zfree(workspace->name); wl_list_remove(&workspace->link); + + lab_cosmic_workspace_destroy(workspace->cosmic_workspace); free(workspace); } @@ -429,6 +467,8 @@ workspaces_reconfigure(struct server *server) actual_workspace->name, configured_workspace->name); free(actual_workspace->name); actual_workspace->name = xstrdup(configured_workspace->name); + lab_cosmic_workspace_set_name( + actual_workspace->cosmic_workspace, actual_workspace->name); } actual_workspace_link = actual_workspace_link->next; } From 4bda13d870b900a4a908c06f2207275664497498 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Sat, 3 Aug 2024 05:30:02 +0200 Subject: [PATCH 089/232] CI: increase speed of codestyle check by running up to nproc ones in parallel --- scripts/check | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/scripts/check b/scripts/check index 7b8c6255..9e108666 100755 --- a/scripts/check +++ b/scripts/check @@ -19,14 +19,10 @@ run_checks () { return $? fi - find src/ include/ \( -name "*.c" -o -name "*.h" \) -type f | - { - errors=0 - while IFS= read -r file; do - run_checkpatch "$file" || errors=1 - done - return ${errors} - } + find src/ include/ \( -name "*.c" -o -name "*.h" \) -type f -print0 | + nice xargs -0 --max-args 1 --max-procs $(nproc) \ + scripts/checkpatch.pl --terse --no-tree --strict --file + return $? } main () { From 1a339f9c7e2d68d4bf7dad91a368984a56ee37ce Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Fri, 2 Aug 2024 20:18:21 -0400 Subject: [PATCH 090/232] xdg: account for drag resistance in do_late_positioning() The position where the view should be anchored can now be slightly different from the current cursor position. Addresses a TODO from #2009. --- include/labwc.h | 6 +++++- src/interactive.c | 11 ++++++----- src/xdg.c | 13 +++++++------ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/include/labwc.h b/include/labwc.h index 564f668c..28aeebca 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -503,8 +503,12 @@ void seat_output_layout_changed(struct seat *seat); * * geometry->{width,height} are provided by the caller. * geometry->{x,y} are computed by this function. + * + * @note When drag-resistance is used, cursor_x/y should be the original + * cursor position when the button was pressed. */ -void interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry); +void interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry, + int cursor_x, int cursor_y); /** * interactive_move_tiled_view_to() - Un-tile the tiled/maximized view at the diff --git a/src/interactive.c b/src/interactive.c index bd9745a0..d8c2f342 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -23,12 +23,12 @@ max_move_scale(double pos_cursor, double pos_current, } void -interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry) +interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry, + int cursor_x, int cursor_y) { - struct wlr_cursor *cursor = view->server->seat.cursor; - geometry->x = max_move_scale(cursor->x, view->current.x, + geometry->x = max_move_scale(cursor_x, view->current.x, view->current.width, geometry->width); - geometry->y = max_move_scale(cursor->y, view->current.y, + geometry->y = max_move_scale(cursor_y, view->current.y, view->current.height, geometry->height); } @@ -113,7 +113,8 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) geometry.width = view->natural_geometry.width; geometry.height = view->natural_geometry.height; if (!wlr_box_empty(&geometry)) { - interactive_anchor_to_cursor(view, &geometry); + interactive_anchor_to_cursor(view, &geometry, + seat->cursor->x, seat->cursor->y); } /* diff --git a/src/xdg.c b/src/xdg.c index 33d26645..ad0e19c1 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -93,13 +93,14 @@ do_late_positioning(struct view *view) struct server *server = view->server; if (server->input_mode == LAB_INPUT_STATE_MOVE && view == server->grabbed_view) { - /* Keep view underneath cursor */ - /* TODO: resistance is not considered */ - interactive_anchor_to_cursor(view, &view->pending); - /* Update grab offsets */ - server->grab_x = server->seat.cursor->x; - server->grab_y = server->seat.cursor->y; + /* Anchor view to original grab position */ + interactive_anchor_to_cursor(view, &view->pending, + server->grab_x, server->grab_y); + /* Next update grab offsets */ server->grab_box = view->pending; + /* Finally, move by same distance cursor has moved */ + view->pending.x += server->seat.cursor->x - server->grab_x; + view->pending.y += server->seat.cursor->y - server->grab_y; } else { /* TODO: smart placement? */ view_compute_centered_position(view, NULL, From f14f006cbab6529db5b5c036b056cd6dcb7fec12 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:18:00 +0200 Subject: [PATCH 091/232] CI: Install libwlroots-0.18-dev for the Debian runner --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 88f83f52..b00ec7a8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -81,6 +81,7 @@ jobs: apt-get upgrade -y apt-get install -y git gcc clang gdb xwayland apt-get build-dep -y labwc + apt-get install libwlroots-0.18-dev - name: Install FreeBSD dependencies if: matrix.name == 'FreeBSD' From 0f6d5cc26a9d76954f995b82addfd5e9aac963b9 Mon Sep 17 00:00:00 2001 From: John Lindgren Date: Thu, 1 Aug 2024 22:15:27 -0400 Subject: [PATCH 092/232] view: stay fullscreen when view's output is disconnected I intended to fix this quite some time ago but didn't get around to it. I don't think there's any good reason why we need to un-fullscreen a view when its output is disconnected. We can handle it the same as a maximized view, and move it to a new output (remaining fullscreen) or, if all outputs are disconnected, just leave it as-is. This is helpful for a media-center use-case, where you have just one view (e.g. Kodi) fullscreen all the time, but the TV might appear to be disconnected if you switch it to a different source. Tested with a couple different scenarios: 1. Single output disconnected and re-connected: view stayed fullscreen. 2. Secondary output disconnected: view stayed fullscreen but moved to the primary output, and the layer-shell panel on that output was hidden as expected. When the secondary output was re-connected, the view was moved back (still fullscreen) and the panel on the primary appeared again. Fixes: #864 --- src/view.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/view.c b/src/view.c index 8dd5e796..d6668d8a 100644 --- a/src/view.c +++ b/src/view.c @@ -349,7 +349,6 @@ static bool view_discover_output(struct view *view, struct wlr_box *geometry) { assert(view); - assert(!view->fullscreen); if (!geometry) { geometry = &view->current; @@ -362,6 +361,10 @@ view_discover_output(struct view *view, struct wlr_box *geometry) if (output && output != view->output) { view->output = output; + /* Show fullscreen views above top-layer */ + if (view->fullscreen) { + desktop_update_top_layer_visiblity(view->server); + } return true; } @@ -409,12 +412,15 @@ void view_set_output(struct view *view, struct output *output) { assert(view); - assert(!view->fullscreen); if (!output_is_usable(output)) { wlr_log(WLR_ERROR, "invalid output set for view"); return; } view->output = output; + /* Show fullscreen views above top-layer */ + if (view->fullscreen) { + desktop_update_top_layer_visiblity(view->server); + } } void @@ -1701,23 +1707,14 @@ view_adjust_for_layout_change(struct view *view) { assert(view); - bool was_fullscreen = view->fullscreen; bool is_floating = view_is_floating(view); + bool use_natural = false; if (!output_is_usable(view->output)) { /* A view losing an output should have a last-layout geometry */ update_last_layout_geometry(view); - - /* Exit fullscreen and re-assess floating status */ - if (was_fullscreen) { - set_fullscreen(view, false); - is_floating = view_is_floating(view); - } } - /* Restore any full-screen window to natural geometry */ - bool use_natural = was_fullscreen; - /* Capture a pointer to the last-layout geometry (only if valid) */ struct wlr_box *last_geometry = NULL; if (last_layout_geometry_is_valid(view)) { @@ -1757,8 +1754,8 @@ view_adjust_for_layout_change(struct view *view) view_apply_special_geometry(view); } else if (use_natural) { /* - * Move the window to its natural location, either because it - * was fullscreen or we are trying to restore a prior layout. + * Move the window to its natural location, because + * we are trying to restore a prior layout. */ view_apply_natural_geometry(view); } else { @@ -1788,11 +1785,6 @@ void view_on_output_destroy(struct view *view) { assert(view); - /* - * This is the only time we modify view->output for a fullscreen - * view. We expect view_adjust_for_layout_change() to be called - * shortly afterward, which will exit fullscreen. - */ view->output = NULL; } @@ -2194,9 +2186,6 @@ void view_move_to_output(struct view *view, struct output *output) { assert(view); - if (view->fullscreen) { - return; - } view_invalidate_last_layout_geometry(view); view_set_output(view, output); @@ -2206,6 +2195,8 @@ view_move_to_output(struct view *view, struct output *output) view->pending.y = output_area.y; view_place_by_policy(view, /* allow_cursor */ false, rc.placement_policy); + } else if (view->fullscreen) { + view_apply_fullscreen_geometry(view); } else if (view->maximized != VIEW_AXIS_NONE) { view_apply_maximized_geometry(view); } else if (view->tiled) { From 6dd0d69889bfdd01df2c0ff28aadd46de8ff5fa5 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Fri, 2 Aug 2024 14:05:18 +0200 Subject: [PATCH 093/232] docs: integrate onRelease into keybind --- docs/labwc-config.5.scd | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index cfd46316..ee04c6cb 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -499,7 +499,7 @@ extending outward from the snapped edge. Stores the keyboard layout either globally or per window and restores it when switching back to the window. Default is global. -** +** Define a *key* binding in the format *modifier-key*, where supported modifiers are: - S (shift) @@ -525,8 +525,8 @@ extending outward from the snapped edge. If set to "no" (or is absent) the keybind will be layout agnostic. Default is no. -** - *onRelease*, when yes, fires the keybind action when the key or key + *onRelease* [yes|no] + When yes, fires the keybind action when the key or key combination is released, rather than first pressed. This is useful to bind actions to only modifier keys, where the action should fire when the modifier is used without another key. Default is no. @@ -535,7 +535,6 @@ extending outward from the snapped edge. pressed & released, without interference from other multi-key combinations that include the super key: - ``` From 116382fd896e82b9a1823143519b7cbee208c10b Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Fri, 2 Aug 2024 14:08:19 +0200 Subject: [PATCH 094/232] keybind: implement allowWhenLocked fixes #2034 see https://github.com/swaywm/sway/blob/bc258a3be2f946c1c93bcbe40735b2db068e0ea8/sway/sway.5.scd?plain=1#L409 --- docs/labwc-config.5.scd | 5 ++++- include/config/keybind.h | 1 + src/config/rcxml.c | 2 ++ src/input/keyboard.c | 29 ++++++++++++++--------------- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index ee04c6cb..df2ecbe1 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -499,7 +499,7 @@ extending outward from the snapped edge. Stores the keyboard layout either globally or per window and restores it when switching back to the window. Default is global. -** +** Define a *key* binding in the format *modifier-key*, where supported modifiers are: - S (shift) @@ -541,6 +541,9 @@ extending outward from the snapped edge. ``` + *allowWhenLocked* [yes|no] + Make this keybind work even if the screen is locked. Default is no. + ** Keybind action. See labwc-actions(5). diff --git a/include/config/keybind.h b/include/config/keybind.h index fc573316..36d81373 100644 --- a/include/config/keybind.h +++ b/include/config/keybind.h @@ -18,6 +18,7 @@ struct keybind { xkb_keycode_t keycodes[MAX_KEYCODES]; size_t keycodes_len; int keycodes_layout; + bool allow_when_locked; struct wl_list actions; /* struct action.link */ struct wl_list link; /* struct rcxml.keybinds */ bool on_release; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 74388b10..aa0bface 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -407,6 +407,8 @@ fill_keybind(char *nodename, char *content) set_bool(content, ¤t_keybind->on_release); } else if (!strcasecmp(nodename, "layoutDependent")) { set_bool(content, ¤t_keybind->use_syms_only); + } else if (!strcasecmp(nodename, "allowWhenLocked")) { + set_bool(content, ¤t_keybind->allow_when_locked); } else if (!strcmp(nodename, "name.action")) { current_keybind_action = action_create(content); if (current_keybind_action) { diff --git a/src/input/keyboard.c b/src/input/keyboard.c index a1979aaa..4188a826 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -449,6 +449,7 @@ handle_compositor_keybindings(struct keyboard *keyboard, struct server *server = seat->server; struct wlr_keyboard *wlr_keyboard = keyboard->wlr_keyboard; struct keyinfo keyinfo = get_keyinfo(wlr_keyboard, event->keycode); + bool locked = seat->server->session_lock_manager->locked; key_state_set_pressed(event->keycode, event->state == WL_KEYBOARD_KEY_STATE_PRESSED, @@ -457,7 +458,7 @@ handle_compositor_keybindings(struct keyboard *keyboard, if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { if (cur_keybind && cur_keybind->on_release) { key_state_bound_key_remove(event->keycode); - if (seat->server->session_lock_manager->locked) { + if (locked && !cur_keybind->allow_when_locked) { cur_keybind = NULL; return true; } @@ -479,27 +480,25 @@ handle_compositor_keybindings(struct keyboard *keyboard, * It's important to do this after key_state_set_pressed() to ensure * _all_ key press/releases are registered */ - if (seat->server->session_lock_manager->locked) { - return false; - } + if (!locked) { + if (server->input_mode == LAB_INPUT_STATE_MENU) { + key_state_store_pressed_key_as_bound(event->keycode); + handle_menu_keys(server, &keyinfo.translated); + return true; + } - if (server->input_mode == LAB_INPUT_STATE_MENU) { - key_state_store_pressed_key_as_bound(event->keycode); - handle_menu_keys(server, &keyinfo.translated); - return true; - } - - if (server->osd_state.cycle_view) { - key_state_store_pressed_key_as_bound(event->keycode); - handle_cycle_view_key(server, &keyinfo); - return true; + if (server->osd_state.cycle_view) { + key_state_store_pressed_key_as_bound(event->keycode); + handle_cycle_view_key(server, &keyinfo); + return true; + } } /* * Handle compositor keybinds */ cur_keybind = match_keybinding(server, &keyinfo, keyboard->is_virtual); - if (cur_keybind) { + if (cur_keybind && (!locked || cur_keybind->allow_when_locked)) { /* * Update key-state before action_run() because the action * might lead to seat_focus() in which case we pass the From 19f0b769de7af7399b54a12964f01acc6bc0bbab Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Tue, 6 Aug 2024 19:49:44 +0100 Subject: [PATCH 095/232] labwc-config(5): minor refactor to fix pandoc rendering --- docs/labwc-config.5.scd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index df2ecbe1..986194d3 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -525,6 +525,9 @@ extending outward from the snapped edge. If set to "no" (or is absent) the keybind will be layout agnostic. Default is no. + *allowWhenLocked* [yes|no] + Make this keybind work even if the screen is locked. Default is no. + *onRelease* [yes|no] When yes, fires the keybind action when the key or key combination is released, rather than first pressed. This is useful to @@ -541,9 +544,6 @@ extending outward from the snapped edge. ``` - *allowWhenLocked* [yes|no] - Make this keybind work even if the screen is locked. Default is no. - ** Keybind action. See labwc-actions(5). From 433a4509af814c9ae92c71596545efbe12acc29c Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Tue, 6 Aug 2024 22:23:10 +0200 Subject: [PATCH 096/232] tearing: add fullscreen options (#1941) Co-authored-by: Consolatis <35009135+Consolatis@users.noreply.github.com> --- docs/labwc-actions.5.scd | 7 ++++++- docs/labwc-config.5.scd | 15 ++++++++++++--- include/config/rcxml.h | 9 ++++++++- include/labwc.h | 1 + include/view.h | 7 +++++++ src/action.c | 19 ++++++++++++++++--- src/config/rcxml.c | 16 +++++++++++++++- src/output.c | 40 +++++++++++++++++++++++++++++++--------- src/tearing.c | 11 +++++++++-- 9 files changed, 105 insertions(+), 20 deletions(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index 9ccfb798..cb6fac63 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -187,7 +187,12 @@ Actions are used in menus and keyboard/mouse bindings. original window. There can be multiple windows with this mode set. ** - Toggles tearing for the focused window. + Toggles tearing for the focused window between enabled and disabled. + This overrides the preference (tearing hint) from the focused window. + + Requires the config option 'allowTearing'. When 'allowTearing' is set + to 'fullscreen' or 'fullscreenForced', tearing will still only be + enabled if the active window is in fullscreen mode. ** Give focus to topmost window on given output and warp the cursor diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 986194d3..55956db4 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -185,9 +185,18 @@ this is for compatibility with Openbox. *fullscreen* enables adaptive sync whenever a window is in fullscreen mode. -** [yes|no] - Allow tearing, if requested by the active window, to reduce input lag. - Default is no. +** [yes|no|fullscreen|fullscreenForced] + Allow tearing to reduce input lag. Default is no. + + *yes* allows tearing if requested by the active window. + + *fullscreen* allows tearing if requested by the active window, but + only when the window is in fullscreen mode. + + *fullscreenForced* enables tearing whenever the active window is in + fullscreen mode, whether or not the application has requested tearing. + + Use the *ToggleTearing* action for forcefully enable tearing. Note: Enabling this option with atomic mode setting is experimental. If you experience undesirable side effects when tearing is allowed, diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 62fb5e57..c49bafd2 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -30,6 +30,13 @@ enum adaptive_sync_mode { LAB_ADAPTIVE_SYNC_FULLSCREEN, }; +enum tearing_mode { + LAB_TEARING_DISABLED = 0, + LAB_TEARING_ENABLED, + LAB_TEARING_FULLSCREEN, + LAB_TEARING_FULLSCREEN_FORCED, +}; + enum tiling_events_mode { LAB_TILING_EVENTS_NEVER = 0, LAB_TILING_EVENTS_REGION = 1 << 0, @@ -54,7 +61,7 @@ struct rcxml { bool xdg_shell_server_side_deco; int gap; enum adaptive_sync_mode adaptive_sync; - bool allow_tearing; + enum tearing_mode allow_tearing; bool reuse_output_mode; enum view_placement_policy placement_policy; bool xwayland_persistence; diff --git a/include/labwc.h b/include/labwc.h index 28aeebca..466a90d3 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -535,6 +535,7 @@ struct output *output_nearest_to_cursor(struct server *server); bool output_is_usable(struct output *output); void output_update_usable_area(struct output *output); void output_update_all_usable_areas(struct server *server, bool layout_changed); +bool output_get_tearing_allowance(struct output *output); struct wlr_box output_usable_area_in_layout_coords(struct output *output); struct wlr_box output_usable_area_scaled(struct output *output); void handle_output_power_manager_set_mode(struct wl_listener *listener, diff --git a/include/view.h b/include/view.h index 9dc70aa8..0fde958e 100644 --- a/include/view.h +++ b/include/view.h @@ -30,6 +30,12 @@ enum ssd_preference { LAB_SSD_PREF_SERVER, }; +enum three_state { + LAB_STATE_UNSPECIFIED = 0, + LAB_STATE_ENABLED, + LAB_STATE_DISABLED +}; + /** * Directions in which a view can be maximized. "None" is used * internally to mean "not maximized" but is not valid in rc.xml. @@ -187,6 +193,7 @@ struct view { enum view_axis maximized; bool fullscreen; bool tearing_hint; + enum three_state force_tearing; bool visible_on_all_workspaces; enum view_edge tiled; uint32_t edges_visible; /* enum wlr_edges bitset */ diff --git a/src/action.c b/src/action.c index 0aa45d7a..69a317bb 100644 --- a/src/action.c +++ b/src/action.c @@ -1109,9 +1109,22 @@ actions_run(struct view *activator, struct server *server, break; case ACTION_TYPE_TOGGLE_TEARING: if (view) { - view->tearing_hint = !view->tearing_hint; - wlr_log(WLR_DEBUG, "tearing %sabled", - view->tearing_hint ? "en" : "dis"); + switch (view->force_tearing) { + case LAB_STATE_UNSPECIFIED: + view->force_tearing = + output_get_tearing_allowance(view->output) + ? LAB_STATE_DISABLED : LAB_STATE_ENABLED; + break; + case LAB_STATE_DISABLED: + view->force_tearing = LAB_STATE_ENABLED; + break; + case LAB_STATE_ENABLED: + view->force_tearing = LAB_STATE_DISABLED; + break; + } + wlr_log(WLR_ERROR, "force tearing %sabled", + view->force_tearing == LAB_STATE_ENABLED + ? "en" : "dis"); } break; case ACTION_TYPE_TOGGLE_SHADE: diff --git a/src/config/rcxml.c b/src/config/rcxml.c index aa0bface..0227911b 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -772,6 +772,20 @@ set_adaptive_sync_mode(const char *str, enum adaptive_sync_mode *variable) } } +static void +set_tearing_mode(const char *str, enum tearing_mode *variable) +{ + if (!strcasecmp(str, "fullscreen")) { + *variable = LAB_TEARING_FULLSCREEN; + } else if (!strcasecmp(str, "fullscreenForced")) { + *variable = LAB_TEARING_FULLSCREEN_FORCED; + } else if (parse_bool(str, -1) == 1) { + *variable = LAB_TEARING_ENABLED; + } else { + *variable = LAB_TEARING_DISABLED; + } +} + static void entry(xmlNode *node, char *nodename, char *content) { @@ -886,7 +900,7 @@ entry(xmlNode *node, char *nodename, char *content) } else if (!strcasecmp(nodename, "adaptiveSync.core")) { set_adaptive_sync_mode(content, &rc.adaptive_sync); } else if (!strcasecmp(nodename, "allowTearing.core")) { - set_bool(content, &rc.allow_tearing); + set_tearing_mode(content, &rc.allow_tearing); } else if (!strcasecmp(nodename, "reuseOutputMode.core")) { set_bool(content, &rc.reuse_output_mode); } else if (!strcmp(nodename, "policy.placement")) { diff --git a/src/output.c b/src/output.c index 8ba8225a..b9ac9855 100644 --- a/src/output.c +++ b/src/output.c @@ -38,28 +38,50 @@ get_tearing_retry_count(struct output *output) return refresh > 0 ? refresh / 500 : 120; } -static bool -get_tearing_preference(struct output *output) +bool +output_get_tearing_allowance(struct output *output) { struct server *server = output->server; - /* Never allow tearing when disabled */ + /* never allow tearing when disabled */ if (!rc.allow_tearing) { return false; } - /* Tearing is only allowed for the output with the active view */ - if (!server->active_view || server->active_view->output != output) { + struct view *view = server->active_view; + + /* tearing is only allowed for the output with the active view */ + if (!view || view->output != output) { return false; } - /* Tearing should not have failed too many times */ + /* tearing should not have failed too many times */ if (output->nr_tearing_failures >= get_tearing_retry_count(output)) { return false; } - /* If the active view requests tearing, or it is toggled on with action, allow it */ - return server->active_view->tearing_hint; + /* allow tearing for any window when requested or forced */ + if (rc.allow_tearing == LAB_TEARING_ENABLED) { + if (view->force_tearing == LAB_STATE_UNSPECIFIED) { + return view->tearing_hint; + } else { + return view->force_tearing == LAB_STATE_ENABLED; + } + } + + /* remaining tearing options apply only to full-screen windows */ + if (!view->fullscreen) { + return false; + } + + if (view->force_tearing == LAB_STATE_UNSPECIFIED) { + /* honor the tearing hint or the fullscreen-force preference */ + return view->tearing_hint || + rc.allow_tearing == LAB_TEARING_FULLSCREEN_FORCED; + } + + /* honor tearing as requested by action */ + return view->force_tearing == LAB_STATE_ENABLED; } static void @@ -128,7 +150,7 @@ output_frame_notify(struct wl_listener *listener, void *data) struct wlr_scene_output *scene_output = output->scene_output; struct wlr_output_state *pending = &output->pending; - pending->tearing_page_flip = get_tearing_preference(output); + pending->tearing_page_flip = output_get_tearing_allowance(output); bool committed = lab_wlr_scene_output_commit(scene_output, pending); diff --git a/src/tearing.c b/src/tearing.c index 583fe1e2..b4e86d60 100644 --- a/src/tearing.c +++ b/src/tearing.c @@ -15,8 +15,15 @@ set_tearing_hint(struct wl_listener *listener, void *data) { struct tearing_controller *controller = wl_container_of(listener, controller, set_hint); struct view *view = view_from_wlr_surface(controller->tearing_control->surface); - if (view && controller->tearing_control->current) { - view->tearing_hint = true; + if (view) { + /* + * tearing_control->current is actually an enum: + * WP_TEARING_CONTROL_V1_PRESENTATION_HINT_VSYNC = 0 + * WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC = 1 + * + * Using it as a bool here allows us to not ship the XML. + */ + view->tearing_hint = controller->tearing_control->current; } } From dc387f7765ef7f6b0e7c2ef4577fdd3181338b71 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Wed, 7 Aug 2024 21:01:30 +0100 Subject: [PATCH 097/232] NEWS.md: interim update --- NEWS.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index ec82c33f..013a8411 100644 --- a/NEWS.md +++ b/NEWS.md @@ -30,6 +30,80 @@ The format is based on [Keep a Changelog] | 2021-04-15 | [0.2.0] | 0.13.0 | 5011 | | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | +## [unreleased] + +The main focus in this release has been to port labwc to wlroots 0.18 and to +grind out associated regressions. Nonetheless, it contains a few non-related +additions and fixes as described below. + +As a one-off note, there appears to be an issue with increased commit failures +when using wlroots-0.18, particularly with intel drivers. If this turns out to +be an issue for anyone please try running with `WLR_DRM_NO_ATOMIC=1` or run the +labwc v0.7 branch or its latest release until this is resolved. + +A v0.7 branch has been created for bug fixes beyond `0.7.3` (built with wlroots +`0.17`). + +A big thank you goes to @Consolatis for carefully crafting the commits to port +across to wlroots 0.18.0. Many thanks also to the other core devs @ahesford, +@jlindgren90, @johanmalm and @tokyo4j for reviewing, merging as well as +contributing many patches with fixes and new features. And in this release we +have some great contributions from @jp7677, @kode54, @xi and @heroin-moose which +have been attributed with a 'Written-by' against each relevant log entry. + +### Added + +- Add options `fullscreen` and `fullscreenForced` for `` + Written-by: @jp7677 & @Consolatis PR #1941 +- Optionally allow keybindings when session is locked, which for example can be + useful for volume settings. Written-by: @xi PR #2041 + +```xml + +``` + +- Add resistance when dragging tiled/maximized windows with config option + ``. PR #2009 +- Implement support for renderer loss recovery. Written-by: @kode54 PR #1997 +- Support xinitrc scripts to configure XWayland server on launch. PR #1963 +- Add theme option `window.button.width` to set window button size. + Written-by: @heroin-moose PR #1965 +- Add `cascade` placement policy. PR #1890 + +```xml + + cascade + + +``` + +- Support relative tablet motion. Written-by: @jp7677 PR #1962 + +```xml + +``` + +### Fixed + +- Take into account CSD borders when unconstraining XDG popups. PR #2016 +- Choose xdg-popup output depending on xdg-positioner PR #2016 +- Fix wlroots-0.18 regression causing flicker with some layer-shell clients like + fuzzel on launch. PR #2021 +- Fix incorrect condition in server-side-deco logic PR #2020 +- Fix flicker of snapped windows in nested session. PR #2010 +- Fix tearing with atomic mode setting. Written-by: @kode54 PR #1996 +- Handle initially maximized and fullscreen xdg-shell windows better. + PRs #1956 and #2007 +- Set initial geometry of maximized and fullscreen XWayland windows in the + `map_request` handler to avoid visual glitches with some apps. PR #1529 +- Disable pango glyph position rounding to avoid text geometry jump around when + changing scale. +- Verify source surface for `xdg_activation` request + +### Changed + +- Make windows stay fullscreen when associated output is disconnected. PR #2040 + ## [0.7.4] ### Fixed @@ -1443,7 +1517,7 @@ 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.4...HEAD +[unreleased]: https://github.com/labwc/labwc/compare/0.7.3...HEAD [0.7.4]: https://github.com/labwc/labwc/compare/0.7.3...0.7.4 [0.7.3]: https://github.com/labwc/labwc/compare/0.7.2...0.7.3 [0.7.2]: https://github.com/labwc/labwc/compare/0.7.1...0.7.2 From 670cc0f511cef7e82ba4024d4c50f980de479b4c Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 8 Aug 2024 09:19:52 +0900 Subject: [PATCH 098/232] Rename to As we already have <{screen,window}EdgeStrength>, will be a better place for this setting. --- docs/labwc-config.5.scd | 8 ++++---- docs/rc.xml.all | 2 +- include/config/rcxml.h | 2 +- include/labwc.h | 4 ++-- src/config/rcxml.c | 6 +++--- src/input/cursor.c | 2 +- src/interactive.c | 8 ++++---- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 55956db4..dd8e4bcb 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -326,6 +326,10 @@ this is for compatibility with Openbox. The default value for both parameters is 20 pixels. +** + Sets the movement of cursor in pixel required for a tiled or maximized + window to be moved with an interactive move. Default is 20. + ## FOCUS ** [yes|no] @@ -356,10 +360,6 @@ extending outward from the snapped edge. SnapToEdge action for that edge. A *range* of 0 disables snapping via interactive moves. Default is 1. -** - Sets the movement of cursor in pixel required for a tiled or maximized - window to be moved with an interactive move. Default is 20. - ** [yes|no] Show an overlay when snapping to a window to an edge. Default is yes. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index ea331352..f71014a6 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -107,6 +107,7 @@ 20 20 + 20 @@ -125,7 +126,6 @@ 1 - 20 diff --git a/include/config/rcxml.h b/include/config/rcxml.h index c49bafd2..2b5da588 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -124,6 +124,7 @@ struct rcxml { /* resistance */ int screen_edge_strength; int window_edge_strength; + int unsnap_threshold; /* window snapping */ int snap_edge_range; @@ -132,7 +133,6 @@ struct rcxml { int snap_overlay_delay_outer; bool snap_top_maximize; enum tiling_events_mode snap_tiling_events_mode; - int snap_drag_resistance; enum resize_indicator_mode resize_indicator; bool resize_draw_contents; diff --git a/include/labwc.h b/include/labwc.h index 466a90d3..1e577c86 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -504,7 +504,7 @@ void seat_output_layout_changed(struct seat *seat); * geometry->{width,height} are provided by the caller. * geometry->{x,y} are computed by this function. * - * @note When drag-resistance is used, cursor_x/y should be the original + * @note When is non-zero, cursor_x/y should be the original * cursor position when the button was pressed. */ void interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry, @@ -514,7 +514,7 @@ void interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry, * interactive_move_tiled_view_to() - Un-tile the tiled/maximized view at the * start of an interactive move or when an interactive move is pending. * Returns true if the distance of cursor motion exceeds the value of - * and the view is un-tiled. + * and the view is un-tiled. */ bool interactive_move_tiled_view_to(struct server *server, struct view *view, struct wlr_box *geometry); diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 0227911b..da0bbde3 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -965,6 +965,8 @@ entry(xmlNode *node, char *nodename, char *content) rc.screen_edge_strength = atoi(content); } else if (!strcasecmp(nodename, "windowEdgeStrength.resistance")) { rc.window_edge_strength = atoi(content); + } else if (!strcasecmp(nodename, "unSnapThreshold.resistance")) { + rc.unsnap_threshold = atoi(content); } else if (!strcasecmp(nodename, "range.snapping")) { rc.snap_edge_range = atoi(content); } else if (!strcasecmp(nodename, "enabled.overlay.snapping")) { @@ -987,8 +989,6 @@ entry(xmlNode *node, char *nodename, char *content) } else { wlr_log(WLR_ERROR, "ignoring invalid value for notifyClient"); } - } else if (!strcasecmp(nodename, "dragResistance.snapping")) { - rc.snap_drag_resistance = atoi(content); /* */ } else if (!strcasecmp(nodename, "show.windowSwitcher")) { @@ -1289,6 +1289,7 @@ rcxml_init(void) rc.kb_layout_per_window = false; rc.screen_edge_strength = 20; rc.window_edge_strength = 20; + rc.unsnap_threshold = 20; rc.snap_edge_range = 1; rc.snap_overlay_enabled = true; @@ -1296,7 +1297,6 @@ rcxml_init(void) rc.snap_overlay_delay_outer = 500; rc.snap_top_maximize = true; rc.snap_tiling_events_mode = LAB_TILING_EVENTS_ALWAYS; - rc.snap_drag_resistance = 20; rc.window_switcher.show = true; rc.window_switcher.preview = true; diff --git a/src/input/cursor.c b/src/input/cursor.c index 3df48983..94e23fdd 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -236,7 +236,7 @@ process_cursor_move(struct server *server, uint32_t time) /* * Un-tile the view when interactive move is delayed and the distance - * of cursor movement exceeds . + * of cursor movement exceeds . */ if (server->move_pending && !interactive_move_tiled_view_to( server, server->grabbed_view, &server->grab_box)) { diff --git a/src/interactive.c b/src/interactive.c index d8c2f342..13392b06 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -38,7 +38,7 @@ interactive_move_tiled_view_to(struct server *server, struct view *view, { assert(!view_is_floating(view)); - int resistance = rc.snap_drag_resistance; + int threshold = rc.unsnap_threshold; if (server->input_mode == LAB_INPUT_STATE_MOVE) { /* When called from cursor motion handler */ @@ -46,12 +46,12 @@ interactive_move_tiled_view_to(struct server *server, struct view *view, double dx = server->seat.cursor->x - server->grab_x; double dy = server->seat.cursor->y - server->grab_y; - if (dx * dx + dy * dy < resistance * resistance) { + if (dx * dx + dy * dy < threshold * threshold) { return false; } } else { /* When called from interactive_begin() */ - if (resistance > 0) { + if (threshold > 0) { return false; } } @@ -118,7 +118,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) } /* - * If is non-zero, the + * If is non-zero, the * tiled/maximized view is un-tiled later in cursor * motion handler. */ From 060626e9c74c3a3fdea048c6be403228bbb53c07 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Thu, 8 Aug 2024 17:46:20 +0200 Subject: [PATCH 099/232] input: subscribe to tablet tool events from cursor Contrary to the raw tablet events, the cursor events transform the coordinates based on a mapped output orientation. Otherwise those events are the same. --- include/input/tablet.h | 8 ++++---- src/input/tablet.c | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/input/tablet.h b/include/input/tablet.h index 93fdda5a..0ca11fae 100644 --- a/include/input/tablet.h +++ b/include/input/tablet.h @@ -23,10 +23,10 @@ struct drawing_tablet { double slider; double wheel_delta; struct { - struct wl_listener proximity; - struct wl_listener axis; - struct wl_listener tip; - struct wl_listener button; + struct wl_listener tablet_tool_proximity; + struct wl_listener tablet_tool_axis; + struct wl_listener tablet_tool_tip; + struct wl_listener tablet_tool_button; struct wl_listener destroy; } handlers; struct wl_list link; /* seat.tablets */ diff --git a/src/input/tablet.c b/src/input/tablet.c index 743fa72e..341d45ac 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -236,7 +236,7 @@ notify_motion(struct drawing_tablet *tablet, struct drawing_tablet_tool *tool, } static void -handle_proximity(struct wl_listener *listener, void *data) +handle_tablet_tool_proximity(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_proximity_event *ev = data; struct drawing_tablet *tablet = ev->tablet->data; @@ -286,7 +286,7 @@ handle_proximity(struct wl_listener *listener, void *data) static bool is_down_mouse_emulation = false; static void -handle_axis(struct wl_listener *listener, void *data) +handle_tablet_tool_axis(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_axis_event *ev = data; struct drawing_tablet *tablet = ev->tablet->data; @@ -460,7 +460,7 @@ seat_pointer_end_grab(struct drawing_tablet_tool *tool, } static void -handle_tip(struct wl_listener *listener, void *data) +handle_tablet_tool_tip(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_tip_event *ev = data; struct drawing_tablet *tablet = ev->tablet->data; @@ -535,7 +535,7 @@ handle_tip(struct wl_listener *listener, void *data) } static void -handle_button(struct wl_listener *listener, void *data) +handle_tablet_tool_button(struct wl_listener *listener, void *data) { struct wlr_tablet_tool_button_event *ev = data; struct drawing_tablet *tablet = ev->tablet->data; @@ -604,10 +604,10 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&tablet->link); tablet_pad_attach_tablet(tablet->seat); - wl_list_remove(&tablet->handlers.tip.link); - wl_list_remove(&tablet->handlers.button.link); - wl_list_remove(&tablet->handlers.proximity.link); - wl_list_remove(&tablet->handlers.axis.link); + wl_list_remove(&tablet->handlers.tablet_tool_tip.link); + wl_list_remove(&tablet->handlers.tablet_tool_button.link); + wl_list_remove(&tablet->handlers.tablet_tool_proximity.link); + wl_list_remove(&tablet->handlers.tablet_tool_axis.link); wl_list_remove(&tablet->handlers.destroy.link); free(tablet); } @@ -636,10 +636,10 @@ tablet_init(struct seat *seat, struct wlr_input_device *wlr_device) tablet->wheel_delta = 0.0; wlr_log(WLR_INFO, "tablet dimensions: %.2fmm x %.2fmm", tablet->tablet->width_mm, tablet->tablet->height_mm); - CONNECT_SIGNAL(tablet->tablet, &tablet->handlers, axis); - CONNECT_SIGNAL(tablet->tablet, &tablet->handlers, proximity); - CONNECT_SIGNAL(tablet->tablet, &tablet->handlers, tip); - CONNECT_SIGNAL(tablet->tablet, &tablet->handlers, button); + CONNECT_SIGNAL(seat->cursor, &tablet->handlers, tablet_tool_axis); + CONNECT_SIGNAL(seat->cursor, &tablet->handlers, tablet_tool_proximity); + CONNECT_SIGNAL(seat->cursor, &tablet->handlers, tablet_tool_tip); + CONNECT_SIGNAL(seat->cursor, &tablet->handlers, tablet_tool_button); CONNECT_SIGNAL(wlr_device, &tablet->handlers, destroy); wl_list_insert(&seat->tablets, &tablet->link); From df12572f299af1b54f552509d87c5418cf26e016 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Thu, 8 Aug 2024 18:09:07 +0200 Subject: [PATCH 100/232] docs: clarify tablet rotation for mapped outputs --- docs/labwc-config.5.scd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index dd8e4bcb..c526f8cd 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -691,6 +691,9 @@ extending outward from the snapped edge. If the output name is left empty or the output does not exists, the tablet will span all outputs. + The tablet cursor automatically applies the orientation/rotation of + a mapped output for absolute motion. + ** [0|90|180|270] The tablet orientation can be changed in 90 degree steps. Default is no rotation (0). Rotation will be applied after applying tablet area From f20b2c7632cdef1d48e6dab0cf5d57e9c6af7d93 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Thu, 8 Aug 2024 20:01:37 +0200 Subject: [PATCH 101/232] input: apply tablet rotation before area transformation This makes the behavior consistent with auto-rotation as a result of output mapping. --- docs/labwc-config.5.scd | 2 +- src/input/tablet.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index c526f8cd..bd03fd6b 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -696,7 +696,7 @@ extending outward from the snapped edge. ** [0|90|180|270] The tablet orientation can be changed in 90 degree steps. Default is - no rotation (0). Rotation will be applied after applying tablet area + no rotation (0). Rotation will be applied before applying tablet area transformation. See also *calibrationMatrix* in libinput section below for advanced diff --git a/src/input/tablet.c b/src/input/tablet.c index 341d45ac..1d280e71 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -125,9 +125,9 @@ tablet_get_coords(struct drawing_tablet *tablet, double *x, double *y, double *d *y = tablet->y; *dx = tablet->dx; *dy = tablet->dy; + adjust_for_rotation(rc.tablet.rotation, x, y); adjust_for_tablet_area(tablet->tablet->width_mm, tablet->tablet->height_mm, rc.tablet.box, x, y); - adjust_for_rotation(rc.tablet.rotation, x, y); adjust_for_rotation_relative(rc.tablet.rotation, dx, dy); adjust_for_motion_sensitivity(rc.tablet_tool.relative_motion_sensitivity, dx, dy); From 72df8fe73c9c42cd0d7362dbf87d7956ed78a635 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 8 Aug 2024 20:14:23 +0100 Subject: [PATCH 102/232] keyboard: fix bug with empty XKB_DEFAULT_LAYOUT With XKB_DEFAULT_LAYOUT= (set to empty rather than unset) the keyboard just does not work. Ref: https://github.com/labwc/labwc-tweaks/issues/89 --- src/input/keyboard.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/input/keyboard.c b/src/input/keyboard.c index 4188a826..ea758296 100644 --- a/src/input/keyboard.c +++ b/src/input/keyboard.c @@ -698,7 +698,15 @@ set_layout(struct server *server, struct wlr_keyboard *kb) struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, XKB_KEYMAP_COMPILE_NO_FLAGS); - if (keymap) { + + /* + * With XKB_DEFAULT_LAYOUT set to empty odd things happen with + * xkb_map_new_from_names() resulting in the keyboard not working, so + * we protect against that. + */ + const char *layout = getenv("XKB_DEFAULT_LAYOUT"); + bool layout_empty = layout && !*layout; + if (keymap && !layout_empty) { if (!wlr_keyboard_keymaps_match(kb->keymap, keymap)) { wlr_keyboard_set_keymap(kb, keymap); reset_window_keyboard_layout_groups(server); @@ -706,7 +714,7 @@ set_layout(struct server *server, struct wlr_keyboard *kb) xkb_keymap_unref(keymap); } else { wlr_log(WLR_ERROR, "failed to create xkb keymap for layout '%s'", - getenv("XKB_DEFAULT_LAYOUT")); + layout); if (!fallback_mode) { wlr_log(WLR_ERROR, "entering fallback mode with layout 'us'"); fallback_mode = true; From de38c771fc4d0a9c5073e40719751b0badc6a60f Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 9 Aug 2024 15:56:26 +0900 Subject: [PATCH 103/232] xdg-activation: temporarily disable source surface verification 71451173 validates the xdg-activation token more strictly by verifying the source surface attached to the token. That improves the security by preventing arbitrary focus-stealing. However, not all clients attach a right source surface to the token or use the received token for activation. For example, when a notification client requests thunderbird to activate its window, thunderbird doesn't use the token passed by the notification client and instead use their own token, thus the activation is rejected as the surface attached to the token is not focused. We will add options to configure the policy for activation requests or implement urgency hint in some way in the future and reland the source surface verification. --- src/xdg.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/xdg.c b/src/xdg.c index ad0e19c1..eb106141 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -785,10 +785,17 @@ xdg_activation_handle_request(struct wl_listener *listener, void *data) return; } - if (!token_data->had_valid_surface) { - wlr_log(WLR_INFO, "Denying focus request, source surface not set"); - return; - } + /* + * TODO: The verification of source surface is temporarily disabled to + * allow activation of some clients (e.g. thunderbird). Reland this + * check when we implement the configuration for activation policy or + * urgency hints. + * + * if (!token_data->had_valid_surface) { + * wlr_log(WLR_INFO, "Denying focus request, source surface not set"); + * return; + * } + */ if (window_rules_get_property(view, "ignoreFocusRequest") == LAB_PROP_TRUE) { wlr_log(WLR_INFO, "Ignoring focus request due to window rule configuration"); From 6687640665a7959ac0c153b1c23049c7bd10b7aa Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 21 Jul 2024 17:09:17 +0900 Subject: [PATCH 104/232] xdg: fix error when launching windowed Chromium Chromium sends 2 commits before the commit with a buffer attached. We were just checking `wlr_box_empty(&view->pending)` to handle the cases where an initially maximized/fullscreen client is windowed, but that check was also returning true on the 2nd commit from Chromium, resulting in an error message: "view has empty geometry, not centering". --- src/xdg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xdg.c b/src/xdg.c index eb106141..b75f864f 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -149,7 +149,7 @@ handle_commit(struct wl_listener *listener, void *data) * the pending x/y is also unset and we still need to position * the window. */ - if (wlr_box_empty(&view->pending)) { + if (wlr_box_empty(&view->pending) && !wlr_box_empty(&size)) { view->pending.width = size.width; view->pending.height = size.height; do_late_positioning(view); From 538eb3ee83926283b8d04a3046984f288f95aef3 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 9 Aug 2024 09:22:28 +0200 Subject: [PATCH 105/232] input: use CONNECT_SIGNAL macro --- src/input/touch.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/input/touch.c b/src/input/touch.c index d2419d1a..511fba20 100644 --- a/src/input/touch.c +++ b/src/input/touch.c @@ -2,6 +2,7 @@ #include #include #include +#include "common/macros.h" #include "common/mem.h" #include "common/scene-helpers.h" #include "idle.h" @@ -9,7 +10,6 @@ #include "labwc.h" #include "config/mousebind.h" #include "action.h" -#include "view.h" /* Holds layout -> surface offsets to report motion events in relative coords */ struct touch_point { @@ -46,7 +46,7 @@ touch_get_coords(struct seat *seat, struct wlr_touch *touch, double x, double y, } static void -touch_motion(struct wl_listener *listener, void *data) +handle_touch_motion(struct wl_listener *listener, void *data) { struct seat *seat = wl_container_of(listener, seat, touch_motion); struct wlr_touch_motion_event *event = data; @@ -78,7 +78,7 @@ touch_motion(struct wl_listener *listener, void *data) } static void -touch_frame(struct wl_listener *listener, void *data) +handle_touch_frame(struct wl_listener *listener, void *data) { struct seat *seat = wl_container_of(listener, seat, touch_frame); @@ -86,7 +86,7 @@ touch_frame(struct wl_listener *listener, void *data) } static void -touch_down(struct wl_listener *listener, void *data) +handle_touch_down(struct wl_listener *listener, void *data) { struct seat *seat = wl_container_of(listener, seat, touch_down); struct wlr_touch_down_event *event = data; @@ -133,7 +133,7 @@ touch_down(struct wl_listener *listener, void *data) } static void -touch_up(struct wl_listener *listener, void *data) +handle_touch_up(struct wl_listener *listener, void *data) { struct seat *seat = wl_container_of(listener, seat, touch_up); struct wlr_touch_up_event *event = data; @@ -159,14 +159,10 @@ touch_up(struct wl_listener *listener, void *data) void touch_init(struct seat *seat) { - seat->touch_down.notify = touch_down; - wl_signal_add(&seat->cursor->events.touch_down, &seat->touch_down); - seat->touch_up.notify = touch_up; - wl_signal_add(&seat->cursor->events.touch_up, &seat->touch_up); - seat->touch_motion.notify = touch_motion; - wl_signal_add(&seat->cursor->events.touch_motion, &seat->touch_motion); - seat->touch_frame.notify = touch_frame; - wl_signal_add(&seat->cursor->events.touch_frame, &seat->touch_frame); + CONNECT_SIGNAL(seat->cursor, seat, touch_down); + CONNECT_SIGNAL(seat->cursor, seat, touch_up); + CONNECT_SIGNAL(seat->cursor, seat, touch_motion); + CONNECT_SIGNAL(seat->cursor, seat, touch_frame); } void From dd0cee02a47e6a806be6209260ebcb2708f24461 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Fri, 9 Aug 2024 09:25:00 +0200 Subject: [PATCH 106/232] input: move cursor event listeners from tablet to seat Otherwise we would subscribe multiple times to the same event when having multiple tablets. --- include/input/tablet-pad.h | 2 +- include/input/tablet-tool.h | 2 +- include/input/tablet.h | 8 +++----- include/labwc.h | 5 +++++ src/input/cursor.c | 7 ++++++- src/input/tablet-pad.c | 2 +- src/input/tablet-tool.c | 2 +- src/input/tablet.c | 30 ++++++++++++++++++++---------- src/seat.c | 4 ++-- 9 files changed, 40 insertions(+), 22 deletions(-) diff --git a/include/input/tablet-pad.h b/include/input/tablet-pad.h index 4f39239f..3ada7634 100644 --- a/include/input/tablet-pad.h +++ b/include/input/tablet-pad.h @@ -36,7 +36,7 @@ struct drawing_tablet_pad { struct wl_list link; /* seat.tablet_pads */ }; -void tablet_pad_init(struct seat *seat, struct wlr_input_device *wlr_input_device); +void tablet_pad_create(struct seat *seat, struct wlr_input_device *wlr_input_device); void tablet_pad_attach_tablet(struct seat *seat); void tablet_pad_enter_surface(struct seat *seat, struct wlr_surface *wlr_surface); diff --git a/include/input/tablet-tool.h b/include/input/tablet-tool.h index 8f7b589d..76306877 100644 --- a/include/input/tablet-tool.h +++ b/include/input/tablet-tool.h @@ -17,7 +17,7 @@ struct drawing_tablet_tool { struct wl_list link; /* seat.tablet_tools */ }; -void tablet_tool_init(struct seat *seat, +void tablet_tool_create(struct seat *seat, struct wlr_tablet_tool *wlr_tablet_tool); bool tablet_tool_has_focused_surface(struct seat *seat); diff --git a/include/input/tablet.h b/include/input/tablet.h index 0ca11fae..e9145636 100644 --- a/include/input/tablet.h +++ b/include/input/tablet.h @@ -23,15 +23,13 @@ struct drawing_tablet { double slider; double wheel_delta; struct { - struct wl_listener tablet_tool_proximity; - struct wl_listener tablet_tool_axis; - struct wl_listener tablet_tool_tip; - struct wl_listener tablet_tool_button; struct wl_listener destroy; } handlers; struct wl_list link; /* seat.tablets */ }; -void tablet_init(struct seat *seat, struct wlr_input_device *wlr_input_device); +void tablet_init(struct seat *seat); +void tablet_finish(struct seat *seat); +void tablet_create(struct seat *seat, struct wlr_input_device *wlr_input_device); #endif /* LABWC_TABLET_H */ diff --git a/include/labwc.h b/include/labwc.h index 1e577c86..14ce607c 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -192,6 +192,11 @@ struct seat { struct wl_listener touch_motion; struct wl_listener touch_frame; + struct wl_listener tablet_tool_proximity; + struct wl_listener tablet_tool_axis; + struct wl_listener tablet_tool_tip; + struct wl_listener tablet_tool_button; + struct wl_list tablets; struct wl_list tablet_tools; struct wl_list tablet_pads; diff --git a/src/input/cursor.c b/src/input/cursor.c index 94e23fdd..3b2583f0 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -13,12 +13,13 @@ #include "common/scene-helpers.h" #include "common/surface-helpers.h" #include "config/mousebind.h" +#include "config/tablet-tool.h" #include "dnd.h" #include "idle.h" #include "input/gestures.h" #include "input/touch.h" +#include "input/tablet.h" #include "input/tablet-tool.h" -#include "input/tablet-pad.h" #include "labwc.h" #include "layers.h" #include "menu/menu.h" @@ -1450,6 +1451,8 @@ cursor_init(struct seat *seat) gestures_init(seat); touch_init(seat); + tablet_init(seat); + seat->request_cursor.notify = request_cursor_notify; wl_signal_add(&seat->seat->events.request_set_cursor, &seat->request_cursor); @@ -1488,6 +1491,8 @@ void cursor_finish(struct seat *seat) gestures_finish(seat); touch_finish(seat); + tablet_finish(seat); + wl_list_remove(&seat->request_cursor.link); wl_list_remove(&seat->request_set_shape.link); wl_list_remove(&seat->request_set_selection.link); diff --git a/src/input/tablet-pad.c b/src/input/tablet-pad.c index c5381ef1..2453be96 100644 --- a/src/input/tablet-pad.c +++ b/src/input/tablet-pad.c @@ -175,7 +175,7 @@ handle_destroy(struct wl_listener *listener, void *data) } void -tablet_pad_init(struct seat *seat, struct wlr_input_device *wlr_device) +tablet_pad_create(struct seat *seat, struct wlr_input_device *wlr_device) { wlr_log(WLR_DEBUG, "setting up tablet pad"); struct drawing_tablet_pad *pad = znew(*pad); diff --git a/src/input/tablet-tool.c b/src/input/tablet-tool.c index 3e36f5b0..fcc1e733 100644 --- a/src/input/tablet-tool.c +++ b/src/input/tablet-tool.c @@ -60,7 +60,7 @@ handle_destroy(struct wl_listener *listener, void *data) } void -tablet_tool_init(struct seat *seat, +tablet_tool_create(struct seat *seat, struct wlr_tablet_tool *wlr_tablet_tool) { wlr_log(WLR_DEBUG, "setting up tablet tool"); diff --git a/src/input/tablet.c b/src/input/tablet.c index 1d280e71..489da6fa 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -266,7 +266,7 @@ handle_tablet_tool_proximity(struct wl_listener *listener, void *data) * Unfortunately `wlr_tool` is only present in the events, so * use proximity for creating a `wlr_tablet_v2_tablet_tool`. */ - tablet_tool_init(tablet->seat, ev->tool); + tablet_tool_create(tablet->seat, ev->tool); } /* @@ -604,16 +604,12 @@ handle_destroy(struct wl_listener *listener, void *data) wl_list_remove(&tablet->link); tablet_pad_attach_tablet(tablet->seat); - wl_list_remove(&tablet->handlers.tablet_tool_tip.link); - wl_list_remove(&tablet->handlers.tablet_tool_button.link); - wl_list_remove(&tablet->handlers.tablet_tool_proximity.link); - wl_list_remove(&tablet->handlers.tablet_tool_axis.link); wl_list_remove(&tablet->handlers.destroy.link); free(tablet); } void -tablet_init(struct seat *seat, struct wlr_input_device *wlr_device) +tablet_create(struct seat *seat, struct wlr_input_device *wlr_device) { wlr_log(WLR_DEBUG, "setting up tablet"); struct drawing_tablet *tablet = znew(*tablet); @@ -636,12 +632,26 @@ tablet_init(struct seat *seat, struct wlr_input_device *wlr_device) tablet->wheel_delta = 0.0; wlr_log(WLR_INFO, "tablet dimensions: %.2fmm x %.2fmm", tablet->tablet->width_mm, tablet->tablet->height_mm); - CONNECT_SIGNAL(seat->cursor, &tablet->handlers, tablet_tool_axis); - CONNECT_SIGNAL(seat->cursor, &tablet->handlers, tablet_tool_proximity); - CONNECT_SIGNAL(seat->cursor, &tablet->handlers, tablet_tool_tip); - CONNECT_SIGNAL(seat->cursor, &tablet->handlers, tablet_tool_button); CONNECT_SIGNAL(wlr_device, &tablet->handlers, destroy); wl_list_insert(&seat->tablets, &tablet->link); tablet_pad_attach_tablet(tablet->seat); } + +void +tablet_init(struct seat *seat) +{ + CONNECT_SIGNAL(seat->cursor, seat, tablet_tool_axis); + CONNECT_SIGNAL(seat->cursor, seat, tablet_tool_proximity); + CONNECT_SIGNAL(seat->cursor, seat, tablet_tool_tip); + CONNECT_SIGNAL(seat->cursor, seat, tablet_tool_button); +} + +void +tablet_finish(struct seat *seat) +{ + wl_list_remove(&seat->tablet_tool_axis.link); + wl_list_remove(&seat->tablet_tool_proximity.link); + wl_list_remove(&seat->tablet_tool_tip.link); + wl_list_remove(&seat->tablet_tool_button.link); +} diff --git a/src/seat.c b/src/seat.c index 7987b5a3..49f317cc 100644 --- a/src/seat.c +++ b/src/seat.c @@ -367,7 +367,7 @@ new_tablet(struct seat *seat, struct wlr_input_device *dev) { struct input *input = znew(*input); input->wlr_input_device = dev; - tablet_init(seat, dev); + tablet_create(seat, dev); wlr_cursor_attach_input_device(seat->cursor, dev); wlr_log(WLR_INFO, "map tablet to output %s\n", rc.tablet.output_name); map_input_to_output(seat, dev, rc.tablet.output_name); @@ -380,7 +380,7 @@ new_tablet_pad(struct seat *seat, struct wlr_input_device *dev) { struct input *input = znew(*input); input->wlr_input_device = dev; - tablet_pad_init(seat, dev); + tablet_pad_create(seat, dev); return input; } From 00512d055fa77957f8c8c3b998dfba61e87b9e34 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Sat, 10 Aug 2024 07:05:05 +0200 Subject: [PATCH 107/232] input: ensure that our own tablet structs are created --- src/input/tablet.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/input/tablet.c b/src/input/tablet.c index 489da6fa..5563db0c 100644 --- a/src/input/tablet.c +++ b/src/input/tablet.c @@ -241,6 +241,10 @@ handle_tablet_tool_proximity(struct wl_listener *listener, void *data) struct wlr_tablet_tool_proximity_event *ev = data; struct drawing_tablet *tablet = ev->tablet->data; struct drawing_tablet_tool *tool = ev->tool->data; + if (!tablet) { + wlr_log(WLR_DEBUG, "tool proximity event before tablet create"); + return; + } if (ev->state == WLR_TABLET_TOOL_PROXIMITY_IN) { tablet->motion_mode = @@ -291,6 +295,10 @@ handle_tablet_tool_axis(struct wl_listener *listener, void *data) struct wlr_tablet_tool_axis_event *ev = data; struct drawing_tablet *tablet = ev->tablet->data; struct drawing_tablet_tool *tool = ev->tool->data; + if (!tablet) { + wlr_log(WLR_DEBUG, "tool axis event before tablet create"); + return; + } /* * Reset relative coordinates. If those axes aren't updated, @@ -465,6 +473,10 @@ handle_tablet_tool_tip(struct wl_listener *listener, void *data) struct wlr_tablet_tool_tip_event *ev = data; struct drawing_tablet *tablet = ev->tablet->data; struct drawing_tablet_tool *tool = ev->tool->data; + if (!tablet) { + wlr_log(WLR_DEBUG, "tool tip event before tablet create"); + return; + } double x, y, dx, dy; struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y, &dx, &dy); @@ -540,6 +552,10 @@ handle_tablet_tool_button(struct wl_listener *listener, void *data) struct wlr_tablet_tool_button_event *ev = data; struct drawing_tablet *tablet = ev->tablet->data; struct drawing_tablet_tool *tool = ev->tool->data; + if (!tablet) { + wlr_log(WLR_DEBUG, "tool button event before tablet create"); + return; + } double x, y, dx, dy; struct wlr_surface *surface = tablet_get_coords(tablet, &x, &y, &dx, &dy); From 6a1ecd6b7dbb4ec5314d60b05aa12e86a5d04626 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 14 Aug 2024 20:17:50 +0200 Subject: [PATCH 108/232] scene-helpers: use pending_commit_damage, chases wlr!4253 Using the output damage_ring for early out will break VRR in direct scanout mode. The reason being that the damage_ring will be completely ignored in that mode so we need to check `output->pending_commit_damage` instead. This matches with what wlroots has been doing since [0] and it was missed in the initial port to wlroots 0.18.x. However, that would then break the magnifier which only adds its damage to the damage ring. After some discussion with the wlroots devs we came up with a solution that should work for both, wlroots 0.18.0 and when [1] is backported to 0.18.1. Note that even with this PR, VRR in direct scanout mode is broken in 0.18.0 from the wlroots side and will be fixed once [1] is backported to the 0.18 branch and 0.18.1 is released. Fixes: #2078 [0] https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4253 [1] https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4753 --- src/common/scene-helpers.c | 49 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/src/common/scene-helpers.c b/src/common/scene-helpers.c index fb627719..2d12d570 100644 --- a/src/common/scene-helpers.c +++ b/src/common/scene-helpers.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "common/scene-helpers.h" #include "labwc.h" #include "magnifier.h" @@ -37,6 +39,43 @@ lab_wlr_scene_get_prev_node(struct wlr_scene_node *node) return prev; } +/* + * This is a slightly modified copy of scene_output_damage(), + * required to properly add the magnifier damage to scene_output + * ->damage_ring and scene_output->pending_commit_damage. + * + * The only difference is code style and removal of wlr_output_schedule_frame(). + */ +static void +scene_output_damage(struct wlr_scene_output *scene_output, + const pixman_region32_t *region) +{ + if (!wlr_damage_ring_add(&scene_output->damage_ring, region)) { + return; + } + + struct wlr_output *output = scene_output->output; + enum wl_output_transform transform = + wlr_output_transform_invert(scene_output->output->transform); + + int width = output->width; + int height = output->height; + if (transform & WL_OUTPUT_TRANSFORM_90) { + width = output->height; + height = output->width; + } + + pixman_region32_t frame_damage; + pixman_region32_init(&frame_damage); + wlr_region_transform(&frame_damage, region, transform, width, height); + + pixman_region32_union(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, &frame_damage); + pixman_region32_intersect_rect(&scene_output->pending_commit_damage, + &scene_output->pending_commit_damage, 0, 0, output->width, output->height); + pixman_region32_fini(&frame_damage); +} + /* * This is a copy of wlr_scene_output_commit() * as it doesn't use the pending state at all. @@ -58,7 +97,7 @@ lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output, * We also need to verify the necessity of wants_magnification. */ if (!wlr_output->needs_frame && !pixman_region32_not_empty( - &scene_output->damage_ring.current) && !wants_magnification) { + &scene_output->pending_commit_damage) && !wants_magnification) { return true; } @@ -86,7 +125,13 @@ lab_wlr_scene_output_commit(struct wlr_scene_output *scene_output, } if (!wlr_box_empty(&additional_damage)) { - wlr_damage_ring_add_box(&scene_output->damage_ring, &additional_damage); + pixman_region32_t region; + pixman_region32_init_rect(®ion, + additional_damage.x, additional_damage.y, + additional_damage.width, additional_damage.height); + scene_output_damage(scene_output, ®ion); + pixman_region32_fini(®ion); } + return true; } From c63dce83c4d9ac79747d8301000e19a8fa4dc73b Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 15 Aug 2024 21:16:48 +0100 Subject: [PATCH 109/232] NEWS.md: update notes for 0.8.0 --- NEWS.md | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index 013a8411..1e28fb84 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog] | Date | All Changes | wlroots version | lines-of-code | |------------|---------------|-----------------|---------------| +| 2024-08-16 | [0.8.0] | 0.18.0 | 23320 | | 2024-06-19 | [0.7.4] | 0.17.4 | 22746 | | 2024-06-12 | [0.7.3] | 0.17.4 | 22731 | | 2024-05-10 | [0.7.2] | 0.17.3 | 21368 | @@ -30,16 +31,21 @@ The format is based on [Keep a Changelog] | 2021-04-15 | [0.2.0] | 0.13.0 | 5011 | | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | -## [unreleased] +## [0.8.0] The main focus in this release has been to port labwc to wlroots 0.18 and to grind out associated regressions. Nonetheless, it contains a few non-related additions and fixes as described below. -As a one-off note, there appears to be an issue with increased commit failures -when using wlroots-0.18, particularly with intel drivers. If this turns out to -be an issue for anyone please try running with `WLR_DRM_NO_ATOMIC=1` or run the -labwc v0.7 branch or its latest release until this is resolved. +There are a couple of regression warnings when using wlroots 0.18: + +1. There appears to be an issue with increased commit failures, particularly + with intel drivers. If this turns out to be an issue for anyone please try + running with `WLR_DRM_NO_ATOMIC=1` or run the labwc v0.7 branch or its latest + release until this is resolved. +2. Fullscreen VRR is broken but should be fixed once wlroots 0.18.1 is released. + Again, if that is a problem we advise to stay with the v0.7 branch in the + short term until fixed. PR #2079 A v0.7 branch has been created for bug fixes beyond `0.7.3` (built with wlroots `0.17`). @@ -63,7 +69,7 @@ have been attributed with a 'Written-by' against each relevant log entry. ``` - Add resistance when dragging tiled/maximized windows with config option - ``. PR #2009 + ``. PR #2009 #2056 - Implement support for renderer loss recovery. Written-by: @kode54 PR #1997 - Support xinitrc scripts to configure XWayland server on launch. PR #1963 - Add theme option `window.button.width` to set window button size. @@ -85,6 +91,9 @@ have been attributed with a 'Written-by' against each relevant log entry. ### Fixed +- Make tablet rotation follow output rotation. Written-by: @jp7677. PR #2060 +- Fix error when launching windowed Chromium. PR #2069 +- Fix empty `XKB_DEFAULT_LAYOUT` bug. PR #2061 - Take into account CSD borders when unconstraining XDG popups. PR #2016 - Choose xdg-popup output depending on xdg-positioner PR #2016 - Fix wlroots-0.18 regression causing flicker with some layer-shell clients like @@ -98,7 +107,6 @@ have been attributed with a 'Written-by' against each relevant log entry. `map_request` handler to avoid visual glitches with some apps. PR #1529 - Disable pango glyph position rounding to avoid text geometry jump around when changing scale. -- Verify source surface for `xdg_activation` request ### Changed @@ -1517,7 +1525,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.3...HEAD +[unreleased]: https://github.com/labwc/labwc/compare/0.8.0...HEAD +[0.8.0]: https://github.com/labwc/labwc/compare/0.7.3...0.8.0 [0.7.4]: https://github.com/labwc/labwc/compare/0.7.3...0.7.4 [0.7.3]: https://github.com/labwc/labwc/compare/0.7.2...0.7.3 [0.7.2]: https://github.com/labwc/labwc/compare/0.7.1...0.7.2 From 98c876d79644eb89aded1db6fba57e95b5a3210f Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Fri, 16 Aug 2024 16:38:54 +0100 Subject: [PATCH 110/232] build: bump version to 0.8.0 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 267b91b3..4761fbc9 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'labwc', 'c', - version: '0.7.3', + version: '0.8.0', license: 'GPL-2.0-only', meson_version: '>=0.59.0', default_options: [ From f31457392b392379aa0ae12ae7f13e854bbed7ed Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 13 Aug 2024 10:01:49 +0900 Subject: [PATCH 111/232] xwayland: sync foreign-toplevel and associated outputs on re-map Fixes a bug that `zwlr_foreign_toplevel_handle_v1::output_enter` is not sent when a xwayland surface is re-mapped (e.g. opening Slack desktop app when it's running in background). --- src/xwayland.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/xwayland.c b/src/xwayland.c index 738c2b17..b98a02c2 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -738,15 +738,6 @@ xwayland_view_map(struct view *view) view->scene_node = &tree->node; } - /* - * Exclude unfocusable views from wlr-foreign-toplevel. These - * views (notifications, floating toolbars, etc.) should not be - * shown in taskbars/docks/etc. - */ - if (!view->toplevel.handle && view_is_focusable(view)) { - init_foreign_toplevel(view); - } - if (!view->been_mapped) { check_natural_geometry(view); set_initial_position(view, xwayland_surface); @@ -760,6 +751,16 @@ xwayland_view_map(struct view *view) view_moved(view); } + /* + * Exclude unfocusable views from wlr-foreign-toplevel. These + * views (notifications, floating toolbars, etc.) should not be + * shown in taskbars/docks/etc. + */ + if (!view->toplevel.handle && view_is_focusable(view)) { + init_foreign_toplevel(view); + foreign_toplevel_update_outputs(view); + } + /* Add commit here, as xwayland map/unmap can change the wlr_surface */ wl_signal_add(&xwayland_surface->surface->events.commit, &view->commit); view->commit.notify = handle_commit; @@ -797,7 +798,6 @@ xwayland_view_unmap(struct view *view, bool client_request) out: if (client_request && view->toplevel.handle) { wlr_foreign_toplevel_handle_v1_destroy(view->toplevel.handle); - view->toplevel.handle = NULL; } } From 8b12b50137859fef979b46c1ab9c84465b60a95a Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Tue, 13 Aug 2024 10:02:46 +0900 Subject: [PATCH 112/232] xdg: destroy foreign toplevel handle on unmap xdg-shell protocol says: All active operations (e.g., move, resize) are canceled and all attributes (e.g. title, state, stacking, ...) are discarded for an xdg_toplevel surface when it is unmapped. So, when a xdg-toplevel is unmapped (not minimized), the corresponding foreign handler should be destroyed to reset attributes. --- src/xdg.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/xdg.c b/src/xdg.c index b75f864f..23bceba2 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -651,8 +651,6 @@ xdg_toplevel_view_map(struct view *view) struct wlr_xdg_surface *xdg_surface = xdg_surface_from_view(view); wlr_scene_node_set_enabled(&view->scene_tree->node, true); if (!view->been_mapped) { - init_foreign_toplevel(view); - if (view_wants_decorations(view)) { view_set_ssd_mode(view, LAB_SSD_MODE_FULL); } else { @@ -687,6 +685,11 @@ xdg_toplevel_view_map(struct view *view) view_moved(view); } + if (!view->toplevel.handle) { + init_foreign_toplevel(view); + foreign_toplevel_update_outputs(view); + } + view_impl_map(view); view->been_mapped = true; } @@ -699,6 +702,15 @@ xdg_toplevel_view_unmap(struct view *view, bool client_request) wlr_scene_node_set_enabled(&view->scene_tree->node, false); view_impl_unmap(view); } + + /* + * If the view was explicitly unmapped by the client (rather + * than just minimized), destroy the foreign toplevel handle so + * the unmapped view doesn't show up in panels and the like. + */ + if (client_request && view->toplevel.handle) { + wlr_foreign_toplevel_handle_v1_destroy(view->toplevel.handle); + } } static pid_t From 33820c2ad12bd7ec14f880d15f5b40657e765db1 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 12 Aug 2024 22:07:53 +0100 Subject: [PATCH 113/232] xwayland: remove dissociate work-around ...to cope with pre-wlroots-0.18.0 behaviour resulting in dissociate events being emitted without a prior associate one. --- src/xwayland.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/xwayland.c b/src/xwayland.c index b98a02c2..c5034ed1 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -279,19 +279,8 @@ handle_dissociate(struct wl_listener *listener, void *data) struct xwayland_view *xwayland_view = wl_container_of(listener, xwayland_view, dissociate); - if (!xwayland_view->base.mappable.connected) { - /* - * In some cases wlroots fails to emit the associate event - * due to an early return in xwayland_surface_associate(). - * This is arguably a wlroots bug, but nevertheless it - * should not bring down labwc. - * - * TODO: Potentially remove when starting to track - * wlroots 0.18 and it got fixed upstream. - */ - wlr_log(WLR_ERROR, "dissociate received before associate"); - return; - } + /* https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4524 */ + assert(xwayland_view->base.mappable.connected); mappable_disconnect(&xwayland_view->base.mappable); } From 497d3d8e055b10969f9842ec83c2f8ecaa6a70e7 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Wed, 7 Aug 2024 23:28:30 +0200 Subject: [PATCH 114/232] output: set 'labwc' as app_id and title for nested outputs --- src/output.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/output.c b/src/output.c index b9ac9855..a94c7c2d 100644 --- a/src/output.c +++ b/src/output.c @@ -323,6 +323,13 @@ new_output_notify(struct wl_listener *listener, void *data) } } + if (wlr_output_is_wl(wlr_output)) { + char title[64]; + snprintf(title, sizeof(title), "%s - %s", "labwc", wlr_output->name); + wlr_wl_output_set_title(wlr_output, title); + wlr_wl_output_set_app_id(wlr_output, "labwc"); + } + /* * We offer any display as available for lease, some apps like * gamescope want to take ownership of a display when they can From 7125a312aab08970420776dfc3420e30b4997f6e Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Sat, 17 Aug 2024 14:46:35 +0200 Subject: [PATCH 115/232] ssd: do not draw background for individual buttons --- include/ssd-internal.h | 11 ++----- src/ssd/ssd-part.c | 49 ++++-------------------------- src/ssd/ssd-titlebar.c | 67 ++++++++++++++++++++++++------------------ 3 files changed, 46 insertions(+), 81 deletions(-) diff --git a/include/ssd-internal.h b/include/ssd-internal.h index fda196e6..c6e3ba77 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -22,7 +22,6 @@ struct ssd_button { struct wlr_scene_node *hover; struct wlr_scene_node *toggled; struct wlr_scene_node *toggled_hover; - struct wlr_scene_node *background; struct wlr_scene_tree *icon_tree; struct wlr_scene_tree *hover_tree; @@ -127,17 +126,11 @@ struct ssd_part *add_scene_buffer( struct wlr_scene_tree *parent, struct wlr_buffer *buffer, int x, int y); struct ssd_part *add_scene_button( struct wl_list *part_list, enum ssd_part_type type, - struct wlr_scene_tree *parent, float *bg_color, - struct wlr_buffer *icon_buffer, struct wlr_buffer *hover_buffer, - int x, struct view *view); + struct wlr_scene_tree *parent, struct wlr_buffer *icon_buffer, + struct wlr_buffer *hover_buffer, int x, struct view *view); void add_toggled_icon(struct ssd_button *button, struct wl_list *part_list, enum ssd_part_type type, struct wlr_buffer *icon_buffer, struct wlr_buffer *hover_buffer); -struct ssd_part *add_scene_button_corner( - struct wl_list *part_list, enum ssd_part_type type, - enum ssd_part_type corner_type, struct wlr_scene_tree *parent, - struct wlr_buffer *corner_buffer, struct wlr_buffer *icon_buffer, - struct wlr_buffer *hover_buffer, int x, struct view *view); /* SSD internal helpers */ struct ssd_part *ssd_get_part( diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c index 5c903609..9c3294e2 100644 --- a/src/ssd/ssd-part.c +++ b/src/ssd/ssd-part.c @@ -78,42 +78,6 @@ add_scene_buffer(struct wl_list *list, enum ssd_part_type type, return part; } -struct ssd_part * -add_scene_button_corner(struct wl_list *part_list, enum ssd_part_type type, - enum ssd_part_type corner_type, struct wlr_scene_tree *parent, - struct wlr_buffer *corner_buffer, struct wlr_buffer *icon_buffer, - struct wlr_buffer *hover_buffer, int x, struct view *view) -{ - int offset_x; - float invisible[4] = { 0, 0, 0, 0 }; - - if (corner_type == LAB_SSD_PART_CORNER_TOP_LEFT) { - offset_x = rc.theme->border_width; - } else if (corner_type == LAB_SSD_PART_CORNER_TOP_RIGHT) { - offset_x = 0; - } else { - assert(false && "invalid corner button type"); - wlr_log(WLR_ERROR, "invalid corner button type"); - abort(); - } - - struct ssd_part *button_root = add_scene_part(part_list, corner_type); - parent = wlr_scene_tree_create(parent); - button_root->node = &parent->node; - wlr_scene_node_set_position(button_root->node, x, 0); - - /* - * Background, x and y adjusted for border_width which is - * already included in rendered theme.c / corner_buffer - */ - add_scene_buffer(part_list, corner_type, parent, corner_buffer, - -offset_x, -rc.theme->border_width); - - /* Finally just put a usual theme button on top, using an invisible hitbox */ - add_scene_button(part_list, type, parent, invisible, icon_buffer, hover_buffer, 0, view); - return button_root; -} - static struct wlr_box get_scale_box(struct wlr_buffer *buffer, double container_width, double container_height) @@ -142,19 +106,19 @@ get_scale_box(struct wlr_buffer *buffer, double container_width, struct ssd_part * add_scene_button(struct wl_list *part_list, enum ssd_part_type type, - struct wlr_scene_tree *parent, float *bg_color, - struct wlr_buffer *icon_buffer, struct wlr_buffer *hover_buffer, - int x, struct view *view) + struct wlr_scene_tree *parent, struct wlr_buffer *icon_buffer, + struct wlr_buffer *hover_buffer, int x, struct view *view) { struct ssd_part *button_root = add_scene_part(part_list, type); parent = wlr_scene_tree_create(parent); button_root->node = &parent->node; wlr_scene_node_set_position(button_root->node, x, 0); - /* Background */ - struct ssd_part *bg_rect = add_scene_rect(part_list, type, parent, + /* Hitbox */ + float invisible[4] = { 0, 0, 0, 0 }; + add_scene_rect(part_list, type, parent, rc.theme->window_button_width, rc.theme->title_height, 0, 0, - bg_color); + invisible); /* Icon */ struct wlr_scene_tree *icon_tree = wlr_scene_tree_create(parent); @@ -186,7 +150,6 @@ add_scene_button(struct wl_list *part_list, enum ssd_part_type type, button->view = view; button->normal = icon_part->node; button->hover = hover_part->node; - button->background = bg_rect->node; button->toggled = NULL; button->toggled_hover = NULL; button->icon_tree = icon_tree; diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 70d9ca6b..ba4dc2bf 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -88,30 +88,34 @@ ssd_titlebar_create(struct ssd *ssd) } wl_list_init(&subtree->parts); - /* Title */ + /* Background */ add_scene_rect(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent, - width - theme->window_button_width * SSD_BUTTON_COUNT, - theme->title_height, theme->window_button_width, 0, color); + width - theme->window_button_width * 2, theme->title_height, + theme->window_button_width, 0, color); + add_scene_buffer(&subtree->parts, LAB_SSD_PART_CORNER_TOP_LEFT, parent, + corner_top_left, -rc.theme->border_width, -rc.theme->border_width); + add_scene_buffer(&subtree->parts, LAB_SSD_PART_CORNER_TOP_RIGHT, parent, + corner_top_right, width - theme->window_button_width, + -rc.theme->border_width); + /* Buttons */ - add_scene_button_corner(&subtree->parts, - LAB_SSD_BUTTON_WINDOW_MENU, LAB_SSD_PART_CORNER_TOP_LEFT, parent, - corner_top_left, menu_button_unpressed, menu_button_hover, 0, view); + add_scene_button(&subtree->parts, LAB_SSD_BUTTON_WINDOW_MENU, parent, + menu_button_unpressed, menu_button_hover, 0, view); add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent, - color, iconify_button_unpressed, iconify_button_hover, + iconify_button_unpressed, iconify_button_hover, width - theme->window_button_width * 3, view); /* Maximize button has an alternate state when maximized */ struct ssd_part *btn_max_root = add_scene_button( &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, parent, - color, maximize_button_unpressed, maximize_button_hover, + maximize_button_unpressed, maximize_button_hover, width - theme->window_button_width * 2, view); struct ssd_button *btn_max = node_ssd_button_from_node(btn_max_root->node); add_toggled_icon(btn_max, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, restore_button_unpressed, restore_button_hover); - add_scene_button_corner(&subtree->parts, - LAB_SSD_BUTTON_CLOSE, LAB_SSD_PART_CORNER_TOP_RIGHT, parent, - corner_top_right, close_button_unpressed, close_button_hover, + add_scene_button(&subtree->parts, LAB_SSD_BUTTON_CLOSE, parent, + close_button_unpressed, close_button_hover, width - theme->window_button_width * 1, view); } FOR_EACH_END @@ -138,27 +142,25 @@ is_direct_child(struct wlr_scene_node *node, struct ssd_sub_tree *subtree) static void set_squared_corners(struct ssd *ssd, bool enable) { + struct view *view = ssd->view; + int width = view->current.width; + struct theme *theme = view->server->theme; + struct ssd_part *part; struct ssd_sub_tree *subtree; - enum ssd_part_type ssd_type[2] = { LAB_SSD_BUTTON_WINDOW_MENU, LAB_SSD_BUTTON_CLOSE }; + int x = enable ? 0 : theme->window_button_width; FOR_EACH_STATE(ssd, subtree) { - for (size_t i = 0; i < ARRAY_SIZE(ssd_type); i++) { - part = ssd_get_part(&subtree->parts, ssd_type[i]); - struct ssd_button *button = node_ssd_button_from_node(part->node); + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); + wlr_scene_node_set_position(part->node, x, 0); + wlr_scene_rect_set_size( + wlr_scene_rect_from_node(part->node), width - 2 * x, theme->title_height); - /* Toggle background between invisible and titlebar background color */ - struct wlr_scene_rect *rect = wlr_scene_rect_from_node(button->background); - wlr_scene_rect_set_color(rect, !enable ? (float[4]) {0, 0, 0, 0} : ( - subtree == &ssd->titlebar.active - ? rc.theme->window_active_title_bg_color - : rc.theme->window_inactive_title_bg_color)); + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_CORNER_TOP_LEFT); + wlr_scene_node_set_enabled(part->node, !enable); - /* Toggle rounded corner image itself */ - struct wlr_scene_node *rounded_corner = - wl_container_of(part->node->link.prev, rounded_corner, link); - wlr_scene_node_set_enabled(rounded_corner, !enable); - } + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_CORNER_TOP_RIGHT); + wlr_scene_node_set_enabled(part->node, !enable); } FOR_EACH_END } @@ -212,14 +214,14 @@ ssd_titlebar_update(struct ssd *ssd) struct ssd_part *part; struct ssd_sub_tree *subtree; + int bg_offset = maximized || tiled_not_maximized ? 0 : theme->window_button_width; FOR_EACH_STATE(ssd, subtree) { wl_list_for_each(part, &subtree->parts, link) { switch (part->type) { case LAB_SSD_PART_TITLEBAR: wlr_scene_rect_set_size( wlr_scene_rect_from_node(part->node), - width - theme->window_button_width * SSD_BUTTON_COUNT, - theme->title_height); + width - bg_offset * 2, theme->title_height); continue; case LAB_SSD_BUTTON_ICONIFY: if (is_direct_child(part->node, subtree)) { @@ -233,12 +235,19 @@ ssd_titlebar_update(struct ssd *ssd) width - theme->window_button_width * 2, 0); } continue; - case LAB_SSD_PART_CORNER_TOP_RIGHT: + case LAB_SSD_BUTTON_CLOSE: if (is_direct_child(part->node, subtree)) { wlr_scene_node_set_position(part->node, width - theme->window_button_width * 1, 0); } continue; + case LAB_SSD_PART_CORNER_TOP_RIGHT: + if (is_direct_child(part->node, subtree)) { + wlr_scene_node_set_position(part->node, + width - theme->window_button_width * 1, + -rc.theme->border_width); + } + continue; default: continue; } From 05bbb9b3862eccca5ac84bc9a11f4d8b6296caa7 Mon Sep 17 00:00:00 2001 From: Weblate Date: Sat, 17 Aug 2024 22:31:07 +0200 Subject: [PATCH 116/232] Translation updates from weblate Co-authored-by: Peter Jespersen --- po/LINGUAS | 2 +- po/da.po | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 po/da.po diff --git a/po/LINGUAS b/po/LINGUAS index d2ca2b20..699dfa24 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1 +1 @@ -ar cs de el es et eu fa fi fr gl hu id it ja ka ko lt nl pa pl pt ru sv tr uk zh_CN +ar cs da de el es et eu fa fi fr gl hu id it ja ka ko lt nl pa pl pt ru sv tr uk zh_CN diff --git a/po/da.po b/po/da.po new file mode 100644 index 00000000..ffa4583d --- /dev/null +++ b/po/da.po @@ -0,0 +1,69 @@ +# 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-03-17 11:06+1000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: da\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: src/menu/menu.c:829 +msgid "Reconfigure" +msgstr "" + +#: src/menu/menu.c:831 +msgid "Exit" +msgstr "" + +#: src/menu/menu.c:847 +msgid "Minimize" +msgstr "" + +#: src/menu/menu.c:849 +msgid "Maximize" +msgstr "" + +#: src/menu/menu.c:851 +msgid "Fullscreen" +msgstr "" + +#: src/menu/menu.c:853 +msgid "Roll Up/Down" +msgstr "" + +#: src/menu/menu.c:855 +msgid "Decorations" +msgstr "" + +#: src/menu/menu.c:857 +msgid "Always on Top" +msgstr "" + +#: src/menu/menu.c:862 +msgid "Move Left" +msgstr "" + +#: src/menu/menu.c:869 +msgid "Move Right" +msgstr "" + +#: src/menu/menu.c:874 +msgid "Always on Visible Workspace" +msgstr "" + +#: src/menu/menu.c:877 src/config/rcxml.c:1497 +msgid "Workspace" +msgstr "" + +#: src/menu/menu.c:880 +msgid "Close" +msgstr "" From c4b8b5319833d22f4ce064465270b59e5eadf02d Mon Sep 17 00:00:00 2001 From: Weblate Date: Sun, 18 Aug 2024 22:01:17 +0200 Subject: [PATCH 117/232] Translation updates from weblate Co-authored-by: Peter Jespersen Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/da/ Translation: Labwc/labwc --- po/da.po | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/po/da.po b/po/da.po index ffa4583d..fc075a8f 100644 --- a/po/da.po +++ b/po/da.po @@ -8,62 +8,65 @@ msgstr "" "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-03-17 11:06+1000\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: Automatically generated\n" -"Language-Team: none\n" +"PO-Revision-Date: 2024-08-18 20:01+0000\n" +"Last-Translator: Peter Jespersen \n" +"Language-Team: Danish \n" "Language: da\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:829 msgid "Reconfigure" -msgstr "" +msgstr "Genindstil" #: src/menu/menu.c:831 msgid "Exit" -msgstr "" +msgstr "Afslut" #: src/menu/menu.c:847 msgid "Minimize" -msgstr "" +msgstr "Minimer" #: src/menu/menu.c:849 msgid "Maximize" -msgstr "" +msgstr "Maksimer" #: src/menu/menu.c:851 msgid "Fullscreen" -msgstr "" +msgstr "Fuldskærm" #: src/menu/menu.c:853 msgid "Roll Up/Down" -msgstr "" +msgstr "Rul Op/Ned" #: src/menu/menu.c:855 msgid "Decorations" -msgstr "" +msgstr "Dekorationer" #: src/menu/menu.c:857 msgid "Always on Top" -msgstr "" +msgstr "Altid øverst" #: src/menu/menu.c:862 msgid "Move Left" -msgstr "" +msgstr "Flyt til venstre" #: src/menu/menu.c:869 msgid "Move Right" -msgstr "" +msgstr "Flyt til højre" #: src/menu/menu.c:874 msgid "Always on Visible Workspace" -msgstr "" +msgstr "Altid på synlig arbejdsplads" #: src/menu/menu.c:877 src/config/rcxml.c:1497 msgid "Workspace" -msgstr "" +msgstr "Arbejdsplads" #: src/menu/menu.c:880 msgid "Close" -msgstr "" +msgstr "Luk" From 5ae9eed1ac1efd3cd5e53928971a27a1ed0d306c Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Tue, 20 Aug 2024 18:09:17 +0200 Subject: [PATCH 118/232] src/menu: prevent delayed pipe menu response on item destroy (#2094) Without this patch we would happily access `pipe_ctx->item` members even if the item had been destroyed in the meantime. --- include/menu/menu.h | 1 + src/menu/menu.c | 58 +++++++++++++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 18 deletions(-) diff --git a/include/menu/menu.h b/include/menu/menu.h index fee7184b..3b5fa55b 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -39,6 +39,7 @@ struct menuitem { struct wlr_scene_tree *tree; struct menu_scene normal; struct menu_scene selected; + struct menu_pipe_context *pipe_ctx; struct wl_list link; /* menu.menuitems */ }; diff --git a/src/menu/menu.c b/src/menu/menu.c index 143a8233..23b5d2a1 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -40,6 +40,16 @@ static struct menu *current_menu; static bool waiting_for_pipe_menu; static struct menuitem *selected_item; +struct menu_pipe_context { + struct server *server; + struct menuitem *item; + struct buf buf; + struct wl_event_source *event_read; + struct wl_event_source *event_timeout; + pid_t pid; + int pipe_fd; +}; + /* TODO: split this whole file into parser.c and actions.c*/ static bool @@ -347,6 +357,9 @@ fill_item(char *nodename, char *content) static void item_destroy(struct menuitem *item) { + if (item->pipe_ctx) { + item->pipe_ctx->item = NULL; + } wl_list_remove(&item->link); action_list_free(&item->actions); wlr_scene_node_destroy(&item->tree->node); @@ -1086,27 +1099,19 @@ menu_open_root(struct menu *menu, int x, int y) selected_item = NULL; } -struct pipe_context { - struct server *server; - struct menuitem *item; - struct buf buf; - struct wl_event_source *event_read; - struct wl_event_source *event_timeout; - pid_t pid; - int pipe_fd; -}; - static void -create_pipe_menu(struct pipe_context *ctx) +create_pipe_menu(struct menu_pipe_context *ctx) { + assert(ctx->item); + struct menu *pipe_parent = ctx->item->parent; if (!pipe_parent) { - wlr_log(WLR_ERROR, "[pipemenu %ld] invalid parent", + wlr_log(WLR_INFO, "[pipemenu %ld] invalid parent", (long)ctx->pid); return; } if (!pipe_parent->scene_tree->node.enabled) { - wlr_log(WLR_ERROR, "[pipemenu %ld] parent menu already closed", + wlr_log(WLR_INFO, "[pipemenu %ld] parent menu already closed", (long)ctx->pid); return; } @@ -1153,12 +1158,15 @@ restore_menus: } static void -pipemenu_ctx_destroy(struct pipe_context *ctx) +pipemenu_ctx_destroy(struct menu_pipe_context *ctx) { wl_event_source_remove(ctx->event_read); wl_event_source_remove(ctx->event_timeout); spawn_piped_close(ctx->pid, ctx->pipe_fd); buf_reset(&ctx->buf); + if (ctx->item) { + ctx->item->pipe_ctx = NULL; + } free(ctx); waiting_for_pipe_menu = false; } @@ -1166,9 +1174,9 @@ pipemenu_ctx_destroy(struct pipe_context *ctx) static int handle_pipemenu_timeout(void *_ctx) { - struct pipe_context *ctx = _ctx; + struct menu_pipe_context *ctx = _ctx; wlr_log(WLR_ERROR, "[pipemenu %ld] timeout reached, killing %s", - (long)ctx->pid, ctx->item->execute); + (long)ctx->pid, ctx->item ? ctx->item->execute : "n/a"); kill(ctx->pid, SIGTERM); pipemenu_ctx_destroy(ctx); return 0; @@ -1183,11 +1191,19 @@ starts_with_less_than(const char *s) static int handle_pipemenu_readable(int fd, uint32_t mask, void *_ctx) { - struct pipe_context *ctx = _ctx; + struct menu_pipe_context *ctx = _ctx; /* two 4k pages + 1 NULL byte */ char data[8193]; ssize_t size; + if (!ctx->item) { + /* parent menu item got destroyed in the meantime */ + wlr_log(WLR_INFO, "[pipemenu %ld] parent menu item destroyed", + (long)ctx->pid); + kill(ctx->pid, SIGTERM); + goto clean_up; + } + do { /* leave space for terminating NULL byte */ size = read(fd, data, sizeof(data) - 1); @@ -1235,6 +1251,11 @@ parse_pipemenu(struct menuitem *item) return; } + if (item->pipe_ctx) { + wlr_log(WLR_ERROR, "item already has a pipe context attached"); + return; + } + int pipe_fd = 0; pid_t pid = spawn_piped(item->execute, &pipe_fd); if (pid <= 0) { @@ -1243,12 +1264,13 @@ parse_pipemenu(struct menuitem *item) } waiting_for_pipe_menu = true; - struct pipe_context *ctx = znew(*ctx); + struct menu_pipe_context *ctx = znew(*ctx); ctx->server = item->parent->server; ctx->item = item; ctx->pid = pid; ctx->pipe_fd = pipe_fd; ctx->buf = BUF_INIT; + item->pipe_ctx = ctx; ctx->event_read = wl_event_loop_add_fd(ctx->server->wl_event_loop, pipe_fd, WL_EVENT_READABLE, handle_pipemenu_readable, ctx); From a368bbee6a67271eb665c9a1b45e8cc1ec214a56 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 23 Sep 2023 15:07:25 +0100 Subject: [PATCH 119/232] Add unit test for common/buf.c --- CONTRIBUTING.md | 30 +++++++++++++++++++--- meson.build | 5 ++++ meson_options.txt | 1 + t/.gitignore | 1 + t/buf-simple.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++ t/meson.build | 27 ++++++++++++++++++++ 6 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 t/.gitignore create mode 100644 t/buf-simple.c create mode 100644 t/meson.build diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 655376c5..0f79ef5f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,9 +14,10 @@ - [4.3.3 The use of GNU extensions](#the-use-of-gnu-extensions) - [4.3.4 Naming Conventions](#naming-conventions) - [5. Commit Messages](#commit-messages) -- [6. Submitting Patches](#submitting-patches) -- [7. Native Language Support](#native-language-support) -- [8. Upversion](#upversion) +- [6. Unit Tests](#unit-tests) +- [7. Submitting Patches](#submitting-patches) +- [8. Native Language Support](#native-language-support) +- [9. Upversion](#upversion) # How to Contribute @@ -345,6 +346,29 @@ This first line should: And please wrap the commit message at max 74 characters, otherwise `git log` and similar look so weird. URLs and other references are exempt. +# Unit Tests + +## Introduction + +The tests live in the `t/` directory. + +In the bigger scheme of validating that the compositor meets users' needs, unit +tests do not contribute a great deal. However, they have a role to play in +providing some verification that stand-alone functions behave as expected. + +On this project, writing unit-tests is not compulsory nor do we measure +coverage. The inclusion of the t/ directory does not signifiy a move towards +test-driven development. We intend to use unit tests sparingly and only when +devs find them useful. + +## Usage + +From repo top level directory: + + meson setup -Dtest=enabled build + meson compile -C build/ + meson test --verbose -C build/ + # Submitting patches Base both bugfixes and new features on `master`. diff --git a/meson.build b/meson.build index 4761fbc9..8f9b5a97 100644 --- a/meson.build +++ b/meson.build @@ -130,6 +130,11 @@ subdir('include') subdir('src') subdir('docs') +dep_cmocka = dependency('cmocka', required: get_option('test')) +if dep_cmocka.found() + subdir('t') +endif + executable( meson.project_name(), labwc_sources, diff --git a/meson_options.txt b/meson_options.txt index d9cb73a8..4d6e8cd5 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -3,3 +3,4 @@ option('xwayland', type: 'feature', value: 'auto', description: 'Enable support option('svg', type: 'feature', value: 'enabled', description: 'Enable svg window buttons') option('nls', type: 'feature', value: 'auto', description: 'Enable native language support') option('static_analyzer', type: 'feature', value: 'disabled', description: 'Run gcc static analyzer') +option('test', type: 'feature', value: 'disabled', description: 'Run tests') diff --git a/t/.gitignore b/t/.gitignore new file mode 100644 index 00000000..a8d6b6c1 --- /dev/null +++ b/t/.gitignore @@ -0,0 +1 @@ +*.t diff --git a/t/buf-simple.c b/t/buf-simple.c new file mode 100644 index 00000000..47722116 --- /dev/null +++ b/t/buf-simple.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +#include +#include +#include +#include "common/buf.h" +#include "common/mem.h" + +static void +test_expand_title(void **state) +{ + (void)state; + + struct buf s = BUF_INIT; + + char TEMPLATE[] = "foo ~/bar"; + char expect[4096]; + snprintf(expect, sizeof(expect), "foo %s/bar", getenv("HOME")); + + buf_add(&s, TEMPLATE); + assert_string_equal(s.data, TEMPLATE); + assert_int_equal(s.len, strlen(TEMPLATE)); + + // Resolve ~ + buf_expand_tilde(&s); + assert_string_equal(s.data, expect); + assert_int_equal(s.len, strlen(expect)); + + setenv("bar", "BAR", 1); + + // Resolve $bar and ${bar} + s.len = 0; + buf_add(&s, "foo $bar baz"); + buf_expand_shell_variables(&s); + assert_string_equal(s.data, "foo BAR baz"); + assert_int_equal(s.len, 11); + + s.len = 0; + buf_add(&s, "foo ${bar} baz"); + buf_expand_shell_variables(&s); + assert_string_equal(s.data, "foo BAR baz"); + assert_int_equal(s.len, 11); + + // Don't resolve $() + s.len = 0; + buf_add(&s, "foo $(bar) baz"); + buf_expand_shell_variables(&s); + assert_string_equal(s.data, "foo $(bar) baz"); + assert_int_equal(s.len, 14); + + unsetenv("bar"); + free(s.data); +} + +int main(int argc, char **argv) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_expand_title), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/t/meson.build b/t/meson.build new file mode 100644 index 00000000..5517b973 --- /dev/null +++ b/t/meson.build @@ -0,0 +1,27 @@ +test_lib = static_library( + 'test_lib', + sources: files( + '../src/common/buf.c', + '../src/common/mem.c', + '../src/common/string-helpers.c' + ), + include_directories: [labwc_inc], + dependencies: [dep_cmocka], +) + +tests = [ + 'buf-simple', +] + +foreach t : tests + test( + 'test_@0@'.format(t), + executable( + 'test_@0@'.format(t), + sources: '@0@.c'.format(t), + include_directories: [labwc_inc], + link_with: [test_lib], + ), + is_parallel: false, + ) +endforeach From 0552c6b7f0008ba31e997a0a4dca56e89ff90627 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 9 May 2024 21:20:15 +0100 Subject: [PATCH 120/232] menu: support titles ...defined by ``. Also add the theme option `menu.title.bg.color: #589bda` The following will be added in separate commits - menu.title.bg.border.color: #7cb6ec - menu.title.text.color: #ffffff - menu.title.text.justify: center --- docs/labwc-menu.5.scd | 10 +++- docs/labwc-theme.5.scd | 4 ++ docs/themerc | 1 + include/menu/menu.h | 7 +++ include/theme.h | 2 + src/menu/menu.c | 102 +++++++++++++++++++++++++++++++---------- src/theme.c | 6 +++ 7 files changed, 106 insertions(+), 26 deletions(-) diff --git a/docs/labwc-menu.5.scd b/docs/labwc-menu.5.scd index b4688a07..a50fa254 100644 --- a/docs/labwc-menu.5.scd +++ b/docs/labwc-menu.5.scd @@ -25,7 +25,7 @@ The menu file must be entirely enclosed within and - @@ -33,6 +33,9 @@ The menu file must be entirely enclosed within and ...some content... + + + @@ -63,6 +66,11 @@ The menu file must be entirely enclosed within and *menu.separator* Horizontal line. +*menu.separator.label* + In a "separator" element, the label attribute transforms the separator + from a horizontal line to a menu title (heading) with the text specified + by label in it. + *menu.execute* Command to execute for pipe menu. See details below. diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index 5b90a962..29a832f5 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -174,6 +174,10 @@ elements are not listed here, but are supported. *menu.separator.color* Menu separator color. Default #888888. +*menu.title.bg.color* + Menu title color. Default #589bda. + Note: A menu title is a separator with a label. + *osd.bg.color* Background color of on-screen-display. Inherits *window.active.title.bg.color* if not set. diff --git a/docs/themerc b/docs/themerc index b2dcb2fe..e54dfb96 100644 --- a/docs/themerc +++ b/docs/themerc @@ -62,6 +62,7 @@ menu.separator.width: 1 menu.separator.padding.width: 6 menu.separator.padding.height: 3 menu.separator.color: #888888 +menu.title.bg.color: #589bda # on screen display (window-cycle dialog) osd.bg.color: #e1dedb diff --git a/include/menu/menu.h b/include/menu/menu.h index 3b5fa55b..9edf8b76 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -27,6 +27,12 @@ struct menu_scene { struct scaled_font_buffer *buffer; }; +enum menuitem_type { + LAB_MENU_ITEM = 0, + LAB_MENU_SEPARATOR_LINE, + LAB_MENU_TITLE, +}; + struct menuitem { struct wl_list actions; char *execute; @@ -34,6 +40,7 @@ struct menuitem { struct menu *parent; struct menu *submenu; bool selectable; + enum menuitem_type type; int height; int native_width; struct wlr_scene_tree *tree; diff --git a/include/theme.h b/include/theme.h index 39931840..9791f7e9 100644 --- a/include/theme.h +++ b/include/theme.h @@ -75,6 +75,8 @@ struct theme { int menu_separator_padding_height; float menu_separator_color[4]; + float menu_title_bg_color[4]; + int osd_border_width; float osd_bg_color[4]; diff --git a/src/menu/menu.c b/src/menu/menu.c index 23b5d2a1..98b214ff 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -118,26 +118,42 @@ menu_update_width(struct menu *menu) } menu->size.width = max_width + 2 * theme->menu_item_padding_x; + /* + * TODO: This function is getting a bit unwieldy. Consider calculating + * the menu-window width up-front to avoid this post_processing() and + * second-bite-of-the-cherry stuff + */ + /* Update all items for the new size */ wl_list_for_each(item, &menu->menuitems, link) { wlr_scene_rect_set_size( wlr_scene_rect_from_node(item->normal.background), menu->size.width, item->height); - if (!item->selected.background) { - /* This is a separator. They don't have a selected background. */ + /* + * Separator lines are special because they change width with + * the menu. + */ + if (item->type == LAB_MENU_SEPARATOR_LINE) { + int width = menu->size.width + - 2 * theme->menu_separator_padding_width + - 2 * theme->menu_item_padding_x; wlr_scene_rect_set_size( wlr_scene_rect_from_node(item->normal.text), - menu->size.width - 2 * theme->menu_separator_padding_width, - theme->menu_separator_line_thickness); - } else { - /* Usual menu item */ + width, theme->menu_separator_line_thickness); + } + + if (item->selectable) { + /* Only selectable items have item->selected.background */ wlr_scene_rect_set_size( wlr_scene_rect_from_node(item->selected.background), menu->size.width, item->height); - if (item->native_width > max_width || item->submenu || item->execute) { - scaled_font_buffer_set_max_width(item->normal.buffer, - max_width); + } + + if (item->native_width > max_width || item->submenu || item->execute) { + scaled_font_buffer_set_max_width(item->normal.buffer, + max_width); + if (item->selectable) { scaled_font_buffer_set_max_width(item->selected.buffer, max_width); } @@ -188,11 +204,13 @@ item_create(struct menu *menu, const char *text, bool show_arrow) struct menuitem *menuitem = znew(*menuitem); menuitem->parent = menu; menuitem->selectable = true; + menuitem->type = LAB_MENU_ITEM; struct server *server = menu->server; struct theme *theme = server->theme; const char *arrow = show_arrow ? "›" : NULL; + /* TODO: Consider setting this somewhere else */ if (!menu->item_height) { menu->item_height = font_height(&rc.font_menuitem) + 2 * theme->menu_item_padding_y; @@ -275,34 +293,68 @@ separator_create(struct menu *menu, const char *label) struct menuitem *menuitem = znew(*menuitem); menuitem->parent = menu; menuitem->selectable = false; + menuitem->type = string_null_or_empty(label) ? LAB_MENU_SEPARATOR_LINE + : LAB_MENU_TITLE; struct server *server = menu->server; struct theme *theme = server->theme; - menuitem->height = theme->menu_separator_line_thickness + - 2 * theme->menu_separator_padding_height; + if (menuitem->type == LAB_MENU_TITLE) { + menuitem->height = menu->item_height; + menuitem->native_width = font_width(&rc.font_menuitem, label); + } else if (menuitem->type == LAB_MENU_SEPARATOR_LINE) { + menuitem->height = theme->menu_separator_line_thickness + + 2 * theme->menu_separator_padding_height; + } + + /* Menu item root node */ menuitem->tree = wlr_scene_tree_create(menu->scene_tree); node_descriptor_create(&menuitem->tree->node, LAB_NODE_DESC_MENUITEM, menuitem); + + /* Tree to hold background and text/line buffer */ menuitem->normal.tree = wlr_scene_tree_create(menuitem->tree); + + /* Item background nodes */ + float *bg_color = menuitem->type == LAB_MENU_TITLE + ? theme->menu_title_bg_color : theme->menu_items_bg_color; menuitem->normal.background = &wlr_scene_rect_create( menuitem->normal.tree, - menu->size.width, menuitem->height, - theme->menu_items_bg_color)->node; - - int width = menu->size.width - 2 * theme->menu_separator_padding_width; - menuitem->normal.text = &wlr_scene_rect_create( - menuitem->normal.tree, - width > 0 ? width : 0, - theme->menu_separator_line_thickness, - theme->menu_separator_color)->node; + menu->size.width, menuitem->height, bg_color)->node; + /* Draw separator line or title */ + if (menuitem->type == LAB_MENU_TITLE) { + menuitem->normal.buffer = scaled_font_buffer_create(menuitem->normal.tree); + if (!menuitem->normal.buffer) { + wlr_log(WLR_ERROR, "Failed to create menu item '%s'", label); + wlr_scene_node_destroy(&menuitem->tree->node); + free(menuitem); + return NULL; + } + menuitem->normal.text = &menuitem->normal.buffer->scene_buffer->node; + /* Font buffer */ + scaled_font_buffer_update(menuitem->normal.buffer, label, + menuitem->native_width, &rc.font_menuitem, + theme->menu_items_text_color, bg_color, /* arrow */ NULL); + /* Center font nodes */ + int x, y; + x = theme->menu_item_padding_x; + y = (menu->item_height - menuitem->normal.buffer->height) / 2; + wlr_scene_node_set_position(menuitem->normal.text, x, y); + } else { + int nominal_width = theme->menu_min_width; + menuitem->normal.text = &wlr_scene_rect_create( + menuitem->normal.tree, nominal_width, + theme->menu_separator_line_thickness, + theme->menu_separator_color)->node; + wlr_scene_node_set_position(&menuitem->tree->node, 0, menu->size.height); + /* Vertically center-align separator line */ + wlr_scene_node_set_position(menuitem->normal.text, + theme->menu_separator_padding_width + + theme->menu_item_padding_x, + theme->menu_separator_padding_height); + } wlr_scene_node_set_position(&menuitem->tree->node, 0, menu->size.height); - /* Vertically center-align separator line */ - wlr_scene_node_set_position(menuitem->normal.text, - theme->menu_separator_padding_width, - theme->menu_separator_padding_height); - menu->size.height += menuitem->height; wl_list_append(&menu->menuitems, &menuitem->link); wl_list_init(&menuitem->actions); diff --git a/src/theme.c b/src/theme.c index d699773e..fe8dc115 100644 --- a/src/theme.c +++ b/src/theme.c @@ -528,6 +528,8 @@ theme_builtin(struct theme *theme, struct server *server) theme->menu_separator_padding_height = 3; parse_hexstr("#888888", theme->menu_separator_color); + parse_hexstr("#589bda", theme->menu_title_bg_color); + theme->osd_window_switcher_width = 600; theme->osd_window_switcher_width_is_percent = false; theme->osd_window_switcher_padding = 4; @@ -766,6 +768,10 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->menu_separator_color); } + if (match_glob(key, "menu.title.bg.color")) { + parse_hexstr(value, theme->menu_title_bg_color); + } + if (match_glob(key, "osd.bg.color")) { parse_hexstr(value, theme->osd_bg_color); } From 9bc381d9e8ba15ed444768259d10151b21a49d34 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 15 Aug 2024 20:54:49 +0100 Subject: [PATCH 121/232] menu: use theme->menu_item_height instead of menu->item_height ...and set it in theme.c post_processing() --- include/menu/menu.h | 1 - include/theme.h | 1 + src/menu/menu.c | 21 ++++++++------------- src/theme.c | 3 +++ 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/include/menu/menu.h b/include/menu/menu.h index 9edf8b76..980eaa7c 100644 --- a/include/menu/menu.h +++ b/include/menu/menu.h @@ -54,7 +54,6 @@ struct menuitem { struct menu { char *id; char *label; - int item_height; struct menu *parent; struct { diff --git a/include/theme.h b/include/theme.h index 9791f7e9..4ad3855c 100644 --- a/include/theme.h +++ b/include/theme.h @@ -61,6 +61,7 @@ struct theme { int menu_item_padding_x; int menu_item_padding_y; + int menu_item_height; float menu_items_bg_color[4]; float menu_items_text_color[4]; diff --git a/src/menu/menu.c b/src/menu/menu.c index 98b214ff..1abef56a 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -210,12 +210,7 @@ item_create(struct menu *menu, const char *text, bool show_arrow) const char *arrow = show_arrow ? "›" : NULL; - /* TODO: Consider setting this somewhere else */ - if (!menu->item_height) { - menu->item_height = font_height(&rc.font_menuitem) - + 2 * theme->menu_item_padding_y; - } - menuitem->height = menu->item_height; + menuitem->height = theme->menu_item_height; int x, y; menuitem->native_width = font_width(&rc.font_menuitem, text); @@ -235,11 +230,11 @@ item_create(struct menu *menu, const char *text, bool show_arrow) /* Item background nodes */ menuitem->normal.background = &wlr_scene_rect_create( menuitem->normal.tree, - menu->size.width, menu->item_height, + menu->size.width, theme->menu_item_height, theme->menu_items_bg_color)->node; menuitem->selected.background = &wlr_scene_rect_create( menuitem->selected.tree, - menu->size.width, menu->item_height, + menu->size.width, theme->menu_item_height, theme->menu_items_active_bg_color)->node; /* Font nodes */ @@ -268,9 +263,9 @@ item_create(struct menu *menu, const char *text, bool show_arrow) /* Center font nodes */ x = theme->menu_item_padding_x; - y = (menu->item_height - menuitem->normal.buffer->height) / 2; + y = (theme->menu_item_height - menuitem->normal.buffer->height) / 2; wlr_scene_node_set_position(menuitem->normal.text, x, y); - y = (menu->item_height - menuitem->selected.buffer->height) / 2; + y = (theme->menu_item_height - menuitem->selected.buffer->height) / 2; wlr_scene_node_set_position(menuitem->selected.text, x, y); /* Position the item in relation to its menu */ @@ -299,7 +294,7 @@ separator_create(struct menu *menu, const char *label) struct theme *theme = server->theme; if (menuitem->type == LAB_MENU_TITLE) { - menuitem->height = menu->item_height; + menuitem->height = theme->menu_item_height; menuitem->native_width = font_width(&rc.font_menuitem, label); } else if (menuitem->type == LAB_MENU_SEPARATOR_LINE) { menuitem->height = theme->menu_separator_line_thickness + @@ -338,7 +333,7 @@ separator_create(struct menu *menu, const char *label) /* Center font nodes */ int x, y; x = theme->menu_item_padding_x; - y = (menu->item_height - menuitem->normal.buffer->height) / 2; + y = (theme->menu_item_height - menuitem->normal.buffer->height) / 2; wlr_scene_node_set_position(menuitem->normal.text, x, y); } else { int nominal_width = theme->menu_min_width; @@ -849,7 +844,7 @@ menu_configure(struct menu *menu, int lx, int ly, enum menu_align align) ly -= menu->size.height; if (menu->parent) { /* For submenus adjust y to bottom left corner */ - ly += menu->item_height; + ly += theme->menu_item_height; } } wlr_scene_node_set_position(&menu->scene_tree->node, lx, ly); diff --git a/src/theme.c b/src/theme.c index fe8dc115..a22d0936 100644 --- a/src/theme.c +++ b/src/theme.c @@ -1305,6 +1305,9 @@ post_processing(struct theme *theme) theme->title_height = h + 2 * theme->padding_height; } + theme->menu_item_height = font_height(&rc.font_menuitem) + + 2 * theme->menu_item_padding_y; + theme->osd_window_switcher_item_height = font_height(&rc.font_osd) + 2 * theme->osd_window_switcher_item_padding_y + 2 * theme->osd_window_switcher_item_active_border_width; From 107d84cef903b4334e02f66de39a46b33763a2f4 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 17 Aug 2024 13:41:10 +0100 Subject: [PATCH 122/232] scaled-font-buffer.c: initialize `buffer` to avoid bug waiting to happen --- src/common/scaled-font-buffer.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/common/scaled-font-buffer.c b/src/common/scaled-font-buffer.c index f785e2e1..c9382378 100644 --- a/src/common/scaled-font-buffer.c +++ b/src/common/scaled-font-buffer.c @@ -14,13 +14,17 @@ static struct lab_data_buffer * _create_buffer(struct scaled_scene_buffer *scaled_buffer, double scale) { - struct lab_data_buffer *buffer; + struct lab_data_buffer *buffer = NULL; struct scaled_font_buffer *self = scaled_buffer->data; /* Buffer gets free'd automatically along the backing wlr_buffer */ font_buffer_create(&buffer, self->max_width, self->text, &self->font, self->color, self->bg_color, self->arrow, scale); + if (!buffer) { + wlr_log(WLR_ERROR, "font_buffer_create() failed"); + } + self->width = buffer ? buffer->unscaled_width : 0; self->height = buffer ? buffer->unscaled_height : 0; return buffer; From cd961b1ac103d553b949acd2578d599a5b230b51 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Sun, 18 Aug 2024 16:09:39 +0200 Subject: [PATCH 123/232] action: add Enable/DisableTabletMouseEmulation Useful for window rules. --- docs/labwc-actions.5.scd | 4 +++- src/action.c | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index cb6fac63..1c9e2ec1 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -294,8 +294,10 @@ Actions are used in menus and keyboard/mouse bindings. decorations (including those for which the server-side titlebar has been hidden) are not eligible for shading. +**++ +**++ ** - Toggle mouse emulation for drawing tablets on or off. + Enable, disable or toggle mouse emulation for drawing tablets respectively. ** Toggle the screen magnifier on or off at the last magnification level diff --git a/src/action.c b/src/action.c index 69a317bb..c38cba70 100644 --- a/src/action.c +++ b/src/action.c @@ -114,6 +114,8 @@ enum action_type { ACTION_TYPE_SHADE, ACTION_TYPE_UNSHADE, ACTION_TYPE_TOGGLE_SHADE, + ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION, + ACTION_TYPE_DISABLE_TABLET_MOUSE_EMULATION, ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION, ACTION_TYPE_TOGGLE_MAGNIFY, ACTION_TYPE_ZOOM_IN, @@ -173,6 +175,8 @@ const char *action_names[] = { "Shade", "Unshade", "ToggleShade", + "EnableTabletMouseEmulation", + "DisableTabletMouseEmulation", "ToggleTabletMouseEmulation", "ToggleMagnify", "ZoomIn", @@ -1142,6 +1146,12 @@ actions_run(struct view *activator, struct server *server, view_set_shade(view, false); } break; + case ACTION_TYPE_ENABLE_TABLET_MOUSE_EMULATION: + rc.tablet.force_mouse_emulation = true; + break; + case ACTION_TYPE_DISABLE_TABLET_MOUSE_EMULATION: + rc.tablet.force_mouse_emulation = false; + break; case ACTION_TYPE_TOGGLE_TABLET_MOUSE_EMULATION: rc.tablet.force_mouse_emulation = !rc.tablet.force_mouse_emulation; break; From 9a252249c98f62a50782ca77e8c9eab1f32435b0 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Sat, 17 Aug 2024 14:45:25 +0200 Subject: [PATCH 124/232] ssd: add titleLayout setting --- docs/labwc-config.5.scd | 12 +++++++ docs/rc.xml.all | 1 + include/config/rcxml.h | 10 ++++++ src/config/rcxml.c | 73 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index bd03fd6b..4345794f 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -432,6 +432,18 @@ extending outward from the snapped edge. ** The name of the Openbox theme to use. It is not set by default. +** + Selection and order of buttons in a window's titlebar. + The following letters can be used, each only once: + - L: window label (aka. title) + - |: empty space (can be used instead of a title) + - W: window menu + - I: iconify + - M: maximize + - C: close + + Example: WLIMC + ** The radius of server side decoration top corners. Default is 8. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index f71014a6..a1d030b5 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -28,6 +28,7 @@ + WLIMC 8 yes no diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 2b5da588..43687668 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -14,6 +14,7 @@ #include "config/tablet-tool.h" #include "config/libinput.h" #include "resize-indicator.h" +#include "ssd.h" #include "theme.h" enum view_placement_policy { @@ -45,6 +46,11 @@ enum tiling_events_mode { (LAB_TILING_EVENTS_REGION | LAB_TILING_EVENTS_EDGE), }; +struct title_button { + enum ssd_part_type type; + struct wl_list link; +}; + struct usable_area_override { struct border margin; char *output; @@ -75,7 +81,11 @@ struct rcxml { /* theme */ char *theme_name; + struct wl_list title_buttons_left; + struct wl_list title_buttons_right; int corner_radius; + bool show_title; + bool title_layout_loaded; bool ssd_keep_border; bool shadows_enabled; struct font font_activewindow; diff --git a/src/config/rcxml.c b/src/config/rcxml.c index da0bbde3..c67a49e9 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -116,6 +116,59 @@ parse_window_type(const char *type) } } +static void +fill_title_layout(char *nodename, char *content) +{ + char *c, *c2; + enum ssd_part_type type; + struct title_button *item; + struct wl_list *list = &rc.title_buttons_left; + + for (c = content; *c != '\0'; c++) { + for (c2 = content; c2 < c; c2++) { + if (*c2 == *c) { + break; + } + } + if (c2 != c) { + continue; + } + + switch (*c) { + /* case 'N': icon */ + case 'L': + list = &rc.title_buttons_right; + rc.show_title = true; + continue; + case '|': + list = &rc.title_buttons_right; + continue; + case 'W': + type = LAB_SSD_BUTTON_WINDOW_MENU; + break; + case 'I': + type = LAB_SSD_BUTTON_ICONIFY; + break; + case 'M': + type = LAB_SSD_BUTTON_MAXIMIZE; + break; + case 'C': + type = LAB_SSD_BUTTON_CLOSE; + break; + /* case 'S': shade */ + /* case 'D': omnipresent */ + default: + continue; + } + + item = znew(*item); + item->type = type; + wl_list_append(list, &item->link); + } + + rc.title_layout_loaded = true; +} + static void fill_usable_area_override(char *nodename, char *content) { @@ -916,6 +969,8 @@ entry(xmlNode *node, char *nodename, char *content) rc.placement_cascade_offset_y = atoi(content); } else if (!strcmp(nodename, "name.theme")) { rc.theme_name = xstrdup(content); + } else if (!strcmp(nodename, "titlelayout.theme")) { + fill_title_layout(nodename, content); } else if (!strcmp(nodename, "cornerradius.theme")) { rc.corner_radius = atoi(content); } else if (!strcasecmp(nodename, "keepBorder.theme")) { @@ -1236,6 +1291,8 @@ rcxml_init(void) static bool has_run; if (!has_run) { + wl_list_init(&rc.title_buttons_left); + wl_list_init(&rc.title_buttons_right); wl_list_init(&rc.usable_area_overrides); wl_list_init(&rc.keybinds); wl_list_init(&rc.mousebinds); @@ -1253,6 +1310,8 @@ rcxml_init(void) rc.placement_cascade_offset_y = 0; rc.xdg_shell_server_side_deco = true; + rc.show_title = false; + rc.title_layout_loaded = false; rc.ssd_keep_border = true; rc.corner_radius = 8; rc.shadows_enabled = false; @@ -1494,6 +1553,10 @@ post_processing(void) load_default_mouse_bindings(); } + if (!rc.title_layout_loaded) { + fill_title_layout("titlelayout.theme", "WLIMC"); + } + /* * Replace all earlier bindings by later ones * and clear the ones with an empty action list. @@ -1720,6 +1783,16 @@ rcxml_finish(void) zfree(rc.theme_name); zfree(rc.workspace_config.prefix); + struct title_button *p, *p_tmp; + wl_list_for_each_safe(p, p_tmp, &rc.title_buttons_left, link) { + wl_list_remove(&p->link); + zfree(p); + } + wl_list_for_each_safe(p, p_tmp, &rc.title_buttons_right, link) { + wl_list_remove(&p->link); + zfree(p); + } + struct usable_area_override *area, *area_tmp; wl_list_for_each_safe(area, area_tmp, &rc.usable_area_overrides, link) { wl_list_remove(&area->link); From 39ff873d5b368434671ecddb0aae64fb3049ca07 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Sun, 18 Aug 2024 10:49:18 +0200 Subject: [PATCH 125/232] ssd: apply title layout --- include/ssd.h | 1 - src/snap.c | 3 +- src/ssd/ssd-titlebar.c | 173 ++++++++++++++++++++++++++--------------- src/ssd/ssd.c | 7 +- src/theme.c | 28 +++++-- src/view.c | 3 +- src/xwayland.c | 3 +- 7 files changed, 140 insertions(+), 78 deletions(-) diff --git a/include/ssd.h b/include/ssd.h index 19b22450..7a2e7a77 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -5,7 +5,6 @@ #include #include "common/border.h" -#define SSD_BUTTON_COUNT 4 #define SSD_EXTENDED_AREA 8 /* diff --git a/src/snap.c b/src/snap.c index 5f1dbff8..3f188f6f 100644 --- a/src/snap.c +++ b/src/snap.c @@ -220,7 +220,8 @@ snap_shrink_to_next_edge(struct view *view, *geo = view->pending; uint32_t resize_edges; - int min_view_width = rc.theme->window_button_width * SSD_BUTTON_COUNT; + int min_view_width = rc.theme->window_button_width * ( + wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); /* * First shrink the view along the relevant edge. The maximum shrink diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index ba4dc2bf..7326a707 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -99,24 +99,82 @@ ssd_titlebar_create(struct ssd *ssd) -rc.theme->border_width); /* Buttons */ - add_scene_button(&subtree->parts, LAB_SSD_BUTTON_WINDOW_MENU, parent, - menu_button_unpressed, menu_button_hover, 0, view); - add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent, - iconify_button_unpressed, iconify_button_hover, - width - theme->window_button_width * 3, view); + int x = 0; + struct ssd_part *btn_max_root; + struct ssd_button *btn_max; + struct title_button *b; + wl_list_for_each(b, &rc.title_buttons_left, link) { + switch (b->type) { + case LAB_SSD_BUTTON_WINDOW_MENU: + add_scene_button(&subtree->parts, LAB_SSD_BUTTON_WINDOW_MENU, + parent, menu_button_unpressed, menu_button_hover, x, + view); + x += theme->window_button_width; + continue; + case LAB_SSD_BUTTON_ICONIFY: + add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent, + iconify_button_unpressed, iconify_button_hover, x, + view); + x += theme->window_button_width; + continue; + case LAB_SSD_BUTTON_MAXIMIZE: + /* Maximize button has an alternate state when maximized */ + btn_max_root = add_scene_button( + &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, parent, + maximize_button_unpressed, maximize_button_hover, x, view); + btn_max = node_ssd_button_from_node(btn_max_root->node); + add_toggled_icon(btn_max, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, + restore_button_unpressed, restore_button_hover); + x += theme->window_button_width; + continue; + case LAB_SSD_BUTTON_CLOSE: + add_scene_button(&subtree->parts, LAB_SSD_BUTTON_CLOSE, parent, + close_button_unpressed, close_button_hover, x, view); + x += theme->window_button_width; + continue; + default: + assert(false && "invalid titlebar part"); + wlr_log(WLR_ERROR, "invalid titlebar type"); + abort(); + } + } - /* Maximize button has an alternate state when maximized */ - struct ssd_part *btn_max_root = add_scene_button( - &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, parent, - maximize_button_unpressed, maximize_button_hover, - width - theme->window_button_width * 2, view); - struct ssd_button *btn_max = node_ssd_button_from_node(btn_max_root->node); - add_toggled_icon(btn_max, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, - restore_button_unpressed, restore_button_hover); - - add_scene_button(&subtree->parts, LAB_SSD_BUTTON_CLOSE, parent, - close_button_unpressed, close_button_hover, - width - theme->window_button_width * 1, view); + x = width - theme->window_button_width; + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + switch (b->type) { + case LAB_SSD_BUTTON_WINDOW_MENU: + add_scene_button(&subtree->parts, LAB_SSD_BUTTON_WINDOW_MENU, + parent, menu_button_unpressed, menu_button_hover, x, + view); + x -= theme->window_button_width; + continue; + case LAB_SSD_BUTTON_ICONIFY: + add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent, + iconify_button_unpressed, iconify_button_hover, x, + view); + x -= theme->window_button_width; + continue; + case LAB_SSD_BUTTON_MAXIMIZE: + /* Maximize button has an alternate state when maximized */ + btn_max_root = add_scene_button( + &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, parent, + maximize_button_unpressed, maximize_button_hover, x, view); + btn_max = node_ssd_button_from_node(btn_max_root->node); + add_toggled_icon(btn_max, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, + restore_button_unpressed, restore_button_hover); + x -= theme->window_button_width; + continue; + case LAB_SSD_BUTTON_CLOSE: + add_scene_button(&subtree->parts, LAB_SSD_BUTTON_CLOSE, parent, + close_button_unpressed, close_button_hover, x, view); + x -= theme->window_button_width; + continue; + default: + assert(false && "invalid titlebar part"); + wlr_log(WLR_ERROR, "invalid titlebar type"); + abort(); + } + } } FOR_EACH_END ssd_update_title(ssd); @@ -133,12 +191,6 @@ ssd_titlebar_create(struct ssd *ssd) } } -static bool -is_direct_child(struct wlr_scene_node *node, struct ssd_sub_tree *subtree) -{ - return node->parent == subtree->tree; -} - static void set_squared_corners(struct ssd *ssd, bool enable) { @@ -173,6 +225,10 @@ set_maximize_alt_icon(struct ssd *ssd, bool enable) FOR_EACH_STATE(ssd, subtree) { part = ssd_get_part(&subtree->parts, LAB_SSD_BUTTON_MAXIMIZE); + if (!part) { + return; + } + button = node_ssd_button_from_node(part->node); if (button->toggled) { @@ -212,45 +268,31 @@ ssd_titlebar_update(struct ssd *ssd) return; } + int x; struct ssd_part *part; struct ssd_sub_tree *subtree; + struct title_button *b; int bg_offset = maximized || tiled_not_maximized ? 0 : theme->window_button_width; FOR_EACH_STATE(ssd, subtree) { - wl_list_for_each(part, &subtree->parts, link) { - switch (part->type) { - case LAB_SSD_PART_TITLEBAR: - wlr_scene_rect_set_size( - wlr_scene_rect_from_node(part->node), - width - bg_offset * 2, theme->title_height); - continue; - case LAB_SSD_BUTTON_ICONIFY: - if (is_direct_child(part->node, subtree)) { - wlr_scene_node_set_position(part->node, - width - theme->window_button_width * 3, 0); - } - continue; - case LAB_SSD_BUTTON_MAXIMIZE: - if (is_direct_child(part->node, subtree)) { - wlr_scene_node_set_position(part->node, - width - theme->window_button_width * 2, 0); - } - continue; - case LAB_SSD_BUTTON_CLOSE: - if (is_direct_child(part->node, subtree)) { - wlr_scene_node_set_position(part->node, - width - theme->window_button_width * 1, 0); - } - continue; - case LAB_SSD_PART_CORNER_TOP_RIGHT: - if (is_direct_child(part->node, subtree)) { - wlr_scene_node_set_position(part->node, - width - theme->window_button_width * 1, - -rc.theme->border_width); - } - continue; - default: - continue; - } + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); + wlr_scene_rect_set_size( + wlr_scene_rect_from_node(part->node), + width - bg_offset * 2, theme->title_height); + + x = 0; + wl_list_for_each(b, &rc.title_buttons_left, link) { + part = ssd_get_part(&subtree->parts, b->type); + wlr_scene_node_set_position(part->node, x, 0); + x += theme->window_button_width; + } + + x = width - theme->window_button_width; + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_CORNER_TOP_RIGHT); + wlr_scene_node_set_position(part->node, x, -rc.theme->border_width); + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + part = ssd_get_part(&subtree->parts, b->type); + wlr_scene_node_set_position(part->node, x, 0); + x -= theme->window_button_width; } } FOR_EACH_END ssd_update_title(ssd); @@ -297,7 +339,9 @@ ssd_update_title_positions(struct ssd *ssd) struct view *view = ssd->view; struct theme *theme = view->server->theme; int width = view->current.width; - int title_bg_width = width - theme->window_button_width * SSD_BUTTON_COUNT; + int offset_left = theme->window_button_width * wl_list_length(&rc.title_buttons_left); + int offset_right = theme->window_button_width * wl_list_length(&rc.title_buttons_right); + int title_bg_width = width - offset_left - offset_right; int x, y; int buffer_height, buffer_width; @@ -313,7 +357,7 @@ ssd_update_title_positions(struct ssd *ssd) buffer_width = part->buffer ? part->buffer->width : 0; buffer_height = part->buffer ? part->buffer->height : 0; - x = theme->window_button_width; + x = offset_left; y = (theme->title_height - buffer_height) / 2; if (title_bg_width <= 0) { @@ -323,7 +367,7 @@ ssd_update_title_positions(struct ssd *ssd) wlr_scene_node_set_enabled(part->node, true); if (theme->window_label_text_justify == LAB_JUSTIFY_CENTER) { - if (buffer_width + theme->window_button_width * 2 <= title_bg_width) { + if (buffer_width + MAX(offset_left, offset_right) * 2 <= width) { /* Center based on the full width */ x = (width - buffer_width) / 2; } else { @@ -346,7 +390,7 @@ ssd_update_title_positions(struct ssd *ssd) void ssd_update_title(struct ssd *ssd) { - if (!ssd) { + if (!ssd || !rc.show_title) { return; } @@ -366,8 +410,9 @@ ssd_update_title(struct ssd *ssd) struct ssd_part *part; struct ssd_sub_tree *subtree; struct ssd_state_title_width *dstate; - int title_bg_width = view->current.width - - theme->window_button_width * SSD_BUTTON_COUNT; + int offset_left = theme->window_button_width * wl_list_length(&rc.title_buttons_left); + int offset_right = theme->window_button_width * wl_list_length(&rc.title_buttons_right); + int title_bg_width = view->current.width - offset_left - offset_right; FOR_EACH_STATE(ssd, subtree) { if (subtree == &ssd->titlebar.active) { diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index a8c3e173..ca82fe4a 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -233,14 +233,17 @@ ssd_update_geometry(struct ssd *ssd) struct wlr_box cached = ssd->state.geometry; struct wlr_box current = ssd->view->current; - int min_view_width = rc.theme->window_button_width * SSD_BUTTON_COUNT; + int min_view_width = rc.theme->window_button_width * ( + wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); int eff_width = current.width; int eff_height = view_effective_height(ssd->view, /* use_pending */ false); if (eff_width > 0 && eff_width < min_view_width) { /* * Prevent negative values in calculations like - * `width - SSD_BUTTON_WIDTH * SSD_BUTTON_COUNT` + * `width - theme->window_button_width + * * (wl_list_length(&rc.title_buttons_left) + * + wl_list_length(&rc.title_buttons_right))` */ wlr_log(WLR_ERROR, "view width is smaller than its minimal value"); diff --git a/src/theme.c b/src/theme.c index d699773e..eeb4a079 100644 --- a/src/theme.c +++ b/src/theme.c @@ -84,14 +84,26 @@ corner_from_icon_name(const char *icon_name) { assert(icon_name); - /* - * TODO: Once we implement titleLayout we can make the - * return values depend on parsed config values. - */ - if (!strcmp(icon_name, "menu")) { - return LAB_CORNER_TOP_LEFT; - } else if (!strcmp(icon_name, "close")) { - return LAB_CORNER_TOP_RIGHT; + struct title_button *b; + wl_list_for_each(b, &rc.title_buttons_left, link) { + if ((b->type == LAB_SSD_BUTTON_WINDOW_MENU && !strcmp(icon_name, "menu")) + || (b->type == LAB_SSD_BUTTON_ICONIFY && !strcmp(icon_name, "iconify")) + || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max")) + || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max_toggled")) + || (b->type == LAB_SSD_BUTTON_CLOSE && !strcmp(icon_name, "close"))) { + return LAB_CORNER_TOP_LEFT; + } + break; + } + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + if ((b->type == LAB_SSD_BUTTON_WINDOW_MENU && !strcmp(icon_name, "menu")) + || (b->type == LAB_SSD_BUTTON_ICONIFY && !strcmp(icon_name, "iconify")) + || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max")) + || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max_toggled")) + || (b->type == LAB_SSD_BUTTON_CLOSE && !strcmp(icon_name, "close"))) { + return LAB_CORNER_TOP_RIGHT; + } + break; } return LAB_CORNER_UNKNOWN; } diff --git a/src/view.c b/src/view.c index d6668d8a..9ac24022 100644 --- a/src/view.c +++ b/src/view.c @@ -617,7 +617,8 @@ view_adjust_size(struct view *view, int *w, int *h) { assert(view); struct view_size_hints hints = view_get_size_hints(view); - int min_view_width = rc.theme->window_button_width * SSD_BUTTON_COUNT; + int min_view_width = rc.theme->window_button_width * ( + wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); /* * "If a base size is not provided, the minimum size is to be diff --git a/src/xwayland.c b/src/xwayland.c index 738c2b17..2e035f38 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -622,7 +622,8 @@ check_natural_geometry(struct view *view) * un-maximized size when started maximized. Try to detect this * and set a fallback size. */ - int min_view_width = rc.theme->window_button_width * SSD_BUTTON_COUNT; + int min_view_width = rc.theme->window_button_width * ( + wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); if (!view_is_floating(view) && (view->natural_geometry.width < min_view_width || view->natural_geometry.height < LAB_MIN_VIEW_HEIGHT)) { From a28a3737b9ce71738f14b7f1f95c0f6102054351 Mon Sep 17 00:00:00 2001 From: Tobias Bengfort Date: Sun, 18 Aug 2024 10:20:24 +0200 Subject: [PATCH 126/232] refactor ssd_titlebar_create --- src/ssd/ssd-titlebar.c | 169 ++++++++++++++++------------------------- 1 file changed, 64 insertions(+), 105 deletions(-) diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 7326a707..292810bd 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -21,6 +21,64 @@ static void set_squared_corners(struct ssd *ssd, bool enable); static void set_maximize_alt_icon(struct ssd *ssd, bool enable); +static void +add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type type, int x) +{ + struct view *view = ssd->view; + struct theme *theme = view->server->theme; + struct wlr_scene_tree *parent = subtree->tree; + bool active = subtree == &ssd->titlebar.active; + + struct ssd_part *btn_max_root; + struct ssd_button *btn_max; + + switch (type) { + case LAB_SSD_BUTTON_WINDOW_MENU: + add_scene_button(&subtree->parts, type, parent, + active ? &theme->button_menu_active_unpressed->base + : &theme->button_menu_inactive_unpressed->base, + active ? &theme->button_menu_active_hover->base + : &theme->button_menu_inactive_hover->base, + x, view); + break; + case LAB_SSD_BUTTON_ICONIFY: + add_scene_button(&subtree->parts, type, parent, + active ? &theme->button_iconify_active_unpressed->base + : &theme->button_iconify_inactive_unpressed->base, + active ? &theme->button_iconify_active_hover->base + : &theme->button_iconify_inactive_hover->base, + x, view); + break; + case LAB_SSD_BUTTON_MAXIMIZE: + /* Maximize button has an alternate state when maximized */ + btn_max_root = add_scene_button(&subtree->parts, type, parent, + active ? &theme->button_maximize_active_unpressed->base + : &theme->button_maximize_inactive_unpressed->base, + active ? &theme->button_maximize_active_hover->base + : &theme->button_maximize_inactive_hover->base, + x, view); + btn_max = node_ssd_button_from_node(btn_max_root->node); + add_toggled_icon(btn_max, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, + active ? &theme->button_restore_active_unpressed->base + : &theme->button_restore_inactive_unpressed->base, + active ? &theme->button_restore_active_hover->base + : &theme->button_restore_inactive_hover->base); + break; + case LAB_SSD_BUTTON_CLOSE: + add_scene_button(&subtree->parts, type, parent, + active ? &theme->button_close_active_unpressed->base + : &theme->button_close_inactive_unpressed->base, + active ? &theme->button_close_active_hover->base + : &theme->button_close_inactive_hover->base, + x, view); + break; + default: + assert(false && "invalid titlebar part"); + wlr_log(WLR_ERROR, "invalid titlebar type"); + abort(); + } +} + void ssd_titlebar_create(struct ssd *ssd) { @@ -33,18 +91,6 @@ ssd_titlebar_create(struct ssd *ssd) struct wlr_buffer *corner_top_left; struct wlr_buffer *corner_top_right; - struct wlr_buffer *menu_button_unpressed; - struct wlr_buffer *iconify_button_unpressed; - struct wlr_buffer *maximize_button_unpressed; - struct wlr_buffer *restore_button_unpressed; - struct wlr_buffer *close_button_unpressed; - - struct wlr_buffer *menu_button_hover; - struct wlr_buffer *iconify_button_hover; - struct wlr_buffer *maximize_button_hover; - struct wlr_buffer *restore_button_hover; - struct wlr_buffer *close_button_hover; - ssd->titlebar.tree = wlr_scene_tree_create(ssd->tree); struct ssd_sub_tree *subtree; @@ -56,33 +102,10 @@ ssd_titlebar_create(struct ssd *ssd) color = theme->window_active_title_bg_color; corner_top_left = &theme->corner_top_left_active_normal->base; corner_top_right = &theme->corner_top_right_active_normal->base; - menu_button_unpressed = &theme->button_menu_active_unpressed->base; - iconify_button_unpressed = &theme->button_iconify_active_unpressed->base; - close_button_unpressed = &theme->button_close_active_unpressed->base; - maximize_button_unpressed = &theme->button_maximize_active_unpressed->base; - restore_button_unpressed = &theme->button_restore_active_unpressed->base; - - menu_button_hover = &theme->button_menu_active_hover->base; - iconify_button_hover = &theme->button_iconify_active_hover->base; - close_button_hover = &theme->button_close_active_hover->base; - maximize_button_hover = &theme->button_maximize_active_hover->base; - restore_button_hover = &theme->button_restore_active_hover->base; } else { color = theme->window_inactive_title_bg_color; corner_top_left = &theme->corner_top_left_inactive_normal->base; corner_top_right = &theme->corner_top_right_inactive_normal->base; - menu_button_unpressed = &theme->button_menu_inactive_unpressed->base; - iconify_button_unpressed = &theme->button_iconify_inactive_unpressed->base; - maximize_button_unpressed = - &theme->button_maximize_inactive_unpressed->base; - restore_button_unpressed = &theme->button_restore_inactive_unpressed->base; - close_button_unpressed = &theme->button_close_inactive_unpressed->base; - - menu_button_hover = &theme->button_menu_inactive_hover->base; - iconify_button_hover = &theme->button_iconify_inactive_hover->base; - close_button_hover = &theme->button_close_inactive_hover->base; - maximize_button_hover = &theme->button_maximize_inactive_hover->base; - restore_button_hover = &theme->button_restore_inactive_hover->base; wlr_scene_node_set_enabled(&parent->node, false); } @@ -99,81 +122,17 @@ ssd_titlebar_create(struct ssd *ssd) -rc.theme->border_width); /* Buttons */ - int x = 0; - struct ssd_part *btn_max_root; - struct ssd_button *btn_max; struct title_button *b; + int x = 0; wl_list_for_each(b, &rc.title_buttons_left, link) { - switch (b->type) { - case LAB_SSD_BUTTON_WINDOW_MENU: - add_scene_button(&subtree->parts, LAB_SSD_BUTTON_WINDOW_MENU, - parent, menu_button_unpressed, menu_button_hover, x, - view); - x += theme->window_button_width; - continue; - case LAB_SSD_BUTTON_ICONIFY: - add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent, - iconify_button_unpressed, iconify_button_hover, x, - view); - x += theme->window_button_width; - continue; - case LAB_SSD_BUTTON_MAXIMIZE: - /* Maximize button has an alternate state when maximized */ - btn_max_root = add_scene_button( - &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, parent, - maximize_button_unpressed, maximize_button_hover, x, view); - btn_max = node_ssd_button_from_node(btn_max_root->node); - add_toggled_icon(btn_max, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, - restore_button_unpressed, restore_button_hover); - x += theme->window_button_width; - continue; - case LAB_SSD_BUTTON_CLOSE: - add_scene_button(&subtree->parts, LAB_SSD_BUTTON_CLOSE, parent, - close_button_unpressed, close_button_hover, x, view); - x += theme->window_button_width; - continue; - default: - assert(false && "invalid titlebar part"); - wlr_log(WLR_ERROR, "invalid titlebar type"); - abort(); - } + add_button(ssd, subtree, b->type, x); + x += theme->window_button_width; } - x = width - theme->window_button_width; + x = width; wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { - switch (b->type) { - case LAB_SSD_BUTTON_WINDOW_MENU: - add_scene_button(&subtree->parts, LAB_SSD_BUTTON_WINDOW_MENU, - parent, menu_button_unpressed, menu_button_hover, x, - view); - x -= theme->window_button_width; - continue; - case LAB_SSD_BUTTON_ICONIFY: - add_scene_button(&subtree->parts, LAB_SSD_BUTTON_ICONIFY, parent, - iconify_button_unpressed, iconify_button_hover, x, - view); - x -= theme->window_button_width; - continue; - case LAB_SSD_BUTTON_MAXIMIZE: - /* Maximize button has an alternate state when maximized */ - btn_max_root = add_scene_button( - &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, parent, - maximize_button_unpressed, maximize_button_hover, x, view); - btn_max = node_ssd_button_from_node(btn_max_root->node); - add_toggled_icon(btn_max, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, - restore_button_unpressed, restore_button_hover); - x -= theme->window_button_width; - continue; - case LAB_SSD_BUTTON_CLOSE: - add_scene_button(&subtree->parts, LAB_SSD_BUTTON_CLOSE, parent, - close_button_unpressed, close_button_hover, x, view); - x -= theme->window_button_width; - continue; - default: - assert(false && "invalid titlebar part"); - wlr_log(WLR_ERROR, "invalid titlebar type"); - abort(); - } + x -= theme->window_button_width; + add_button(ssd, subtree, b->type, x); } } FOR_EACH_END From 6564e1bc8d2fbdfa02c5d03d53dbc5b63180dd62 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Mon, 19 Aug 2024 21:22:50 +0100 Subject: [PATCH 127/232] buf: add buf_add_fmt() --- include/common/buf.h | 7 +++++++ src/common/buf.c | 34 ++++++++++++++++++++++++++++++++++ t/buf-simple.c | 15 +++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/include/common/buf.h b/include/common/buf.h index bcbc8301..a75c1144 100644 --- a/include/common/buf.h +++ b/include/common/buf.h @@ -43,6 +43,13 @@ void buf_expand_tilde(struct buf *s); */ void buf_expand_shell_variables(struct buf *s); +/** + * buf_add_fmt - add format string to C string buffer + * @s: buffer + * @fmt: format string to be added + */ +void buf_add_fmt(struct buf *s, const char *fmt, ...); + /** * buf_add - add data to C string buffer * @s: buffer diff --git a/src/common/buf.c b/src/common/buf.c index ecdddc6e..d36f64d5 100644 --- a/src/common/buf.c +++ b/src/common/buf.c @@ -1,7 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include +#include #include +#include #include #include "common/buf.h" #include "common/macros.h" @@ -95,6 +97,38 @@ buf_expand(struct buf *s, int new_alloc) s->alloc = new_alloc; } +void +buf_add_fmt(struct buf *s, const char *fmt, ...) +{ + if (string_null_or_empty(fmt)) { + return; + } + size_t size = 0; + va_list ap; + + va_start(ap, fmt); + int n = vsnprintf(NULL, size, fmt, ap); + va_end(ap); + + if (n < 0) { + return; + } + + size = (size_t)n + 1; + buf_expand(s, s->len + size); + + va_start(ap, fmt); + n = vsnprintf(s->data + s->len, size, fmt, ap); + va_end(ap); + + if (n < 0) { + return; + } + + s->len += n; + s->data[s->len] = 0; +} + void buf_add(struct buf *s, const char *data) { diff --git a/t/buf-simple.c b/t/buf-simple.c index 47722116..3993ae83 100644 --- a/t/buf-simple.c +++ b/t/buf-simple.c @@ -55,10 +55,25 @@ test_expand_title(void **state) free(s.data); } +static void +test_buf_add_fmt(void **state) +{ + (void)state; + + struct buf s = BUF_INIT; + + buf_add(&s, "foo"); + buf_add_fmt(&s, " %s baz %d", "bar", 10); + assert_string_equal(s.data, "foo bar baz 10"); + + buf_reset(&s); +} + int main(int argc, char **argv) { const struct CMUnitTest tests[] = { cmocka_unit_test(test_expand_title), + cmocka_unit_test(test_buf_add_fmt), }; return cmocka_run_group_tests(tests, NULL, NULL); From 8fda9968e63562b401d9f96b4f1a7171c8740dd6 Mon Sep 17 00:00:00 2001 From: droc12345 <80716141+droc12345@users.noreply.github.com> Date: Wed, 21 Aug 2024 12:27:07 -0500 Subject: [PATCH 128/232] menu: add title theme options (#2097) Add theme options `menu.title.text.color` and `menu.title.text.justify`. Add font place MenuHeader: `` Add `Oblique` font style ``` Oblique ``` --- docs/labwc-config.5.scd | 3 ++- docs/labwc-theme.5.scd | 7 +++++++ docs/menu.xml | 6 ++++++ docs/rc.xml.all | 6 ++++++ docs/themerc | 2 ++ include/common/font.h | 3 ++- include/config/rcxml.h | 1 + include/theme.h | 3 +++ src/common/font.c | 3 +++ src/config/rcxml.c | 21 +++++++++++++++++++-- src/menu/menu.c | 21 ++++++++++++++++++--- src/theme.c | 10 ++++++++++ 12 files changed, 79 insertions(+), 7 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index bd03fd6b..4c961115 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -448,6 +448,7 @@ extending outward from the snapped edge. - ActiveWindow - titlebar of active window - InactiveWindow - titlebar of all windows that aren't focused by the cursor + - MenuHeader - menu title - MenuItem - menu item (currently only root menu) - OnScreenDisplay - items in the on screen display If no place attribute is provided, the setting will be applied to all @@ -460,7 +461,7 @@ extending outward from the snapped edge. Font size in pixels. Default is 10. ** - Font slant (normal or italic). Default is normal. + Font slant (normal, oblique or italic). Default is normal. ** Font weight (normal or bold). Default is normal. diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index 29a832f5..9f8310af 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -67,6 +67,10 @@ labwc-config(5). Vertical padding of menu text entries in pixels. Default is 4. +*menu.title.text.justify* + Specifies how menu titles are aligned in the titlebar. + Type justification. Default Center. + *menu.overlap.x* Horizontal overlap in pixels between submenus and their parents. A positive value move submenus over the top of their parents, whereas a @@ -178,6 +182,9 @@ elements are not listed here, but are supported. Menu title color. Default #589bda. Note: A menu title is a separator with a label. +*menu.title.text.color* + Text color of separator label. Default #ffffff. + *osd.bg.color* Background color of on-screen-display. Inherits *window.active.title.bg.color* if not set. diff --git a/docs/menu.xml b/docs/menu.xml index a23f1b91..f150b258 100644 --- a/docs/menu.xml +++ b/docs/menu.xml @@ -61,6 +61,12 @@ + + diff --git a/docs/rc.xml.all b/docs/rc.xml.all index f71014a6..d1fc811b 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -43,6 +43,12 @@ normal normal + + sans + 10 + normal + normal + sans 10 diff --git a/docs/themerc b/docs/themerc index e54dfb96..66e1d09c 100644 --- a/docs/themerc +++ b/docs/themerc @@ -63,6 +63,8 @@ menu.separator.padding.width: 6 menu.separator.padding.height: 3 menu.separator.color: #888888 menu.title.bg.color: #589bda +menu.title.text.color: #ffffff +menu.title.text.justify: Center # on screen display (window-cycle dialog) osd.bg.color: #e1dedb diff --git a/include/common/font.h b/include/common/font.h index ddca15aa..7684f7e2 100644 --- a/include/common/font.h +++ b/include/common/font.h @@ -6,7 +6,8 @@ struct lab_data_buffer; enum font_slant { FONT_SLANT_NORMAL = 0, - FONT_SLANT_ITALIC + FONT_SLANT_ITALIC, + FONT_SLANT_OBLIQUE }; enum font_weight { diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 2b5da588..e981cb36 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -80,6 +80,7 @@ struct rcxml { bool shadows_enabled; struct font font_activewindow; struct font font_inactivewindow; + struct font font_menuheader; struct font font_menuitem; struct font font_osd; diff --git a/include/theme.h b/include/theme.h index 4ad3855c..f0c3b757 100644 --- a/include/theme.h +++ b/include/theme.h @@ -44,6 +44,7 @@ struct theme { float window_active_label_text_color[4]; float window_inactive_label_text_color[4]; enum lab_justification window_label_text_justify; + enum lab_justification menu_title_text_justify; /* button width */ int window_button_width; @@ -78,6 +79,8 @@ struct theme { float menu_title_bg_color[4]; + float menu_title_text_color[4]; + int osd_border_width; float osd_bg_color[4]; diff --git a/src/common/font.c b/src/common/font.c index 6e12e780..e917dec9 100644 --- a/src/common/font.c +++ b/src/common/font.c @@ -20,6 +20,9 @@ font_to_pango_desc(struct font *font) if (font->slant == FONT_SLANT_ITALIC) { pango_font_description_set_style(desc, PANGO_STYLE_ITALIC); } + if (font->slant == FONT_SLANT_OBLIQUE) { + pango_font_description_set_style(desc, PANGO_STYLE_OBLIQUE); + } if (font->weight == FONT_WEIGHT_BOLD) { pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); } diff --git a/src/config/rcxml.c b/src/config/rcxml.c index da0bbde3..38e5490f 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -69,6 +69,7 @@ enum font_place { FONT_PLACE_UNKNOWN, FONT_PLACE_ACTIVEWINDOW, FONT_PLACE_INACTIVEWINDOW, + FONT_PLACE_MENUHEADER, FONT_PLACE_MENUITEM, FONT_PLACE_OSD, /* TODO: Add all places based on Openbox's rc.xml */ @@ -691,8 +692,13 @@ set_font_attr(struct font *font, const char *nodename, const char *content) } else if (!strcmp(nodename, "size")) { font->size = atoi(content); } else if (!strcmp(nodename, "slant")) { - font->slant = !strcasecmp(content, "italic") ? - FONT_SLANT_ITALIC : FONT_SLANT_NORMAL; + if (!strcasecmp(content, "italic")) { + font->slant = FONT_SLANT_ITALIC; + } else if (!strcasecmp(content, "oblique")) { + font->slant = FONT_SLANT_OBLIQUE; + } else { + font->slant = FONT_SLANT_NORMAL; + } } else if (!strcmp(nodename, "weight")) { font->weight = !strcasecmp(content, "bold") ? FONT_WEIGHT_BOLD : FONT_WEIGHT_NORMAL; @@ -715,6 +721,7 @@ fill_font(char *nodename, char *content, enum font_place place) */ set_font_attr(&rc.font_activewindow, nodename, content); set_font_attr(&rc.font_inactivewindow, nodename, content); + set_font_attr(&rc.font_menuheader, nodename, content); set_font_attr(&rc.font_menuitem, nodename, content); set_font_attr(&rc.font_osd, nodename, content); break; @@ -724,6 +731,9 @@ fill_font(char *nodename, char *content, enum font_place place) case FONT_PLACE_INACTIVEWINDOW: set_font_attr(&rc.font_inactivewindow, nodename, content); break; + case FONT_PLACE_MENUHEADER: + set_font_attr(&rc.font_menuheader, nodename, content); + break; case FONT_PLACE_MENUITEM: set_font_attr(&rc.font_menuitem, nodename, content); break; @@ -748,6 +758,8 @@ enum_font_place(const char *place) return FONT_PLACE_ACTIVEWINDOW; } else if (!strcasecmp(place, "InactiveWindow")) { return FONT_PLACE_INACTIVEWINDOW; + } else if (!strcasecmp(place, "MenuHeader")) { + return FONT_PLACE_MENUHEADER; } else if (!strcasecmp(place, "MenuItem")) { return FONT_PLACE_MENUITEM; } else if (!strcasecmp(place, "OnScreenDisplay") @@ -1265,6 +1277,7 @@ rcxml_init(void) init_font_defaults(&rc.font_activewindow); init_font_defaults(&rc.font_inactivewindow); + init_font_defaults(&rc.font_menuheader); init_font_defaults(&rc.font_menuitem); init_font_defaults(&rc.font_osd); @@ -1510,6 +1523,9 @@ post_processing(void) if (!rc.font_inactivewindow.name) { rc.font_inactivewindow.name = xstrdup("sans"); } + if (!rc.font_menuheader.name) { + rc.font_menuheader.name = xstrdup("sans"); + } if (!rc.font_menuitem.name) { rc.font_menuitem.name = xstrdup("sans"); } @@ -1715,6 +1731,7 @@ rcxml_finish(void) { zfree(rc.font_activewindow.name); zfree(rc.font_inactivewindow.name); + zfree(rc.font_menuheader.name); zfree(rc.font_menuitem.name); zfree(rc.font_osd.name); zfree(rc.theme_name); diff --git a/src/menu/menu.c b/src/menu/menu.c index 1abef56a..f8653d64 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -141,6 +141,19 @@ menu_update_width(struct menu *menu) wlr_scene_rect_set_size( wlr_scene_rect_from_node(item->normal.text), width, theme->menu_separator_line_thickness); + } else if (item->type == LAB_MENU_TITLE) { + if (item->native_width > max_width) { + scaled_font_buffer_set_max_width(item->normal.buffer, + max_width); + } + if (theme->menu_title_text_justify == LAB_JUSTIFY_CENTER) { + int x, y; + x = (max_width - theme->menu_item_padding_x - + item->native_width) / 2; + x = x < 0 ? 0 : x; + y = (theme->menu_item_height - item->normal.buffer->height) / 2; + wlr_scene_node_set_position(item->normal.text, x, y); + } } if (item->selectable) { @@ -295,7 +308,7 @@ separator_create(struct menu *menu, const char *label) if (menuitem->type == LAB_MENU_TITLE) { menuitem->height = theme->menu_item_height; - menuitem->native_width = font_width(&rc.font_menuitem, label); + menuitem->native_width = font_width(&rc.font_menuheader, label); } else if (menuitem->type == LAB_MENU_SEPARATOR_LINE) { menuitem->height = theme->menu_separator_line_thickness + 2 * theme->menu_separator_padding_height; @@ -312,6 +325,8 @@ separator_create(struct menu *menu, const char *label) /* Item background nodes */ float *bg_color = menuitem->type == LAB_MENU_TITLE ? theme->menu_title_bg_color : theme->menu_items_bg_color; + float *text_color = menuitem->type == LAB_MENU_TITLE + ? theme->menu_title_text_color : theme->menu_items_text_color; menuitem->normal.background = &wlr_scene_rect_create( menuitem->normal.tree, menu->size.width, menuitem->height, bg_color)->node; @@ -328,8 +343,8 @@ separator_create(struct menu *menu, const char *label) menuitem->normal.text = &menuitem->normal.buffer->scene_buffer->node; /* Font buffer */ scaled_font_buffer_update(menuitem->normal.buffer, label, - menuitem->native_width, &rc.font_menuitem, - theme->menu_items_text_color, bg_color, /* arrow */ NULL); + menuitem->native_width, &rc.font_menuheader, + text_color, bg_color, /* arrow */ NULL); /* Center font nodes */ int x, y; x = theme->menu_item_padding_x; diff --git a/src/theme.c b/src/theme.c index a22d0936..ee104268 100644 --- a/src/theme.c +++ b/src/theme.c @@ -487,6 +487,7 @@ theme_builtin(struct theme *theme, struct server *server) parse_hexstr("#000000", theme->window_active_label_text_color); parse_hexstr("#000000", theme->window_inactive_label_text_color); theme->window_label_text_justify = parse_justification("Center"); + theme->menu_title_text_justify = parse_justification("Center"); theme->window_button_width = 26; @@ -530,6 +531,8 @@ theme_builtin(struct theme *theme, struct server *server) parse_hexstr("#589bda", theme->menu_title_bg_color); + parse_hexstr("#ffffff", theme->menu_title_text_color); + theme->osd_window_switcher_width = 600; theme->osd_window_switcher_width_is_percent = false; theme->osd_window_switcher_padding = 4; @@ -606,6 +609,9 @@ entry(struct theme *theme, const char *key, const char *value) if (match_glob(key, "menu.items.padding.y")) { theme->menu_item_padding_y = atoi(value); } + if (match_glob(key, "menu.title.text.justify")) { + theme->menu_title_text_justify = parse_justification(value); + } if (match_glob(key, "menu.overlap.x")) { theme->menu_overlap_x = atoi(value); } @@ -772,6 +778,10 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->menu_title_bg_color); } + if (match_glob(key, "menu.title.text.color")) { + parse_hexstr(value, theme->menu_title_text_color); + } + if (match_glob(key, "osd.bg.color")) { parse_hexstr(value, theme->osd_bg_color); } From b667107d1acea44bd2b4e62cf4a9976d375881fd Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Thu, 8 Aug 2024 17:29:43 +0900 Subject: [PATCH 129/232] input: move to section This allows per-device configuration of scroll factor (e.g. setting different scroll factors for mice and touchpads). --- docs/labwc-config.5.scd | 7 ++++--- docs/rc.xml.all | 3 ++- include/config/libinput.h | 1 + include/config/rcxml.h | 1 - include/labwc.h | 2 ++ src/config/libinput.c | 1 + src/config/rcxml.c | 20 +++++++++++++++++--- src/input/cursor.c | 11 +++++++++-- src/seat.c | 9 +++++++++ 9 files changed, 45 insertions(+), 10 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 7ff83625..6da0af34 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -603,9 +603,6 @@ extending outward from the snapped edge. ** Set double click time in milliseconds. Default is 500. -** - Set scroll factor. Default is 1.0. - ** Multiple ** can exist within one **; and multiple ** can exist within one **. @@ -814,6 +811,7 @@ extending outward from the snapped edge. + 1.0 ``` @@ -939,6 +937,9 @@ The most common matrices are: visit https://wayland.freedesktop.org/libinput/doc/latest/absolute-axes.html#calibration-of-absolute-devices for more information. +** + Set scroll factor. Default is 1.0. + ## WINDOW RULES Two types of window rules are supported, actions and properties. They are diff --git a/docs/rc.xml.all b/docs/rc.xml.all index b3b7821f..ab462a14 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -330,7 +330,6 @@ 500 - 1.0 @@ -559,6 +558,7 @@ - clickMethod [none|buttonAreas|clickfinger] - sendEventsMode [yes|no|disabledOnExternalMouse] - calibrationMatrix [six float values split by space] + - scrollFactor [float] --> @@ -575,6 +575,7 @@ + 1.0 diff --git a/include/config/libinput.h b/include/config/libinput.h index 0c211613..94a99a3a 100644 --- a/include/config/libinput.h +++ b/include/config/libinput.h @@ -31,6 +31,7 @@ struct libinput_category { int click_method; /* -1 or libinput_config_click_method */ int send_events_mode; /* -1 or libinput_config_send_events_mode */ bool have_calibration_matrix; + double scroll_factor; float calibration_matrix[6]; }; diff --git a/include/config/rcxml.h b/include/config/rcxml.h index 03a25881..cb56d473 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -110,7 +110,6 @@ struct rcxml { /* mouse */ long doubleclick_time; /* in ms */ struct wl_list mousebinds; /* struct mousebind.link */ - double scroll_factor; /* touch tablet */ struct wl_list touch_configs; diff --git a/include/labwc.h b/include/labwc.h index 14ce607c..30f9fbaf 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -71,6 +71,8 @@ enum input_mode { struct input { struct wlr_input_device *wlr_input_device; struct seat *seat; + /* Set for pointer/touch devices */ + double scroll_factor; struct wl_listener destroy; struct wl_list link; /* seat.inputs */ }; diff --git a/src/config/libinput.c b/src/config/libinput.c index af4f1bd0..42ec647a 100644 --- a/src/config/libinput.c +++ b/src/config/libinput.c @@ -26,6 +26,7 @@ libinput_category_init(struct libinput_category *l) l->click_method = -1; l->send_events_mode = -1; l->have_calibration_matrix = false; + l->scroll_factor = 1.0; } enum lab_libinput_device_type diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 923822fc..30fd6afe 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -63,6 +63,8 @@ static struct window_rule *current_window_rule; static struct action *current_window_rule_action; static struct view_query *current_view_query; static struct action *current_child_action; +/* for backword compatibility of */ +static double mouse_scroll_factor = -1; enum font_place { FONT_PLACE_NONE = 0, @@ -733,6 +735,8 @@ fill_libinput_category(char *nodename, char *content) current_libinput_category->have_calibration_matrix = false; } g_strfreev(elements); + } else if (!strcasecmp(nodename, "scrollFactor")) { + set_double(content, ¤t_libinput_category->scroll_factor); } } @@ -1011,7 +1015,8 @@ entry(xmlNode *node, char *nodename, char *content) wlr_log(WLR_ERROR, "invalid doubleClickTime"); } } else if (!strcasecmp(nodename, "scrollFactor.mouse")) { - set_double(content, &rc.scroll_factor); + /* This is deprecated. Show an error message in post_processing() */ + set_double(content, &mouse_scroll_factor); } else if (!strcasecmp(nodename, "name.context.mouse")) { current_mouse_context = content; current_mousebind = NULL; @@ -1345,7 +1350,6 @@ rcxml_init(void) rc.raise_on_focus = false; rc.doubleclick_time = 500; - rc.scroll_factor = 1.0; rc.tablet.force_mouse_emulation = false; rc.tablet.output_name = NULL; @@ -1596,12 +1600,21 @@ post_processing(void) rc.font_osd.name = xstrdup("sans"); } if (!libinput_category_get_default()) { - /* So we still allow tap to click by default */ + /* So we set default values of and */ struct libinput_category *l = libinput_category_create(); /* Prevents unused variable warning when compiled without asserts */ (void)l; assert(l && libinput_category_get_default() == l); } + if (mouse_scroll_factor >= 0) { + wlr_log(WLR_ERROR, " is deprecated" + " and overwrites ." + " Use only ."); + struct libinput_category *l; + wl_list_for_each(l, &rc.libinput_categories, link) { + l->scroll_factor = mouse_scroll_factor; + } + } int nr_workspaces = wl_list_length(&rc.workspace_config.workspaces); if (nr_workspaces < rc.workspace_config.min_nr_workspaces) { @@ -1884,4 +1897,5 @@ rcxml_finish(void) current_field = NULL; current_window_rule = NULL; current_window_rule_action = NULL; + mouse_scroll_factor = -1; } diff --git a/src/input/cursor.c b/src/input/cursor.c index 3b2583f0..cd28cf44 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -1335,6 +1335,13 @@ cursor_axis(struct wl_listener *listener, void *data) struct seat *seat = wl_container_of(listener, seat, cursor_axis); struct wlr_pointer_axis_event *event = data; struct server *server = seat->server; + + /* input->scroll_factor is set for pointer/touch devices */ + assert(event->pointer->base.type == WLR_INPUT_DEVICE_POINTER + || event->pointer->base.type == WLR_INPUT_DEVICE_TOUCH); + struct input *input = event->pointer->base.data; + double scroll_factor = input->scroll_factor; + struct cursor_context ctx = get_cursor_context(server); idle_manager_notify_activity(seat->seat); @@ -1349,8 +1356,8 @@ cursor_axis(struct wl_listener *listener, void *data) /* Notify the client with pointer focus of the axis event. */ wlr_seat_pointer_notify_axis(seat->seat, event->time_msec, - event->orientation, rc.scroll_factor * event->delta, - round(rc.scroll_factor * event->delta_discrete), + event->orientation, scroll_factor * event->delta, + round(scroll_factor * event->delta_discrete), event->source, event->relative_direction); } } diff --git a/src/seat.c b/src/seat.c index 49f317cc..004b610e 100644 --- a/src/seat.c +++ b/src/seat.c @@ -114,7 +114,11 @@ configure_libinput(struct wlr_input_device *wlr_input_device) wlr_log(WLR_ERROR, "no wlr_input_device"); return; } + struct input *input = wlr_input_device->data; + + /* Set scroll factor to 1.0 for Wayland/X11 backends or virtual pointers */ if (!wlr_input_device_is_libinput(wlr_input_device)) { + input->scroll_factor = 1.0; return; } @@ -247,6 +251,9 @@ configure_libinput(struct wlr_input_device *wlr_input_device) wlr_log(WLR_INFO, "calibration matrix configured"); libinput_device_config_calibration_set_matrix(libinput_dev, dc->calibration_matrix); } + + wlr_log(WLR_INFO, "scroll factor configured"); + input->scroll_factor = dc->scroll_factor; } static struct wlr_output * @@ -286,6 +293,7 @@ new_pointer(struct seat *seat, struct wlr_input_device *dev) { struct input *input = znew(*input); input->wlr_input_device = dev; + dev->data = input; configure_libinput(dev); wlr_cursor_attach_input_device(seat->cursor, dev); @@ -354,6 +362,7 @@ new_touch(struct seat *seat, struct wlr_input_device *dev) { struct input *input = znew(*input); input->wlr_input_device = dev; + dev->data = input; configure_libinput(dev); wlr_cursor_attach_input_device(seat->cursor, dev); /* In support of running with WLR_WL_OUTPUTS set to >=2 */ From 23a9df0f30a314853f05148f8feb6664323f0331 Mon Sep 17 00:00:00 2001 From: Consolatis <35009135+Consolatis@users.noreply.github.com> Date: Fri, 23 Aug 2024 20:09:47 +0200 Subject: [PATCH 130/232] common/buf.c: use 0 directly in vsnprintf() This works around a wrong truncation warning in older GCC versions: ``` ../src/common/buf.c:110:10: error: null destination pointer [-Werror=format-truncation=] 110 | int n = vsnprintf(NULL, size, fmt, ap) ``` --- src/common/buf.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/common/buf.c b/src/common/buf.c index d36f64d5..70be9ed0 100644 --- a/src/common/buf.c +++ b/src/common/buf.c @@ -103,18 +103,17 @@ buf_add_fmt(struct buf *s, const char *fmt, ...) if (string_null_or_empty(fmt)) { return; } - size_t size = 0; va_list ap; va_start(ap, fmt); - int n = vsnprintf(NULL, size, fmt, ap); + int n = vsnprintf(NULL, 0, fmt, ap); va_end(ap); if (n < 0) { return; } - size = (size_t)n + 1; + size_t size = (size_t)n + 1; buf_expand(s, s->len + size); va_start(ap, fmt); From 9f2eae672ccd0b048717798e413951c729ffe6dd Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Fri, 23 Aug 2024 13:05:13 +0900 Subject: [PATCH 131/232] ssd: fix incorrect cursor shape on titlebar corner without buttons Before this commit, the backgrounds of titlebar corners were tagged as LAB_SSD_PART_CORNER_TOP_{LEFT,RIGHT}, so the cursor shape on titlebar corners without buttons were north-west or north-east. This commit fixes it by tagging those backgrounds as LAB_SSD_TITLEBAR_CORNER_{LEFT,RIGHT}. --- include/ssd.h | 5 +++++ src/ssd/ssd-titlebar.c | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/include/ssd.h b/include/ssd.h index 7a2e7a77..2924253c 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -26,7 +26,11 @@ enum ssd_part_type { LAB_SSD_BUTTON_ICONIFY, LAB_SSD_BUTTON_WINDOW_MENU, LAB_SSD_PART_TITLEBAR, + LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, + LAB_SSD_PART_TITLEBAR_CORNER_LEFT, LAB_SSD_PART_TITLE, + + /* shared by shadows, borders and extents */ LAB_SSD_PART_CORNER_TOP_LEFT, LAB_SSD_PART_CORNER_TOP_RIGHT, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, @@ -35,6 +39,7 @@ enum ssd_part_type { LAB_SSD_PART_RIGHT, LAB_SSD_PART_BOTTOM, LAB_SSD_PART_LEFT, + LAB_SSD_CLIENT, LAB_SSD_FRAME, LAB_SSD_ROOT, diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 292810bd..827269aa 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -115,9 +115,9 @@ ssd_titlebar_create(struct ssd *ssd) add_scene_rect(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent, width - theme->window_button_width * 2, theme->title_height, theme->window_button_width, 0, color); - add_scene_buffer(&subtree->parts, LAB_SSD_PART_CORNER_TOP_LEFT, parent, + add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, parent, corner_top_left, -rc.theme->border_width, -rc.theme->border_width); - add_scene_buffer(&subtree->parts, LAB_SSD_PART_CORNER_TOP_RIGHT, parent, + add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, parent, corner_top_right, width - theme->window_button_width, -rc.theme->border_width); @@ -167,10 +167,10 @@ set_squared_corners(struct ssd *ssd, bool enable) wlr_scene_rect_set_size( wlr_scene_rect_from_node(part->node), width - 2 * x, theme->title_height); - part = ssd_get_part(&subtree->parts, LAB_SSD_PART_CORNER_TOP_LEFT); + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT); wlr_scene_node_set_enabled(part->node, !enable); - part = ssd_get_part(&subtree->parts, LAB_SSD_PART_CORNER_TOP_RIGHT); + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT); wlr_scene_node_set_enabled(part->node, !enable); } FOR_EACH_END } @@ -246,7 +246,7 @@ ssd_titlebar_update(struct ssd *ssd) } x = width - theme->window_button_width; - part = ssd_get_part(&subtree->parts, LAB_SSD_PART_CORNER_TOP_RIGHT); + part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT); wlr_scene_node_set_position(part->node, x, -rc.theme->border_width); wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { part = ssd_get_part(&subtree->parts, b->type); From 186a07be9b5053733a7e418dec2b8d7422ffbfcd Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" Date: Thu, 22 Aug 2024 16:27:24 -0400 Subject: [PATCH 132/232] ssd: add optional shade button --- docs/labwc-config.5.scd | 2 + docs/rc.xml.all | 6 +++ include/config/default-bindings.h | 5 ++ include/ssd-internal.h | 1 + include/ssd.h | 2 +- include/theme.h | 10 ++++ src/config/mousebind.c | 2 + src/config/rcxml.c | 4 +- src/ssd/ssd-titlebar.c | 45 ++++++++++++++---- src/ssd/ssd.c | 17 ++----- src/theme.c | 78 +++++++++++++++++++++++++++---- 11 files changed, 138 insertions(+), 34 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 6da0af34..ed063aed 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -438,6 +438,7 @@ extending outward from the snapped edge. - L: window label (aka. title) - |: empty space (can be used instead of a title) - W: window menu + - S: shade - I: iconify - M: maximize - C: close @@ -615,6 +616,7 @@ extending outward from the snapped edge. - WindowMenu: The button on the left. - Iconify: The button that looks like an underline. - Maximize: The button that looks like a box. + - Shade: A button that looks like an overline. - Close: The button that looks like an X. - Top: The top edge of the window's border. - Bottom: The bottom edge of the window's border. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index ab462a14..f2de0f29 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -441,6 +441,12 @@ + + + + + + diff --git a/include/config/default-bindings.h b/include/config/default-bindings.h index 535ee25a..f3de19b2 100644 --- a/include/config/default-bindings.h +++ b/include/config/default-bindings.h @@ -322,6 +322,11 @@ static struct mouse_combos { .button = "Left", .event = "Click", .action = "ToggleMaximize", + }, { + .context = "Shade", + .button = "Left", + .event = "Click", + .action = "ToggleShade", }, { .context = "Maximize", .button = "Right", diff --git a/include/ssd-internal.h b/include/ssd-internal.h index c6e3ba77..c8f4ab7b 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -48,6 +48,7 @@ struct ssd { * don't update things we don't have to. */ struct { + bool was_shaded; /* To toggle icon on shade */ bool was_maximized; /* To un-round corner buttons and toggle icon on maximize */ bool was_tiled_not_maximized; /* To un-round corner buttons */ struct wlr_box geometry; diff --git a/include/ssd.h b/include/ssd.h index 2924253c..8b8f77d3 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -25,6 +25,7 @@ enum ssd_part_type { LAB_SSD_BUTTON_MAXIMIZE, LAB_SSD_BUTTON_ICONIFY, LAB_SSD_BUTTON_WINDOW_MENU, + LAB_SSD_BUTTON_SHADE, LAB_SSD_PART_TITLEBAR, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, @@ -100,7 +101,6 @@ enum ssd_part_type ssd_at(const struct ssd *ssd, enum ssd_part_type ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node); uint32_t ssd_resize_edges(enum ssd_part_type type); -bool ssd_is_button(enum ssd_part_type type); bool ssd_part_contains(enum ssd_part_type whole, enum ssd_part_type candidate); enum ssd_mode ssd_mode_parse(const char *mode); diff --git a/include/theme.h b/include/theme.h index f0c3b757..2a40b589 100644 --- a/include/theme.h +++ b/include/theme.h @@ -54,10 +54,12 @@ struct theme { float window_active_button_iconify_unpressed_image_color[4]; float window_active_button_max_unpressed_image_color[4]; float window_active_button_close_unpressed_image_color[4]; + float window_active_button_shade_unpressed_image_color[4]; float window_inactive_button_menu_unpressed_image_color[4]; float window_inactive_button_iconify_unpressed_image_color[4]; float window_inactive_button_max_unpressed_image_color[4]; float window_inactive_button_close_unpressed_image_color[4]; + float window_inactive_button_shade_unpressed_image_color[4]; /* TODO: add pressed and hover colors for buttons */ int menu_item_padding_x; @@ -114,12 +116,16 @@ struct theme { struct lab_data_buffer *button_restore_active_unpressed; struct lab_data_buffer *button_iconify_active_unpressed; struct lab_data_buffer *button_menu_active_unpressed; + struct lab_data_buffer *button_shade_active_unpressed; + struct lab_data_buffer *button_unshade_active_unpressed; struct lab_data_buffer *button_close_inactive_unpressed; struct lab_data_buffer *button_maximize_inactive_unpressed; struct lab_data_buffer *button_restore_inactive_unpressed; struct lab_data_buffer *button_iconify_inactive_unpressed; struct lab_data_buffer *button_menu_inactive_unpressed; + struct lab_data_buffer *button_shade_inactive_unpressed; + struct lab_data_buffer *button_unshade_inactive_unpressed; /* hover variants are optional and may be NULL */ struct lab_data_buffer *button_close_active_hover; @@ -127,12 +133,16 @@ struct theme { struct lab_data_buffer *button_restore_active_hover; struct lab_data_buffer *button_iconify_active_hover; struct lab_data_buffer *button_menu_active_hover; + struct lab_data_buffer *button_shade_active_hover; + struct lab_data_buffer *button_unshade_active_hover; struct lab_data_buffer *button_close_inactive_hover; struct lab_data_buffer *button_maximize_inactive_hover; struct lab_data_buffer *button_restore_inactive_hover; struct lab_data_buffer *button_iconify_inactive_hover; struct lab_data_buffer *button_menu_inactive_hover; + struct lab_data_buffer *button_shade_inactive_hover; + struct lab_data_buffer *button_unshade_inactive_hover; struct lab_data_buffer *corner_top_left_active_normal; struct lab_data_buffer *corner_top_right_active_normal; diff --git a/src/config/mousebind.c b/src/config/mousebind.c index 857012fd..f7b0b44e 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -114,6 +114,8 @@ context_from_str(const char *str) return LAB_SSD_BUTTON_ICONIFY; } else if (!strcasecmp(str, "WindowMenu")) { return LAB_SSD_BUTTON_WINDOW_MENU; + } else if (!strcasecmp(str, "Shade")) { + return LAB_SSD_BUTTON_SHADE; } else if (!strcasecmp(str, "Titlebar")) { return LAB_SSD_PART_TITLEBAR; } else if (!strcasecmp(str, "Title")) { diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 30fd6afe..71b22768 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -158,7 +158,9 @@ fill_title_layout(char *nodename, char *content) case 'C': type = LAB_SSD_BUTTON_CLOSE; break; - /* case 'S': shade */ + case 'S': + type = LAB_SSD_BUTTON_SHADE; + break; /* case 'D': omnipresent */ default: continue; diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 827269aa..834d8831 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -19,7 +19,7 @@ &(ssd)->titlebar.inactive) static void set_squared_corners(struct ssd *ssd, bool enable); -static void set_maximize_alt_icon(struct ssd *ssd, bool enable); +static void set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable); static void add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type type, int x) @@ -29,8 +29,8 @@ add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type typ struct wlr_scene_tree *parent = subtree->tree; bool active = subtree == &ssd->titlebar.active; - struct ssd_part *btn_max_root; - struct ssd_button *btn_max; + struct ssd_part *btn_root; + struct ssd_button *btn; switch (type) { case LAB_SSD_BUTTON_WINDOW_MENU: @@ -51,19 +51,34 @@ add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type typ break; case LAB_SSD_BUTTON_MAXIMIZE: /* Maximize button has an alternate state when maximized */ - btn_max_root = add_scene_button(&subtree->parts, type, parent, + btn_root = add_scene_button(&subtree->parts, type, parent, active ? &theme->button_maximize_active_unpressed->base : &theme->button_maximize_inactive_unpressed->base, active ? &theme->button_maximize_active_hover->base : &theme->button_maximize_inactive_hover->base, x, view); - btn_max = node_ssd_button_from_node(btn_max_root->node); - add_toggled_icon(btn_max, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, + btn = node_ssd_button_from_node(btn_root->node); + add_toggled_icon(btn, &subtree->parts, LAB_SSD_BUTTON_MAXIMIZE, active ? &theme->button_restore_active_unpressed->base : &theme->button_restore_inactive_unpressed->base, active ? &theme->button_restore_active_hover->base : &theme->button_restore_inactive_hover->base); break; + case LAB_SSD_BUTTON_SHADE: + /* Shade button has an alternate state when shaded */ + btn_root = add_scene_button(&subtree->parts, type, parent, + active ? &theme->button_shade_active_unpressed->base + : &theme->button_shade_inactive_unpressed->base, + active ? &theme->button_shade_active_hover->base + : &theme->button_shade_inactive_hover->base, + x, view); + btn = node_ssd_button_from_node(btn_root->node); + add_toggled_icon(btn, &subtree->parts, LAB_SSD_BUTTON_SHADE, + active ? &theme->button_unshade_active_unpressed->base + : &theme->button_unshade_inactive_unpressed->base, + active ? &theme->button_unshade_active_hover->base + : &theme->button_unshade_inactive_hover->base); + break; case LAB_SSD_BUTTON_CLOSE: add_scene_button(&subtree->parts, type, parent, active ? &theme->button_close_active_unpressed->base @@ -141,9 +156,14 @@ ssd_titlebar_create(struct ssd *ssd) bool maximized = view->maximized == VIEW_AXIS_BOTH; if (maximized) { set_squared_corners(ssd, true); - set_maximize_alt_icon(ssd, true); + set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, true); ssd->state.was_maximized = true; } + + if (view->shaded) { + set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, true); + } + if (view_is_tiled_and_notify_tiled(view) && !maximized) { set_squared_corners(ssd, true); ssd->state.was_tiled_not_maximized = true; @@ -176,14 +196,14 @@ set_squared_corners(struct ssd *ssd, bool enable) } static void -set_maximize_alt_icon(struct ssd *ssd, bool enable) +set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable) { struct ssd_part *part; struct ssd_button *button; struct ssd_sub_tree *subtree; FOR_EACH_STATE(ssd, subtree) { - part = ssd_get_part(&subtree->parts, LAB_SSD_BUTTON_MAXIMIZE); + part = ssd_get_part(&subtree->parts, type); if (!part) { return; } @@ -214,12 +234,17 @@ ssd_titlebar_update(struct ssd *ssd) && !maximized; if (ssd->state.was_maximized != maximized + || ssd->state.was_shaded != view->shaded || ssd->state.was_tiled_not_maximized != tiled_not_maximized) { set_squared_corners(ssd, maximized || tiled_not_maximized); if (ssd->state.was_maximized != maximized) { - set_maximize_alt_icon(ssd, maximized); + set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, maximized); + } + if (ssd->state.was_shaded != view->shaded) { + set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, view->shaded); } ssd->state.was_maximized = maximized; + ssd->state.was_shaded = view->shaded; ssd->state.was_tiled_not_maximized = tiled_not_maximized; } diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index ca82fe4a..3d147500 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -74,15 +74,6 @@ ssd_max_extents(struct view *view) }; } -bool -ssd_is_button(enum ssd_part_type type) -{ - return type == LAB_SSD_BUTTON_CLOSE - || type == LAB_SSD_BUTTON_MAXIMIZE - || type == LAB_SSD_BUTTON_ICONIFY - || type == LAB_SSD_BUTTON_WINDOW_MENU; -} - enum ssd_part_type ssd_get_part_type(const struct ssd *ssd, struct wlr_scene_node *node) { @@ -257,16 +248,17 @@ ssd_update_geometry(struct ssd *ssd) ssd->state.geometry = current; } bool maximized = ssd->view->maximized == VIEW_AXIS_BOTH; - if (ssd->state.was_maximized != maximized) { + if (ssd->state.was_maximized != maximized + || ssd->state.was_shaded != ssd->view->shaded) { ssd_titlebar_update(ssd); ssd_border_update(ssd); ssd_shadow_update(ssd); /* * Not strictly necessary as ssd_titlebar_update() - * already sets state.was_maximized but to future - * proof this a bit we also set it here again. + * already sets these values, but set here to be safe. */ ssd->state.was_maximized = maximized; + ssd->state.was_shaded = ssd->view->shaded; } bool tiled_and_not_maximized = view_is_tiled(ssd->view) && !maximized; if (ssd->state.was_tiled_not_maximized != tiled_and_not_maximized) { @@ -403,6 +395,7 @@ ssd_enable_shade(struct ssd *ssd, bool enable) if (!ssd) { return; } + ssd_titlebar_update(ssd); ssd_border_update(ssd); wlr_scene_node_set_enabled(&ssd->extents.tree->node, !enable); ssd_shadow_update(ssd); diff --git a/src/theme.c b/src/theme.c index 48677302..c4f576af 100644 --- a/src/theme.c +++ b/src/theme.c @@ -79,6 +79,19 @@ zdrop(struct lab_data_buffer **buffer) } } +static bool +match_button_by_name(struct title_button *b, const char *icon_name) +{ + assert(icon_name); + return (b->type == LAB_SSD_BUTTON_WINDOW_MENU && !strcmp(icon_name, "menu")) + || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max")) + || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max_toggled")) + || (b->type == LAB_SSD_BUTTON_ICONIFY && !strcmp(icon_name, "iconify")) + || (b->type == LAB_SSD_BUTTON_CLOSE && !strcmp(icon_name, "close")) + || (b->type == LAB_SSD_BUTTON_SHADE && !strcmp(icon_name, "shade")) + || (b->type == LAB_SSD_BUTTON_SHADE && !strcmp(icon_name, "shade_toggled")); +} + static enum corner corner_from_icon_name(const char *icon_name) { @@ -86,21 +99,13 @@ corner_from_icon_name(const char *icon_name) struct title_button *b; wl_list_for_each(b, &rc.title_buttons_left, link) { - if ((b->type == LAB_SSD_BUTTON_WINDOW_MENU && !strcmp(icon_name, "menu")) - || (b->type == LAB_SSD_BUTTON_ICONIFY && !strcmp(icon_name, "iconify")) - || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max")) - || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max_toggled")) - || (b->type == LAB_SSD_BUTTON_CLOSE && !strcmp(icon_name, "close"))) { + if (match_button_by_name(b, icon_name)) { return LAB_CORNER_TOP_LEFT; } break; } wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { - if ((b->type == LAB_SSD_BUTTON_WINDOW_MENU && !strcmp(icon_name, "menu")) - || (b->type == LAB_SSD_BUTTON_ICONIFY && !strcmp(icon_name, "iconify")) - || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max")) - || (b->type == LAB_SSD_BUTTON_MAXIMIZE && !strcmp(icon_name, "max_toggled")) - || (b->type == LAB_SSD_BUTTON_CLOSE && !strcmp(icon_name, "close"))) { + if (match_button_by_name(b, icon_name)) { return LAB_CORNER_TOP_RIGHT; } break; @@ -244,6 +249,20 @@ load_buttons(struct theme *theme) .active.rgba = theme->window_active_button_max_unpressed_image_color, .inactive.buffer = &theme->button_restore_inactive_unpressed, .inactive.rgba = theme->window_inactive_button_max_unpressed_image_color, + }, { + .name = "shade", + .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x0c, 0x1e, 0x3f }, + .active.buffer = &theme->button_shade_active_unpressed, + .active.rgba = theme->window_active_button_shade_unpressed_image_color, + .inactive.buffer = &theme->button_shade_inactive_unpressed, + .inactive.rgba = theme->window_inactive_button_shade_unpressed_image_color, + }, { + .name = "shade_toggled", + .fallback_button = (const char[]){ 0x3f, 0x3f, 0x00, 0x3f, 0x1e, 0x0c }, + .active.buffer = &theme->button_unshade_active_unpressed, + .active.rgba = theme->window_active_button_shade_unpressed_image_color, + .inactive.buffer = &theme->button_unshade_inactive_unpressed, + .inactive.rgba = theme->window_inactive_button_shade_unpressed_image_color, }, { .name = "close", .fallback_button = (const char[]){ 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }, @@ -280,6 +299,21 @@ load_buttons(struct theme *theme) .active.rgba = theme->window_active_button_max_unpressed_image_color, .inactive.buffer = &theme->button_restore_inactive_hover, .inactive.rgba = theme->window_inactive_button_max_unpressed_image_color, + }, { + .name = "shade_hover", + /* no fallback (non-hover variant is used instead) */ + .active.buffer = &theme->button_shade_active_hover, + .active.rgba = theme->window_active_button_shade_unpressed_image_color, + .inactive.buffer = &theme->button_shade_inactive_hover, + .inactive.rgba = theme->window_inactive_button_shade_unpressed_image_color, + }, { + .name = "shade_toggled_hover", + .alt_name = "shade_hover_toggled", + /* no fallback (non-hover variant is used instead) */ + .active.buffer = &theme->button_unshade_active_hover, + .active.rgba = theme->window_active_button_shade_unpressed_image_color, + .inactive.buffer = &theme->button_unshade_inactive_hover, + .inactive.rgba = theme->window_inactive_button_shade_unpressed_image_color, }, { .name = "close_hover", /* no fallback (non-hover variant is used instead) */ @@ -509,6 +543,8 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_active_button_iconify_unpressed_image_color); parse_hexstr("#000000", theme->window_active_button_max_unpressed_image_color); + parse_hexstr("#000000", + theme->window_active_button_shade_unpressed_image_color); parse_hexstr("#000000", theme->window_active_button_close_unpressed_image_color); parse_hexstr("#000000", @@ -517,6 +553,8 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_inactive_button_iconify_unpressed_image_color); parse_hexstr("#000000", theme->window_inactive_button_max_unpressed_image_color); + parse_hexstr("#000000", + theme->window_inactive_button_shade_unpressed_image_color); parse_hexstr("#000000", theme->window_inactive_button_close_unpressed_image_color); @@ -681,6 +719,8 @@ entry(struct theme *theme, const char *key, const char *value) theme->window_active_button_iconify_unpressed_image_color); parse_hexstr(value, theme->window_active_button_max_unpressed_image_color); + parse_hexstr(value, + theme->window_active_button_shade_unpressed_image_color); parse_hexstr(value, theme->window_active_button_close_unpressed_image_color); } @@ -691,6 +731,8 @@ entry(struct theme *theme, const char *key, const char *value) theme->window_inactive_button_iconify_unpressed_image_color); parse_hexstr(value, theme->window_inactive_button_max_unpressed_image_color); + parse_hexstr(value, + theme->window_inactive_button_shade_unpressed_image_color); parse_hexstr(value, theme->window_inactive_button_close_unpressed_image_color); } @@ -708,6 +750,10 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->window_active_button_max_unpressed_image_color); } + if (match_glob(key, "window.active.button.shade.unpressed.image.color")) { + parse_hexstr(value, + theme->window_active_button_shade_unpressed_image_color); + } if (match_glob(key, "window.active.button.close.unpressed.image.color")) { parse_hexstr(value, theme->window_active_button_close_unpressed_image_color); @@ -724,6 +770,10 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->window_inactive_button_max_unpressed_image_color); } + if (match_glob(key, "window.inactive.button.shade.unpressed.image.color")) { + parse_hexstr(value, + theme->window_inactive_button_shade_unpressed_image_color); + } if (match_glob(key, "window.inactive.button.close.unpressed.image.color")) { parse_hexstr(value, theme->window_inactive_button_close_unpressed_image_color); @@ -1442,24 +1492,32 @@ theme_finish(struct theme *theme) zdrop(&theme->button_close_active_unpressed); zdrop(&theme->button_maximize_active_unpressed); zdrop(&theme->button_restore_active_unpressed); + zdrop(&theme->button_shade_active_unpressed); + zdrop(&theme->button_unshade_active_unpressed); zdrop(&theme->button_iconify_active_unpressed); zdrop(&theme->button_menu_active_unpressed); zdrop(&theme->button_close_inactive_unpressed); zdrop(&theme->button_maximize_inactive_unpressed); zdrop(&theme->button_restore_inactive_unpressed); + zdrop(&theme->button_shade_inactive_unpressed); + zdrop(&theme->button_unshade_inactive_unpressed); zdrop(&theme->button_iconify_inactive_unpressed); zdrop(&theme->button_menu_inactive_unpressed); zdrop(&theme->button_close_active_hover); zdrop(&theme->button_maximize_active_hover); zdrop(&theme->button_restore_active_hover); + zdrop(&theme->button_shade_active_hover); + zdrop(&theme->button_unshade_active_hover); zdrop(&theme->button_iconify_active_hover); zdrop(&theme->button_menu_active_hover); zdrop(&theme->button_close_inactive_hover); zdrop(&theme->button_maximize_inactive_hover); zdrop(&theme->button_restore_inactive_hover); + zdrop(&theme->button_shade_inactive_hover); + zdrop(&theme->button_unshade_inactive_hover); zdrop(&theme->button_iconify_inactive_hover); zdrop(&theme->button_menu_inactive_hover); From b7bccc8026aebb2e73d9369a0bf450238c1fdd46 Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" Date: Fri, 23 Aug 2024 12:45:14 -0400 Subject: [PATCH 133/232] ssd: add optional omnipresent button --- docs/labwc-config.5.scd | 14 ++++--- docs/rc.xml.all | 6 +++ include/config/default-bindings.h | 5 +++ include/ssd-internal.h | 15 ++++++-- include/ssd.h | 1 + include/theme.h | 10 +++++ src/config/mousebind.c | 2 + src/config/rcxml.c | 4 +- src/ssd/ssd-titlebar.c | 39 +++++++++++++++---- src/ssd/ssd.c | 63 ++++++++++++++----------------- src/theme.c | 57 +++++++++++++++++++++++++++- src/view.c | 1 + 12 files changed, 165 insertions(+), 52 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index ed063aed..a5864974 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -438,10 +438,11 @@ extending outward from the snapped edge. - L: window label (aka. title) - |: empty space (can be used instead of a title) - W: window menu - - S: shade - I: iconify - M: maximize - C: close + - S: shade + - D: omnipresent Example: WLIMC @@ -613,11 +614,12 @@ extending outward from the snapped edge. buttons and the window title are shown. - Title: The area of the titlebar (including blank space) between the window buttons, where the window title is displayed. - - WindowMenu: The button on the left. - - Iconify: The button that looks like an underline. - - Maximize: The button that looks like a box. - - Shade: A button that looks like an overline. - - Close: The button that looks like an X. + - WindowMenu: A button that, by default, displays a window menu. + - Iconify: A button that, by default, iconifies a window. + - Maximize: A button that, by default, toggles maximization of a window. + - Shade: A button that, by default, toggles window shading. + - Omnipresent: A button that, by default, toggles omnipresence of a window. + - Close: A button that, by default, closses a window. - Top: The top edge of the window's border. - Bottom: The bottom edge of the window's border. - Left: The left edge of the window's border. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index f2de0f29..77f412cc 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -447,6 +447,12 @@ + + + + + + diff --git a/include/config/default-bindings.h b/include/config/default-bindings.h index f3de19b2..1be873ff 100644 --- a/include/config/default-bindings.h +++ b/include/config/default-bindings.h @@ -327,6 +327,11 @@ static struct mouse_combos { .button = "Left", .event = "Click", .action = "ToggleShade", + }, { + .context = "Omnipresent", + .button = "Left", + .event = "Click", + .action = "ToggleOmnipresent", }, { .context = "Maximize", .button = "Right", diff --git a/include/ssd-internal.h b/include/ssd-internal.h index c8f4ab7b..3d973355 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -48,9 +48,18 @@ struct ssd { * don't update things we don't have to. */ struct { - bool was_shaded; /* To toggle icon on shade */ - bool was_maximized; /* To un-round corner buttons and toggle icon on maximize */ - bool was_tiled_not_maximized; /* To un-round corner buttons */ + /* Button icons need to be swapped on shade or omnipresent toggles */ + bool was_shaded; + bool was_omnipresent; + + /* + * Corners need to be (un)rounded when toggling tiling or + * maximization, and the button needs to be swapped on + * maximization toggles. + */ + bool was_maximized; + bool was_tiled_not_maximized; + struct wlr_box geometry; struct ssd_state_title { char *text; diff --git a/include/ssd.h b/include/ssd.h index 8b8f77d3..2afd0087 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -26,6 +26,7 @@ enum ssd_part_type { LAB_SSD_BUTTON_ICONIFY, LAB_SSD_BUTTON_WINDOW_MENU, LAB_SSD_BUTTON_SHADE, + LAB_SSD_BUTTON_OMNIPRESENT, LAB_SSD_PART_TITLEBAR, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, diff --git a/include/theme.h b/include/theme.h index 2a40b589..1b15af6b 100644 --- a/include/theme.h +++ b/include/theme.h @@ -55,11 +55,13 @@ struct theme { float window_active_button_max_unpressed_image_color[4]; float window_active_button_close_unpressed_image_color[4]; float window_active_button_shade_unpressed_image_color[4]; + float window_active_button_omnipresent_unpressed_image_color[4]; float window_inactive_button_menu_unpressed_image_color[4]; float window_inactive_button_iconify_unpressed_image_color[4]; float window_inactive_button_max_unpressed_image_color[4]; float window_inactive_button_close_unpressed_image_color[4]; float window_inactive_button_shade_unpressed_image_color[4]; + float window_inactive_button_omnipresent_unpressed_image_color[4]; /* TODO: add pressed and hover colors for buttons */ int menu_item_padding_x; @@ -118,6 +120,8 @@ struct theme { struct lab_data_buffer *button_menu_active_unpressed; struct lab_data_buffer *button_shade_active_unpressed; struct lab_data_buffer *button_unshade_active_unpressed; + struct lab_data_buffer *button_omnipresent_active_unpressed; + struct lab_data_buffer *button_exclusive_active_unpressed; struct lab_data_buffer *button_close_inactive_unpressed; struct lab_data_buffer *button_maximize_inactive_unpressed; @@ -126,6 +130,8 @@ struct theme { struct lab_data_buffer *button_menu_inactive_unpressed; struct lab_data_buffer *button_shade_inactive_unpressed; struct lab_data_buffer *button_unshade_inactive_unpressed; + struct lab_data_buffer *button_omnipresent_inactive_unpressed; + struct lab_data_buffer *button_exclusive_inactive_unpressed; /* hover variants are optional and may be NULL */ struct lab_data_buffer *button_close_active_hover; @@ -135,6 +141,8 @@ struct theme { struct lab_data_buffer *button_menu_active_hover; struct lab_data_buffer *button_shade_active_hover; struct lab_data_buffer *button_unshade_active_hover; + struct lab_data_buffer *button_omnipresent_active_hover; + struct lab_data_buffer *button_exclusive_active_hover; struct lab_data_buffer *button_close_inactive_hover; struct lab_data_buffer *button_maximize_inactive_hover; @@ -143,6 +151,8 @@ struct theme { struct lab_data_buffer *button_menu_inactive_hover; struct lab_data_buffer *button_shade_inactive_hover; struct lab_data_buffer *button_unshade_inactive_hover; + struct lab_data_buffer *button_omnipresent_inactive_hover; + struct lab_data_buffer *button_exclusive_inactive_hover; struct lab_data_buffer *corner_top_left_active_normal; struct lab_data_buffer *corner_top_right_active_normal; diff --git a/src/config/mousebind.c b/src/config/mousebind.c index f7b0b44e..82f4c6d6 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -116,6 +116,8 @@ context_from_str(const char *str) return LAB_SSD_BUTTON_WINDOW_MENU; } else if (!strcasecmp(str, "Shade")) { return LAB_SSD_BUTTON_SHADE; + } else if (!strcasecmp(str, "Omnipresent")) { + return LAB_SSD_BUTTON_OMNIPRESENT; } else if (!strcasecmp(str, "Titlebar")) { return LAB_SSD_PART_TITLEBAR; } else if (!strcasecmp(str, "Title")) { diff --git a/src/config/rcxml.c b/src/config/rcxml.c index 71b22768..d6ce92b6 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -161,7 +161,9 @@ fill_title_layout(char *nodename, char *content) case 'S': type = LAB_SSD_BUTTON_SHADE; break; - /* case 'D': omnipresent */ + case 'D': + type = LAB_SSD_BUTTON_OMNIPRESENT; + break; default: continue; } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 834d8831..407753bb 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -79,6 +79,21 @@ add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type typ active ? &theme->button_unshade_active_hover->base : &theme->button_unshade_inactive_hover->base); break; + case LAB_SSD_BUTTON_OMNIPRESENT: + /* Omnipresent button has an alternate state when enabled */ + btn_root = add_scene_button(&subtree->parts, type, parent, + active ? &theme->button_omnipresent_active_unpressed->base + : &theme->button_omnipresent_inactive_unpressed->base, + active ? &theme->button_omnipresent_active_hover->base + : &theme->button_omnipresent_inactive_hover->base, + x, view); + btn = node_ssd_button_from_node(btn_root->node); + add_toggled_icon(btn, &subtree->parts, LAB_SSD_BUTTON_OMNIPRESENT, + active ? &theme->button_exclusive_active_unpressed->base + : &theme->button_exclusive_inactive_unpressed->base, + active ? &theme->button_exclusive_active_hover->base + : &theme->button_exclusive_inactive_hover->base); + break; case LAB_SSD_BUTTON_CLOSE: add_scene_button(&subtree->parts, type, parent, active ? &theme->button_close_active_unpressed->base @@ -164,6 +179,10 @@ ssd_titlebar_create(struct ssd *ssd) set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, true); } + if (view->visible_on_all_workspaces) { + set_alt_button_icon(ssd, LAB_SSD_BUTTON_OMNIPRESENT, true); + } + if (view_is_tiled_and_notify_tiled(view) && !maximized) { set_squared_corners(ssd, true); ssd->state.was_tiled_not_maximized = true; @@ -230,24 +249,30 @@ ssd_titlebar_update(struct ssd *ssd) struct theme *theme = view->server->theme; bool maximized = view->maximized == VIEW_AXIS_BOTH; - bool tiled_not_maximized = view_is_tiled_and_notify_tiled(ssd->view) - && !maximized; + bool tiled_not_maximized = + view_is_tiled_and_notify_tiled(ssd->view) && !maximized; if (ssd->state.was_maximized != maximized - || ssd->state.was_shaded != view->shaded || ssd->state.was_tiled_not_maximized != tiled_not_maximized) { set_squared_corners(ssd, maximized || tiled_not_maximized); if (ssd->state.was_maximized != maximized) { set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, maximized); } - if (ssd->state.was_shaded != view->shaded) { - set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, view->shaded); - } ssd->state.was_maximized = maximized; - ssd->state.was_shaded = view->shaded; ssd->state.was_tiled_not_maximized = tiled_not_maximized; } + if (ssd->state.was_shaded != view->shaded) { + set_alt_button_icon(ssd, LAB_SSD_BUTTON_SHADE, view->shaded); + ssd->state.was_shaded = view->shaded; + } + + if (ssd->state.was_omnipresent != view->visible_on_all_workspaces) { + set_alt_button_icon(ssd, LAB_SSD_BUTTON_OMNIPRESENT, + view->visible_on_all_workspaces); + ssd->state.was_omnipresent = view->visible_on_all_workspaces; + } + if (width == ssd->state.geometry.width) { return; } diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 3d147500..98d40d4f 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -221,13 +221,16 @@ ssd_update_geometry(struct ssd *ssd) return; } + struct view *view = ssd->view; + assert(view); + struct wlr_box cached = ssd->state.geometry; - struct wlr_box current = ssd->view->current; + struct wlr_box current = view->current; int min_view_width = rc.theme->window_button_width * ( wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); int eff_width = current.width; - int eff_height = view_effective_height(ssd->view, /* use_pending */ false); + int eff_height = view_effective_height(view, /* use_pending */ false); if (eff_width > 0 && eff_width < min_view_width) { /* @@ -241,39 +244,31 @@ ssd_update_geometry(struct ssd *ssd) return; } - if (eff_width == cached.width && eff_height == cached.height) { - if (current.x != cached.x || current.y != cached.y) { - /* Dynamically resize extents based on position and usable_area */ - ssd_extents_update(ssd); - ssd->state.geometry = current; - } - bool maximized = ssd->view->maximized == VIEW_AXIS_BOTH; - if (ssd->state.was_maximized != maximized - || ssd->state.was_shaded != ssd->view->shaded) { - ssd_titlebar_update(ssd); - ssd_border_update(ssd); - ssd_shadow_update(ssd); - /* - * Not strictly necessary as ssd_titlebar_update() - * already sets these values, but set here to be safe. - */ - ssd->state.was_maximized = maximized; - ssd->state.was_shaded = ssd->view->shaded; - } - bool tiled_and_not_maximized = view_is_tiled(ssd->view) && !maximized; - if (ssd->state.was_tiled_not_maximized != tiled_and_not_maximized) { - ssd_titlebar_update(ssd); - ssd_border_update(ssd); - /* see above about being future proof */ - ssd->state.was_tiled_not_maximized = tiled_and_not_maximized; - } - return; + bool update_area = eff_width != cached.width || eff_height != cached.height; + bool update_extents = update_area + || current.x != cached.x || current.y != cached.y; + + bool maximized = view->maximized == VIEW_AXIS_BOTH; + bool tiled_not_maximized = view_is_tiled(view) && !maximized; + + bool state_changed = ssd->state.was_maximized != maximized + || ssd->state.was_shaded != view->shaded + || ssd->state.was_tiled_not_maximized != tiled_not_maximized + || ssd->state.was_omnipresent != view->visible_on_all_workspaces; + + if (update_extents) { + ssd_extents_update(ssd); + } + + if (update_area || state_changed) { + ssd_titlebar_update(ssd); + ssd_border_update(ssd); + ssd_shadow_update(ssd); + } + + if (update_extents) { + ssd->state.geometry = current; } - ssd_extents_update(ssd); - ssd_titlebar_update(ssd); - ssd_border_update(ssd); - ssd_shadow_update(ssd); - ssd->state.geometry = current; } void diff --git a/src/theme.c b/src/theme.c index c4f576af..94ba0a0b 100644 --- a/src/theme.c +++ b/src/theme.c @@ -89,7 +89,9 @@ match_button_by_name(struct title_button *b, const char *icon_name) || (b->type == LAB_SSD_BUTTON_ICONIFY && !strcmp(icon_name, "iconify")) || (b->type == LAB_SSD_BUTTON_CLOSE && !strcmp(icon_name, "close")) || (b->type == LAB_SSD_BUTTON_SHADE && !strcmp(icon_name, "shade")) - || (b->type == LAB_SSD_BUTTON_SHADE && !strcmp(icon_name, "shade_toggled")); + || (b->type == LAB_SSD_BUTTON_SHADE && !strcmp(icon_name, "shade_toggled")) + || (b->type == LAB_SSD_BUTTON_OMNIPRESENT && !strcmp(icon_name, "desk")) + || (b->type == LAB_SSD_BUTTON_OMNIPRESENT && !strcmp(icon_name, "desk_toggled")); } static enum corner @@ -263,6 +265,20 @@ load_buttons(struct theme *theme) .active.rgba = theme->window_active_button_shade_unpressed_image_color, .inactive.buffer = &theme->button_unshade_inactive_unpressed, .inactive.rgba = theme->window_inactive_button_shade_unpressed_image_color, + }, { + .name = "desk", + .fallback_button = (const char[]){ 0x33, 0x33, 0x00, 0x00, 0x33, 0x33 }, + .active.buffer = &theme->button_omnipresent_active_unpressed, + .active.rgba = theme->window_active_button_omnipresent_unpressed_image_color, + .inactive.buffer = &theme->button_omnipresent_inactive_unpressed, + .inactive.rgba = theme->window_inactive_button_omnipresent_unpressed_image_color, + }, { + .name = "desk_toggled", + .fallback_button = (const char[]){ 0x00, 0x1e, 0x1a, 0x16, 0x1e, 0x00 }, + .active.buffer = &theme->button_exclusive_active_unpressed, + .active.rgba = theme->window_active_button_omnipresent_unpressed_image_color, + .inactive.buffer = &theme->button_exclusive_inactive_unpressed, + .inactive.rgba = theme->window_inactive_button_omnipresent_unpressed_image_color, }, { .name = "close", .fallback_button = (const char[]){ 0x33, 0x3f, 0x1e, 0x1e, 0x3f, 0x33 }, @@ -314,6 +330,21 @@ load_buttons(struct theme *theme) .active.rgba = theme->window_active_button_shade_unpressed_image_color, .inactive.buffer = &theme->button_unshade_inactive_hover, .inactive.rgba = theme->window_inactive_button_shade_unpressed_image_color, + }, { + .name = "desk_hover", + /* no fallback (non-hover variant is used instead) */ + .active.buffer = &theme->button_omnipresent_active_hover, + .active.rgba = theme->window_active_button_omnipresent_unpressed_image_color, + .inactive.buffer = &theme->button_omnipresent_inactive_hover, + .inactive.rgba = theme->window_inactive_button_omnipresent_unpressed_image_color, + }, { + .name = "desk_toggled_hover", + .alt_name = "desk_hover_toggled", + /* no fallback (non-hover variant is used instead) */ + .active.buffer = &theme->button_exclusive_active_hover, + .active.rgba = theme->window_active_button_omnipresent_unpressed_image_color, + .inactive.buffer = &theme->button_exclusive_inactive_hover, + .inactive.rgba = theme->window_inactive_button_omnipresent_unpressed_image_color, }, { .name = "close_hover", /* no fallback (non-hover variant is used instead) */ @@ -545,6 +576,8 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_active_button_max_unpressed_image_color); parse_hexstr("#000000", theme->window_active_button_shade_unpressed_image_color); + parse_hexstr("#000000", + theme->window_active_button_omnipresent_unpressed_image_color); parse_hexstr("#000000", theme->window_active_button_close_unpressed_image_color); parse_hexstr("#000000", @@ -555,6 +588,8 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_inactive_button_max_unpressed_image_color); parse_hexstr("#000000", theme->window_inactive_button_shade_unpressed_image_color); + parse_hexstr("#000000", + theme->window_inactive_button_omnipresent_unpressed_image_color); parse_hexstr("#000000", theme->window_inactive_button_close_unpressed_image_color); @@ -721,6 +756,8 @@ entry(struct theme *theme, const char *key, const char *value) theme->window_active_button_max_unpressed_image_color); parse_hexstr(value, theme->window_active_button_shade_unpressed_image_color); + parse_hexstr(value, + theme->window_active_button_omnipresent_unpressed_image_color); parse_hexstr(value, theme->window_active_button_close_unpressed_image_color); } @@ -733,6 +770,8 @@ entry(struct theme *theme, const char *key, const char *value) theme->window_inactive_button_max_unpressed_image_color); parse_hexstr(value, theme->window_inactive_button_shade_unpressed_image_color); + parse_hexstr(value, + theme->window_inactive_button_omnipresent_unpressed_image_color); parse_hexstr(value, theme->window_inactive_button_close_unpressed_image_color); } @@ -754,6 +793,10 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->window_active_button_shade_unpressed_image_color); } + if (match_glob(key, "window.active.button.omnipresent.unpressed.image.color")) { + parse_hexstr(value, + theme->window_active_button_omnipresent_unpressed_image_color); + } if (match_glob(key, "window.active.button.close.unpressed.image.color")) { parse_hexstr(value, theme->window_active_button_close_unpressed_image_color); @@ -774,6 +817,10 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->window_inactive_button_shade_unpressed_image_color); } + if (match_glob(key, "window.inactive.button.omnipresent.unpressed.image.color")) { + parse_hexstr(value, + theme->window_inactive_button_omnipresent_unpressed_image_color); + } if (match_glob(key, "window.inactive.button.close.unpressed.image.color")) { parse_hexstr(value, theme->window_inactive_button_close_unpressed_image_color); @@ -1494,6 +1541,8 @@ theme_finish(struct theme *theme) zdrop(&theme->button_restore_active_unpressed); zdrop(&theme->button_shade_active_unpressed); zdrop(&theme->button_unshade_active_unpressed); + zdrop(&theme->button_omnipresent_active_unpressed); + zdrop(&theme->button_exclusive_active_unpressed); zdrop(&theme->button_iconify_active_unpressed); zdrop(&theme->button_menu_active_unpressed); @@ -1502,6 +1551,8 @@ theme_finish(struct theme *theme) zdrop(&theme->button_restore_inactive_unpressed); zdrop(&theme->button_shade_inactive_unpressed); zdrop(&theme->button_unshade_inactive_unpressed); + zdrop(&theme->button_omnipresent_inactive_unpressed); + zdrop(&theme->button_exclusive_inactive_unpressed); zdrop(&theme->button_iconify_inactive_unpressed); zdrop(&theme->button_menu_inactive_unpressed); @@ -1510,6 +1561,8 @@ theme_finish(struct theme *theme) zdrop(&theme->button_restore_active_hover); zdrop(&theme->button_shade_active_hover); zdrop(&theme->button_unshade_active_hover); + zdrop(&theme->button_omnipresent_active_hover); + zdrop(&theme->button_exclusive_active_hover); zdrop(&theme->button_iconify_active_hover); zdrop(&theme->button_menu_active_hover); @@ -1518,6 +1571,8 @@ theme_finish(struct theme *theme) zdrop(&theme->button_restore_inactive_hover); zdrop(&theme->button_shade_inactive_hover); zdrop(&theme->button_unshade_inactive_hover); + zdrop(&theme->button_omnipresent_inactive_hover); + zdrop(&theme->button_exclusive_inactive_hover); zdrop(&theme->button_iconify_inactive_hover); zdrop(&theme->button_menu_inactive_hover); diff --git a/src/view.c b/src/view.c index 9ac24022..4d8586b8 100644 --- a/src/view.c +++ b/src/view.c @@ -1494,6 +1494,7 @@ view_toggle_visible_on_all_workspaces(struct view *view) { assert(view); view->visible_on_all_workspaces = !view->visible_on_all_workspaces; + ssd_update_geometry(view->ssd); } void From f2b6661db4828d40a45002bb749aaae6a6d5ee89 Mon Sep 17 00:00:00 2001 From: Standreas Date: Sat, 24 Aug 2024 15:42:10 +0200 Subject: [PATCH 134/232] Fix typo in rc.xml.all --- docs/rc.xml.all | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rc.xml.all b/docs/rc.xml.all index 77f412cc..d0df9760 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -447,7 +447,7 @@ - + From dda47a5e14b33e49ed8349cd9a6546f78fded2a9 Mon Sep 17 00:00:00 2001 From: Orfeas <38209077+0xfea5@users.noreply.github.com> Date: Wed, 21 Aug 2024 07:38:44 +0300 Subject: [PATCH 135/232] action: make "FocusOutput" behave like "MoveToOutput" --- docs/labwc-actions.5.scd | 18 ++++++-- include/common/direction.h | 10 +++++ include/labwc.h | 16 +++++++ include/view.h | 5 +-- src/action.c | 65 +++++++++++++++------------- src/common/direction.c | 42 ++++++++++++++++++ src/common/meson.build | 3 +- src/desktop.c | 4 +- src/output.c | 48 ++++++++++++++++++++ src/view.c | 89 +------------------------------------- 10 files changed, 172 insertions(+), 128 deletions(-) create mode 100644 include/common/direction.h create mode 100644 src/common/direction.c diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index 1c9e2ec1..6b9dfe8d 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -194,10 +194,20 @@ Actions are used in menus and keyboard/mouse bindings. to 'fullscreen' or 'fullscreenForced', tearing will still only be enabled if the active window is in fullscreen mode. -** - Give focus to topmost window on given output and warp the cursor - to the center of the window. If the given output does not contain - any windows, the cursor is centered on the given output. +** + Give focus to topmost window on other output and warp the cursor + to the center of the window. + + If *output* is specified, the focus is given to the specified output and + *direction* is ignored. If *output* is omitted, *direction* may be one + of "left", "right", "up" or "down" to indicate that the focus should be + given 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 is no. + + If the target output does not contain any windows, the cursor will + be centered on the output. ** Moves active window to other output, unless the window state is diff --git a/include/common/direction.h b/include/common/direction.h new file mode 100644 index 00000000..bc8cbd0c --- /dev/null +++ b/include/common/direction.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_DIRECTION_H +#define LABWC_DIRECTION_H + +#include "view.h" + +enum wlr_direction direction_from_view_edge(enum view_edge edge); +enum wlr_direction direction_get_opposite(enum wlr_direction direction); + +#endif /* LABWC_DIRECTION_H */ diff --git a/include/labwc.h b/include/labwc.h index 30f9fbaf..810fed13 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -460,6 +460,7 @@ void desktop_focus_view_or_surface(struct seat *seat, struct view *view, void desktop_arrange_all_views(struct server *server); void desktop_focus_output(struct output *output); +void warp_cursor(struct view *view); struct view *desktop_topmost_focusable_view(struct server *server); /** @@ -539,6 +540,21 @@ struct output *output_from_wlr_output(struct server *server, struct output *output_from_name(struct server *server, const char *name); struct output *output_nearest_to(struct server *server, int lx, int ly); struct output *output_nearest_to_cursor(struct server *server); + +/** + * output_get_adjacent() - get next output, in a given direction, + * from a given output + * + * @output: reference output + * @edge: direction in which to look for the nearest output + * @wrap: if true, wrap around at layout edge + * + * Note: if output is NULL, the output nearest the cursor will be used as the + * reference instead. + */ +struct output *output_get_adjacent(struct output *output, + enum view_edge edge, bool wrap); + bool output_is_usable(struct output *output); void output_update_usable_area(struct output *output); void output_update_all_usable_areas(struct server *server, bool layout_changed); diff --git a/include/view.h b/include/view.h index 0fde958e..6f47f2a7 100644 --- a/include/view.h +++ b/include/view.h @@ -2,6 +2,7 @@ #ifndef LABWC_VIEW_H #define LABWC_VIEW_H +#include "config/rcxml.h" #include "config.h" #include "ssd.h" #include @@ -482,7 +483,7 @@ void view_center(struct view *view, const struct wlr_box *ref); * @policy: placement policy to apply */ void view_place_by_policy(struct view *view, bool allow_cursor, - enum view_placement_policy); + enum view_placement_policy policy); void view_constrain_size_to_that_of_usable_area(struct view *view); void view_restore_to(struct view *view, struct wlr_box geometry); @@ -548,8 +549,6 @@ 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, - bool wrap); enum view_axis view_axis_parse(const char *direction); enum view_edge view_edge_parse(const char *direction); enum view_placement_policy view_placement_parse(const char *policy); diff --git a/src/action.c b/src/action.c index c38cba70..79008566 100644 --- a/src/action.c +++ b/src/action.c @@ -19,7 +19,6 @@ #include "menu/menu.h" #include "osd.h" #include "output-virtual.h" -#include "placement.h" #include "regions.h" #include "ssd.h" #include "view.h" @@ -413,11 +412,6 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char } break; case ACTION_TYPE_FOCUS_OUTPUT: - if (!strcmp(argument, "output")) { - action_arg_add_str(action, argument, content); - goto cleanup; - } - break; case ACTION_TYPE_MOVE_TO_OUTPUT: if (!strcmp(argument, "output")) { action_arg_add_str(action, argument, content); @@ -552,9 +546,6 @@ action_is_valid(struct action *action) case ACTION_TYPE_SNAP_TO_REGION: arg_name = "region"; break; - case ACTION_TYPE_FOCUS_OUTPUT: - arg_name = "output"; - break; case ACTION_TYPE_IF: case ACTION_TYPE_FOR_EACH: ; /* works around "a label can only be part of a statement" */ @@ -725,6 +716,29 @@ start_window_cycling(struct server *server, enum lab_cycle_dir direction) osd_update(server); } +static struct output * +get_target_output(struct output *output, struct server *server, + struct action *action) +{ + const char *output_name = action_get_str(action, "output", NULL); + struct output *target = NULL; + + if (output_name) { + target = output_from_name(server, output_name); + } else { + enum view_edge edge = + action_get_int(action, "direction", VIEW_EDGE_INVALID); + bool wrap = action_get_bool(action, "wrap", false); + target = output_get_adjacent(output, edge, wrap); + } + + if (!target) { + wlr_log(WLR_DEBUG, "Invalid output"); + } + + return target; +} + void actions_run(struct view *activator, struct server *server, struct wl_list *actions, uint32_t resize_edges) @@ -739,6 +753,9 @@ actions_run(struct view *activator, struct server *server, struct view *view; struct action *action; + struct output *output; + struct output *target; + wl_list_for_each(action, actions, link) { wlr_log(WLR_DEBUG, "Handling action %u: %s", action->type, action_names[action->type]); @@ -1009,25 +1026,10 @@ actions_run(struct view *activator, struct server *server, if (!view) { break; } - const char *output_name = action_get_str(action, "output", NULL); - struct output *target = NULL; - if (output_name) { - target = output_from_name(view->server, output_name); - } else { - enum view_edge edge = action_get_int(action, "direction", 0); - bool wrap = action_get_bool(action, "wrap", false); - target = view_get_adjacent_output(view, edge, wrap); + target = get_target_output(view->output, server, action); + if (target) { + view_move_to_output(view, target); } - if (!target) { - /* - * Most likely because we're already on the - * output furthest in the requested direction - * or the output or direction was invalid. - */ - wlr_log(WLR_DEBUG, "Invalid output"); - break; - } - view_move_to_output(view, target); break; case ACTION_TYPE_FIT_TO_OUTPUT: if (!view) { @@ -1039,7 +1041,7 @@ actions_run(struct view *activator, struct server *server, if (!view) { break; } - struct output *output = view->output; + output = view->output; if (!output) { break; } @@ -1058,9 +1060,10 @@ actions_run(struct view *activator, struct server *server, } break; case ACTION_TYPE_FOCUS_OUTPUT: - { - const char *output_name = action_get_str(action, "output", NULL); - desktop_focus_output(output_from_name(server, output_name)); + output = output_nearest_to_cursor(server); + target = get_target_output(output, server, action); + if (target) { + desktop_focus_output(target); } break; case ACTION_TYPE_IF: diff --git a/src/common/direction.c b/src/common/direction.c new file mode 100644 index 00000000..fa88eee4 --- /dev/null +++ b/src/common/direction.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include +#include "common/direction.h" +#include "view.h" + +enum wlr_direction +direction_from_view_edge(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 WLR_DIRECTION_UP; + } +} + +enum wlr_direction +direction_get_opposite(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: + assert(0); /* Unreachable */ + return WLR_DIRECTION_UP; + } +} diff --git a/src/common/meson.build b/src/common/meson.build index 1569f775..d9be1d1a 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -1,5 +1,6 @@ labwc_sources += files( - 'box.c', + 'direction.c', + 'box.c', 'buf.c', 'dir.c', 'fd-util.c', diff --git a/src/desktop.c b/src/desktop.c index 2c0db207..e30154b3 100644 --- a/src/desktop.c +++ b/src/desktop.c @@ -197,10 +197,10 @@ desktop_focus_output(struct output *output) if (wlr_output_layout_intersects(layout, output->wlr_output, &view->current)) { desktop_focus_view(view, /*raise*/ false); - wlr_cursor_warp(output->server->seat.cursor, NULL, + wlr_cursor_warp(view->server->seat.cursor, NULL, view->current.x + view->current.width / 2, view->current.y + view->current.height / 2); - cursor_update_focus(output->server); + cursor_update_focus(view->server); return; } } diff --git a/src/output.c b/src/output.c index a94c7c2d..15c8ed46 100644 --- a/src/output.c +++ b/src/output.c @@ -18,6 +18,7 @@ #include #include #include +#include "common/direction.h" #include "common/macros.h" #include "common/mem.h" #include "common/scene-helpers.h" @@ -880,6 +881,53 @@ output_nearest_to_cursor(struct server *server) server->seat.cursor->y); } +struct output * +output_get_adjacent(struct output *output, enum view_edge edge, bool wrap) +{ + if (!output_is_usable(output)) { + wlr_log(WLR_ERROR, + "output is not usable, cannot find adjacent output"); + 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 = output->server->output_layout; + enum wlr_direction direction = direction_from_view_edge(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, + direction_get_opposite(direction), current_output, lx, ly); + } + + /* + * When "adjacent" output is the same as the original, there is no + * adjacent + */ + if (!new_output || new_output == current_output) { + return NULL; + } + + output = output_from_wlr_output(output->server, new_output); + if (!output_is_usable(output)) { + wlr_log(WLR_ERROR, "invalid output in layout"); + return NULL; + } + + return output; +} + bool output_is_usable(struct output *output) { diff --git a/src/view.c b/src/view.c index 4d8586b8..b1b4265a 100644 --- a/src/view.c +++ b/src/view.c @@ -1790,91 +1790,6 @@ 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, bool wrap) -{ - assert(view); - struct output *output = view->output; - if (!output_is_usable(output)) { - wlr_log(WLR_ERROR, - "view has no output, cannot find adjacent output"); - 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; - 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 - */ - if (!new_output || new_output == current_output) { - return NULL; - } - - output = output_from_wlr_output(view->server, new_output); - if (!output_is_usable(output)) { - wlr_log(WLR_ERROR, "invalid output in layout"); - return NULL; - } - - return output; -} - static int shift_view_to_usable_1d(int size, int cur_pos, int cur_lo, int cur_extent, @@ -1936,7 +1851,7 @@ 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, /* wrap */ false); + output_get_adjacent(view->output, direction, /* wrap */ false); if (!output) { return; } @@ -2115,7 +2030,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, /* wrap */ false); + output = output_get_adjacent(view->output, edge, /* wrap */ false); if (!output) { /* From 1bcaf8255f240ec93f492a84f8d9ab3df4ce9149 Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" Date: Sun, 25 Aug 2024 00:00:24 -0400 Subject: [PATCH 136/232] action: rename "omnipresent" button options for Openbox compatibility - Mouse context "Omnipresent" -> "AllDesktops" - Theme elements "window.*.button.omnipresent" -> "window.*.button.desk" - Update documentation accordingly --- docs/labwc-config.5.scd | 8 ++++---- docs/labwc-theme.5.scd | 20 +++++++++++++------- docs/rc.xml.all | 2 +- include/config/default-bindings.h | 2 +- src/config/mousebind.c | 2 +- src/theme.c | 4 ++-- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index a5864974..7436fc5f 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -439,10 +439,10 @@ extending outward from the snapped edge. - |: empty space (can be used instead of a title) - W: window menu - I: iconify - - M: maximize + - M: maximize toggle - C: close - - S: shade - - D: omnipresent + - S: shade toggle + - D: all-desktops toggle Example: WLIMC @@ -618,7 +618,7 @@ extending outward from the snapped edge. - Iconify: A button that, by default, iconifies a window. - Maximize: A button that, by default, toggles maximization of a window. - Shade: A button that, by default, toggles window shading. - - Omnipresent: A button that, by default, toggles omnipresence of a window. + - AllDesktops: A button that, by default, toggles omnipresence of a window. - Close: A button that, by default, closses a window. - Top: The top edge of the window's border. - Bottom: The bottom edge of the window's border. diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index 9f8310af..57957dc5 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -130,13 +130,19 @@ labwc-config(5). Color of the images in titlebar buttons in their default, unpressed, state. This element is for non-focused windows. -Note: The button elements (i.e. window.[in]active.button.\*) support defining -different types of buttons individually by inserting the type ("menu", -"iconify", "max" and "close") after the button node. For example: -window.active.button.iconify.unpressed.image.color -This syntax is not documented on the openbox.org wiki, but is supported by -openbox and is used by many popular themes. For the sake of brevity, these -elements are not listed here, but are supported. +Note: Button elements (i.e. window.[in]active.button.\*) may be defined uniquely +for each type of button ("menu", "iconify", "max", "shade", "desk" or "close") +by appending the type after the "button" node. For example: + +- window.active.button.iconify.unpressed.image.color indicates the color of the +iconify button for active windows; while + +- window.inactive.button.close.unpressed.image.color indicates the color of the +close button for inactive windows. + +This syntax is not documented by Openbox, but is supported and is used by many +popular themes. For the sake of brevity, these elements are not listed here, but +all are supported. *window.active.shadow.size* Size of the drop-shadow for the focused window, in pixels. diff --git a/docs/rc.xml.all b/docs/rc.xml.all index d0df9760..f7bba998 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -447,7 +447,7 @@ - + diff --git a/include/config/default-bindings.h b/include/config/default-bindings.h index 1be873ff..46a3819e 100644 --- a/include/config/default-bindings.h +++ b/include/config/default-bindings.h @@ -328,7 +328,7 @@ static struct mouse_combos { .event = "Click", .action = "ToggleShade", }, { - .context = "Omnipresent", + .context = "AllDesktops", .button = "Left", .event = "Click", .action = "ToggleOmnipresent", diff --git a/src/config/mousebind.c b/src/config/mousebind.c index 82f4c6d6..dc3dfb65 100644 --- a/src/config/mousebind.c +++ b/src/config/mousebind.c @@ -116,7 +116,7 @@ context_from_str(const char *str) return LAB_SSD_BUTTON_WINDOW_MENU; } else if (!strcasecmp(str, "Shade")) { return LAB_SSD_BUTTON_SHADE; - } else if (!strcasecmp(str, "Omnipresent")) { + } else if (!strcasecmp(str, "AllDesktops")) { return LAB_SSD_BUTTON_OMNIPRESENT; } else if (!strcasecmp(str, "Titlebar")) { return LAB_SSD_PART_TITLEBAR; diff --git a/src/theme.c b/src/theme.c index 94ba0a0b..28408990 100644 --- a/src/theme.c +++ b/src/theme.c @@ -793,7 +793,7 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->window_active_button_shade_unpressed_image_color); } - if (match_glob(key, "window.active.button.omnipresent.unpressed.image.color")) { + if (match_glob(key, "window.active.button.desk.unpressed.image.color")) { parse_hexstr(value, theme->window_active_button_omnipresent_unpressed_image_color); } @@ -817,7 +817,7 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->window_inactive_button_shade_unpressed_image_color); } - if (match_glob(key, "window.inactive.button.omnipresent.unpressed.image.color")) { + if (match_glob(key, "window.inactive.button.desk.unpressed.image.color")) { parse_hexstr(value, theme->window_inactive_button_omnipresent_unpressed_image_color); } From 72f59c32a2d2ee33bf74452cea39c821ce60fbb2 Mon Sep 17 00:00:00 2001 From: 01micko <01micko#gmail.com> Date: Sun, 25 Aug 2024 14:47:00 +1000 Subject: [PATCH 137/232] docs: add description for new theme buttons 'shade' and 'desk' --- docs/labwc-theme.5.scd | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index 57957dc5..42dd0407 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -324,6 +324,10 @@ file within a particular theme. The following xbm buttons are supported: - close.xbm - menu.xbm - max_toggled.xbm +- desk.xbm +- desk_toggled.xbm +- shade.xbm +- shade_toggled.xbm Additional icons can be defined to be shown when the mouse pointer is hovering over the button in question: @@ -333,6 +337,10 @@ over the button in question: - close_hover.xbm - menu_hover.xbm - max_toggled_hover.xbm +- desk_hover.xbm +- desk_toggle_hover.xbm +- shade_hover.xbm +- shade_toggle_hover.xbm One advantage of xbm buttons over other formats is that they change color based on the theme. Other formats use the suffices "-active" and "-inactive" to align @@ -366,6 +374,22 @@ following icons should be added: - menu_hover-active.[png|svg] - menu_hover-inactive.[png|svg] - menu-inactive.[png|svg] +- shade-active.[png|svg] +- shade_hover-active.[png|svg] +- shade_hover-inactive.[png|svg] +- shade-inactive.[png|svg] +- shade_toggled-active.[png|svg] +- shade_toggled-inactive.[png|svg] +- shade_toggled_hover-active.[png|svg] +- shade_toggled_hover-inactive.[png|svg] +- desk-active.[png|svg] +- desk_hover-active.[png|svg] +- desk_hover-inactive.[png|svg] +- desk-inactive.[png|svg] +- desk_toggled-active.[png|svg] +- desk_toggled-inactive.[png|svg] +- desk_toggled_hover-active.[png|svg] +- desk_toggled_hover-inactive.[png|svg] # DEFINITIONS From 3fa9810a3660e7b5dc7654568033b080f9b2cc47 Mon Sep 17 00:00:00 2001 From: micko <01micko@gmail.com> Date: Mon, 26 Aug 2024 10:16:43 +1000 Subject: [PATCH 138/232] README.md: update config files to six --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88ed65ad..f6f206f1 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ prevent installing the wlroots headers: ## 3. Configuration User config files are located at `${XDG_CONFIG_HOME:-$HOME/.config/labwc/}` -with the following five files being used: [rc.xml], [menu.xml], [autostart], [shutdown], +with the following six files being used: [rc.xml], [menu.xml], [autostart], [shutdown], [environment] and [themerc-override]. Run `labwc --reconfigure` to reload configuration and theme. From 2e19bd4d5b3c67c27d13be24d41a0ea9ba2f1626 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 7 Aug 2024 12:45:23 +0900 Subject: [PATCH 139/232] src/interactive.c: don't unshade when view is un-tiled by dragging Unshading a maximized/tiled window at the start of interactive move doesn't seem to be useful and caused flicker. --- src/interactive.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/interactive.c b/src/interactive.c index 13392b06..570d80fc 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -56,7 +56,6 @@ interactive_move_tiled_view_to(struct server *server, struct view *view, } } - view_set_shade(view, false); view_set_untiled(view); view_restore_to(view, *geometry); server->move_pending = false; @@ -100,11 +99,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) } if (!view_is_floating(view)) { /* - * Un-maximize, unshade and restore natural - * width/height. - * Don't reset tiled state yet since we may want - * to keep it (in the snap-to-maximize case). - * + * Un-maximize and restore natural width/height. * If the natural geometry is unknown (possible * with xdg-shell views), then we set a size of * 0x0 here and determine the correct geometry From 1f1bdad0873561d32c39674f5808931ef8880bec Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Wed, 7 Aug 2024 09:17:25 +0900 Subject: [PATCH 140/232] interactive: allow moving horizontally/vertically maximized window Applies drag resistance unidirectionally for horizontally/vertically maximized windows, allowing them to be dragged without being untiled immediately. When the distance of cursor movement orthogonal to the maximized direction exceeds . While dragging a horizontally/vertically maximized window, edge/region snapping is disabled to prevent unintentional snapping and overlays. This commit also includes some refactoring to simplify the logic. --- docs/labwc-config.5.scd | 5 ++ docs/rc.xml.all | 3 + include/config/rcxml.h | 1 + include/labwc.h | 30 ++-------- include/resistance.h | 7 ++- src/config/rcxml.c | 3 + src/input/cursor.c | 40 ++++++++----- src/interactive.c | 123 +++++++++++++++++----------------------- src/regions.c | 3 +- src/resistance.c | 32 ++++++++++- src/xdg.c | 10 +--- 11 files changed, 137 insertions(+), 120 deletions(-) diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd index 7436fc5f..e2c84116 100644 --- a/docs/labwc-config.5.scd +++ b/docs/labwc-config.5.scd @@ -330,6 +330,11 @@ this is for compatibility with Openbox. Sets the movement of cursor in pixel required for a tiled or maximized window to be moved with an interactive move. Default is 20. +** + Sets the one-dimentional movement of cursor in pixel required for a + *vertically or horizontally* maximized window to be moved with an + interactive move. Default is 150. + ## FOCUS ** [yes|no] diff --git a/docs/rc.xml.all b/docs/rc.xml.all index f7bba998..cb95ac71 100644 --- a/docs/rc.xml.all +++ b/docs/rc.xml.all @@ -114,7 +114,10 @@ 20 20 + 20 + + 150 diff --git a/include/config/rcxml.h b/include/config/rcxml.h index cb56d473..80ce4df7 100644 --- a/include/config/rcxml.h +++ b/include/config/rcxml.h @@ -135,6 +135,7 @@ struct rcxml { int screen_edge_strength; int window_edge_strength; int unsnap_threshold; + int unmaximize_threshold; /* window snapping */ int snap_edge_range; diff --git a/include/labwc.h b/include/labwc.h index 810fed13..93cb4038 100644 --- a/include/labwc.h +++ b/include/labwc.h @@ -263,13 +263,6 @@ struct server { /* cursor interactive */ enum input_mode input_mode; struct view *grabbed_view; - /* - * When an interactive move is requested for tiled/maximized views by CSD - * clients or by Drag actions, the actual motion and untiling of the view - * can be delayed to prevent the view from being unintentionally untiled. - * During this delay, move_pending is set. - */ - bool move_pending; /* Cursor position when interactive move/resize is requested */ double grab_x, grab_y; /* View geometry when interactive move/resize is requested */ @@ -506,26 +499,15 @@ void seat_reset_pressed(struct seat *seat); void seat_output_layout_changed(struct seat *seat); /** - * interactive_anchor_to_cursor() - repositions the view to remain + * interactive_anchor_to_cursor() - repositions the geometry to remain * underneath the cursor when its size changes during interactive move. + * This function also resizes server->grab_box and repositions it to remain + * underneath server->grab_{x,y}. * - * geometry->{width,height} are provided by the caller. - * geometry->{x,y} are computed by this function. - * - * @note When is non-zero, cursor_x/y should be the original - * cursor position when the button was pressed. + * geo->{width,height} are provided by the caller. + * geo->{x,y} are computed by this function. */ -void interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry, - int cursor_x, int cursor_y); - -/** - * interactive_move_tiled_view_to() - Un-tile the tiled/maximized view at the - * start of an interactive move or when an interactive move is pending. - * Returns true if the distance of cursor motion exceeds the value of - * and the view is un-tiled. - */ -bool interactive_move_tiled_view_to(struct server *server, struct view *view, - struct wlr_box *geometry); +void interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo); void interactive_begin(struct view *view, enum input_mode mode, uint32_t edges); void interactive_finish(struct view *view); diff --git a/include/resistance.h b/include/resistance.h index 65b14869..6a155ceb 100644 --- a/include/resistance.h +++ b/include/resistance.h @@ -3,7 +3,12 @@ #define LABWC_RESISTANCE_H #include "labwc.h" -void resistance_move_apply(struct view *view, double *x, double *y); +/** + * resistance_unsnap_apply() - Apply resistance when dragging a + * maximized/tiled window. Returns true when the view needs to be un-tiled. + */ +bool resistance_unsnap_apply(struct view *view, int *x, int *y); +void resistance_move_apply(struct view *view, int *x, int *y); void resistance_resize_apply(struct view *view, struct wlr_box *new_view_geo); #endif /* LABWC_RESISTANCE_H */ diff --git a/src/config/rcxml.c b/src/config/rcxml.c index d6ce92b6..007ae160 100644 --- a/src/config/rcxml.c +++ b/src/config/rcxml.c @@ -1043,6 +1043,8 @@ entry(xmlNode *node, char *nodename, char *content) rc.window_edge_strength = atoi(content); } else if (!strcasecmp(nodename, "unSnapThreshold.resistance")) { rc.unsnap_threshold = atoi(content); + } else if (!strcasecmp(nodename, "unMaximizeThreshold.resistance")) { + rc.unmaximize_threshold = atoi(content); } else if (!strcasecmp(nodename, "range.snapping")) { rc.snap_edge_range = atoi(content); } else if (!strcasecmp(nodename, "enabled.overlay.snapping")) { @@ -1370,6 +1372,7 @@ rcxml_init(void) rc.screen_edge_strength = 20; rc.window_edge_strength = 20; rc.unsnap_threshold = 20; + rc.unmaximize_threshold = 150; rc.snap_edge_range = 1; rc.snap_overlay_enabled = true; diff --git a/src/input/cursor.c b/src/input/cursor.c index cd28cf44..4b76d75a 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -235,24 +235,34 @@ process_cursor_move(struct server *server, uint32_t time) { struct view *view = server->grabbed_view; - /* - * Un-tile the view when interactive move is delayed and the distance - * of cursor movement exceeds . - */ - if (server->move_pending && !interactive_move_tiled_view_to( - server, server->grabbed_view, &server->grab_box)) { - return; + int x = server->grab_box.x + (server->seat.cursor->x - server->grab_x); + int y = server->grab_box.y + (server->seat.cursor->y - server->grab_y); + + /* Apply resistance for maximized/tiled view */ + bool needs_untile = resistance_unsnap_apply(view, &x, &y); + if (needs_untile) { + /* + * When the view needs to be un-tiled, resize it to natural + * geometry while anchoring it to cursor. If the natural + * geometry is unknown (possible with xdg-shell views), then + * we set a size of 0x0 here and determine the correct geometry + * later. See do_late_positioning() in xdg.c. + */ + struct wlr_box new_geo = { + .width = view->natural_geometry.width, + .height = view->natural_geometry.height, + }; + interactive_anchor_to_cursor(server, &new_geo); + view_set_untiled(view); + view_restore_to(view, new_geo); + x = new_geo.x; + y = new_geo.y; } - double dx = server->seat.cursor->x - server->grab_x; - double dy = server->seat.cursor->y - server->grab_y; - - /* Move the grabbed view to the new position. */ - dx += server->grab_box.x; - dy += server->grab_box.y; - resistance_move_apply(view, &dx, &dy); - view_move(view, dx, dy); + /* Then apply window & edge resistance */ + resistance_move_apply(view, &x, &y); + view_move(view, x, y); overlay_update(&server->seat); } diff --git a/src/interactive.c b/src/interactive.c index 570d80fc..851e2231 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -9,58 +9,47 @@ #include "view.h" #include "window-rules.h" +/* + * pos_old pos_cursor + * v v + * +---------+-------------------+ + * <-----------size_old----------> + * + * return value + * v + * +----+---------+ + * <---size_new---> + */ static int -max_move_scale(double pos_cursor, double pos_current, - double size_current, double size_orig) +max_move_scale(double pos_cursor, double pos_old, double size_old, + double size_new) { - double anchor_frac = (pos_cursor - pos_current) / size_current; - int pos_new = pos_cursor - (size_orig * anchor_frac); - if (pos_new < pos_current) { + double anchor_frac = (pos_cursor - pos_old) / size_old; + int pos_new = pos_cursor - (size_new * anchor_frac); + if (pos_new < pos_old) { /* Clamp by using the old offsets of the maximized window */ - pos_new = pos_current; + pos_new = pos_old; } return pos_new; } void -interactive_anchor_to_cursor(struct view *view, struct wlr_box *geometry, - int cursor_x, int cursor_y) +interactive_anchor_to_cursor(struct server *server, struct wlr_box *geo) { - geometry->x = max_move_scale(cursor_x, view->current.x, - view->current.width, geometry->width); - geometry->y = max_move_scale(cursor_y, view->current.y, - view->current.height, geometry->height); -} - -bool -interactive_move_tiled_view_to(struct server *server, struct view *view, - struct wlr_box *geometry) -{ - assert(!view_is_floating(view)); - - int threshold = rc.unsnap_threshold; - - if (server->input_mode == LAB_INPUT_STATE_MOVE) { - /* When called from cursor motion handler */ - assert(server->move_pending && server->grabbed_view == view); - - double dx = server->seat.cursor->x - server->grab_x; - double dy = server->seat.cursor->y - server->grab_y; - if (dx * dx + dy * dy < threshold * threshold) { - return false; - } - } else { - /* When called from interactive_begin() */ - if (threshold > 0) { - return false; - } + assert(server->input_mode == LAB_INPUT_STATE_MOVE); + if (wlr_box_empty(geo)) { + return; } + /* Resize grab_box while anchoring it to grab_box.{x,y} */ + server->grab_box.x = max_move_scale(server->grab_x, server->grab_box.x, + server->grab_box.width, geo->width); + server->grab_box.y = max_move_scale(server->grab_y, server->grab_box.y, + server->grab_box.height, geo->height); + server->grab_box.width = geo->width; + server->grab_box.height = geo->height; - view_set_untiled(view); - view_restore_to(view, *geometry); - server->move_pending = false; - - return true; + geo->x = server->grab_box.x + (server->seat.cursor->x - server->grab_x); + geo->y = server->grab_box.y + (server->seat.cursor->y - server->grab_y); } void @@ -73,7 +62,6 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) */ struct server *server = view->server; struct seat *seat = &server->seat; - struct wlr_box geometry = view->current; if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH) { return; @@ -97,31 +85,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) */ return; } - if (!view_is_floating(view)) { - /* - * Un-maximize and restore natural width/height. - * If the natural geometry is unknown (possible - * with xdg-shell views), then we set a size of - * 0x0 here and determine the correct geometry - * later. See do_late_positioning() in xdg.c. - */ - geometry.width = view->natural_geometry.width; - geometry.height = view->natural_geometry.height; - if (!wlr_box_empty(&geometry)) { - interactive_anchor_to_cursor(view, &geometry, - seat->cursor->x, seat->cursor->y); - } - - /* - * If is non-zero, the - * tiled/maximized view is un-tiled later in cursor - * motion handler. - */ - if (!interactive_move_tiled_view_to( - server, view, &geometry)) { - server->move_pending = true; - } - } else { + if (view_is_floating(view)) { /* Store natural geometry at start of move */ view_store_natural_geometry(view); view_invalidate_last_layout_geometry(view); @@ -151,7 +115,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) /* * If tiled or maximized in only one direction, reset - * tiled/maximized state but keep the same geometry as + * maximized/tiled state but keep the same geometry as * the starting point for the resize. */ view_set_untiled(view); @@ -168,8 +132,24 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) /* Remember view and cursor positions at start of move/resize */ server->grab_x = seat->cursor->x; server->grab_y = seat->cursor->y; - server->grab_box = geometry; + server->grab_box = view->current; server->resize_edges = edges; + + /* + * Un-tile maximized/tiled view immediately if is + * zero. Otherwise, un-tile it later in cursor motion handler. + * If the natural geometry is unknown (possible with xdg-shell views), + * then we set a size of 0x0 here and determine the correct geometry + * later. See do_late_positioning() in xdg.c. + */ + if (mode == LAB_INPUT_STATE_MOVE && !view_is_floating(view) + && rc.unsnap_threshold <= 0) { + struct wlr_box natural_geo = view->natural_geometry; + interactive_anchor_to_cursor(server, &natural_geo); + view_set_untiled(view); + view_restore_to(view, natural_geo); + } + if (rc.resize_indicator) { resize_indicator_show(view); } @@ -181,6 +161,10 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) enum view_edge edge_from_cursor(struct seat *seat, struct output **dest_output) { + if (!view_is_floating(seat->server->grabbed_view)) { + return VIEW_EDGE_INVALID; + } + int snap_range = rc.snap_edge_range; if (!snap_range) { return VIEW_EDGE_INVALID; @@ -296,7 +280,6 @@ interactive_cancel(struct view *view) view->server->input_mode = LAB_INPUT_STATE_PASSTHROUGH; view->server->grabbed_view = NULL; - view->server->move_pending = false; /* Update focus/cursor image */ cursor_update_focus(view->server); diff --git a/src/regions.c b/src/regions.c index 1b265fe3..f52a5619 100644 --- a/src/regions.c +++ b/src/regions.c @@ -20,7 +20,8 @@ regions_should_snap(struct server *server) { if (server->input_mode != LAB_INPUT_STATE_MOVE || wl_list_empty(&rc.regions) - || server->seat.region_prevent_snap) { + || server->seat.region_prevent_snap + || !view_is_floating(server->grabbed_view)) { return false; } diff --git a/src/resistance.c b/src/resistance.c index ce2efbdd..a20f3b3c 100644 --- a/src/resistance.c +++ b/src/resistance.c @@ -91,8 +91,38 @@ check_edge_window(int *next, struct edge current, struct edge target, oppose, align, rc.window_edge_strength, lesser); } +bool +resistance_unsnap_apply(struct view *view, int *x, int *y) +{ + if (view_is_floating(view)) { + return false; + } + + int dx = *x - view->current.x; + int dy = *y - view->current.y; + if (view->maximized == VIEW_AXIS_HORIZONTAL) { + if (abs(dx) < rc.unmaximize_threshold) { + *x = view->current.x; + return false; + } + } else if (view->maximized == VIEW_AXIS_VERTICAL) { + if (abs(dy) < rc.unmaximize_threshold) { + *y = view->current.y; + return false; + } + } else { + if (dx * dx + dy * dy < rc.unsnap_threshold * rc.unsnap_threshold) { + *x = view->current.x; + *y = view->current.y; + return false; + } + } + + return true; +} + void -resistance_move_apply(struct view *view, double *x, double *y) +resistance_move_apply(struct view *view, int *x, int *y) { assert(view); diff --git a/src/xdg.c b/src/xdg.c index 23bceba2..b73ff84d 100644 --- a/src/xdg.c +++ b/src/xdg.c @@ -93,14 +93,8 @@ do_late_positioning(struct view *view) struct server *server = view->server; if (server->input_mode == LAB_INPUT_STATE_MOVE && view == server->grabbed_view) { - /* Anchor view to original grab position */ - interactive_anchor_to_cursor(view, &view->pending, - server->grab_x, server->grab_y); - /* Next update grab offsets */ - server->grab_box = view->pending; - /* Finally, move by same distance cursor has moved */ - view->pending.x += server->seat.cursor->x - server->grab_x; - view->pending.y += server->seat.cursor->y - server->grab_y; + /* Reposition the view while anchoring it to cursor */ + interactive_anchor_to_cursor(server, &view->pending); } else { /* TODO: smart placement? */ view_compute_centered_position(view, NULL, From b34f2c9d85a52bc19a9458b6d38bae64fae55baa Mon Sep 17 00:00:00 2001 From: "Andrew J. Hesford" Date: Tue, 27 Aug 2024 10:10:53 -0400 Subject: [PATCH 141/232] Revert "src/interactive.c: don't unshade when view is un-tiled by dragging" When labwc un-tiles views, it generally changes their size, which sends a configure request to the client. However, because the view has been disabled in the wlroots scene, it will not receive and process the configure when labwc expects. Instead, the handling will be deferred until the user unshades the view at some arbitrary time in the future, resulting in labwc registering complains like [../src/xdg.c:239] client did not respond to configure request in 100 ms Furthermore, the reconfigure will still generally produce flicker (as the view opens in its tiled size and then jumps to its natural geometry). Because skipping the unshade might cause client problems and doesn't eliminate the problem it sought to resolve, let's revert this. This reverts commit 2e19bd4d5b3c67c27d13be24d41a0ea9ba2f1626. --- src/input/cursor.c | 2 ++ src/interactive.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/input/cursor.c b/src/input/cursor.c index 4b76d75a..6598c7e7 100644 --- a/src/input/cursor.c +++ b/src/input/cursor.c @@ -253,6 +253,8 @@ process_cursor_move(struct server *server, uint32_t time) .height = view->natural_geometry.height, }; interactive_anchor_to_cursor(server, &new_geo); + /* Shaded clients will not process resize events until unshaded */ + view_set_shade(view, false); view_set_untiled(view); view_restore_to(view, new_geo); x = new_geo.x; diff --git a/src/interactive.c b/src/interactive.c index 851e2231..91081fd3 100644 --- a/src/interactive.c +++ b/src/interactive.c @@ -85,6 +85,7 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) */ return; } + if (view_is_floating(view)) { /* Store natural geometry at start of move */ view_store_natural_geometry(view); @@ -146,6 +147,8 @@ interactive_begin(struct view *view, enum input_mode mode, uint32_t edges) && rc.unsnap_threshold <= 0) { struct wlr_box natural_geo = view->natural_geometry; interactive_anchor_to_cursor(server, &natural_geo); + /* Shaded clients will not process resize events until unshaded */ + view_set_shade(view, false); view_set_untiled(view); view_restore_to(view, natural_geo); } From 1be69dc28b000854b4781c62203ef8ef67e25be8 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sun, 25 Aug 2024 16:33:41 +0900 Subject: [PATCH 142/232] ssd: allow ssd to be smaller than minimal size by hiding buttons This fixes the ugly look of SSD for tiny windows (e.g. "xterm -geometry 1x1") due to the early return in `ssd_update_geometry()`. Now SSDs are rendered correctly for those windows by hiding some buttons when the window width is smaller than the total width of buttons. Additionally for windows smaller than (button width)*2, the corners are un-rounded so a small titlebar can be rendered with a scene-rect. --- include/ssd-internal.h | 17 ++++-- src/ssd/ssd-border.c | 8 +-- src/ssd/ssd-extents.c | 132 ++++++++++++++++++----------------------- src/ssd/ssd-part.c | 4 -- src/ssd/ssd-titlebar.c | 119 ++++++++++++++++++++++++++++++++----- src/ssd/ssd.c | 24 ++------ 6 files changed, 184 insertions(+), 120 deletions(-) diff --git a/include/ssd-internal.h b/include/ssd-internal.h index 3d973355..3c0fe38b 100644 --- a/include/ssd-internal.h +++ b/include/ssd-internal.h @@ -53,12 +53,19 @@ struct ssd { bool was_omnipresent; /* - * Corners need to be (un)rounded when toggling tiling or - * maximization, and the button needs to be swapped on + * Corners need to be (un)rounded and borders need be shown/hidden + * when toggling maximization, and the button needs to be swapped on * maximization toggles. */ bool was_maximized; - bool was_tiled_not_maximized; + + /* + * Corners need to be (un)rounded but borders should be kept shown when + * the window is (un)tiled and notified about it or when the window may + * become so small that only a squared scene-rect can be used to render + * such a small titlebar. + */ + bool was_squared; struct wlr_box geometry; struct ssd_state_title { @@ -109,9 +116,6 @@ struct ssd_part { /* This part represented in scene graph */ struct wlr_scene_node *node; - /* Targeted geometry. May be NULL */ - struct wlr_box *geometry; - struct wl_list link; }; @@ -151,6 +155,7 @@ void ssd_destroy_parts(struct wl_list *list); void ssd_titlebar_create(struct ssd *ssd); void ssd_titlebar_update(struct ssd *ssd); void ssd_titlebar_destroy(struct ssd *ssd); +bool ssd_should_be_squared(struct ssd *ssd); void ssd_border_create(struct ssd *ssd); void ssd_border_update(struct ssd *ssd); diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index fb1ac283..3b7b26cb 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -113,16 +113,16 @@ ssd_border_update(struct ssd *ssd) * |_______________| */ - int side_height = ssd->state.was_tiled_not_maximized + int side_height = ssd->state.was_squared ? height + ssd->titlebar.height : height; - int side_y = ssd->state.was_tiled_not_maximized + int side_y = ssd->state.was_squared ? -ssd->titlebar.height : 0; - int top_width = ssd->titlebar.height <= 0 || ssd->state.was_tiled_not_maximized + int top_width = ssd->titlebar.height <= 0 || ssd->state.was_squared ? full_width : width - 2 * theme->window_button_width; - int top_x = ssd->titlebar.height <= 0 || ssd->state.was_tiled_not_maximized + int top_x = ssd->titlebar.height <= 0 || ssd->state.was_squared ? 0 : theme->border_width + theme->window_button_width; diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index 274b5f50..b91cf94f 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only +#include #include #include "common/mem.h" #include "common/scene-helpers.h" @@ -14,14 +15,7 @@ add_extent(struct wl_list *part_list, enum ssd_part_type type, { float invisible[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; struct ssd_part *part = add_scene_part(part_list, type); - /* - * Extents need additional geometry to enable dynamic - * resize based on position and output->usable_area. - * - * part->geometry will get free'd automatically in ssd_destroy_parts(). - */ part->node = &wlr_scene_rect_create(parent, 0, 0, invisible)->node; - part->geometry = znew(struct wlr_box); return part; } @@ -32,8 +26,6 @@ ssd_extents_create(struct ssd *ssd) struct theme *theme = view->server->theme; struct wl_list *part_list = &ssd->extents.parts; int extended_area = SSD_EXTENDED_AREA; - int corner_size = extended_area + theme->border_width + - theme->window_button_width / 2; ssd->extents.tree = wlr_scene_tree_create(ssd->tree); struct wlr_scene_tree *parent = ssd->extents.tree; @@ -45,43 +37,17 @@ ssd_extents_create(struct ssd *ssd) -(theme->border_width + extended_area), -(ssd->titlebar.height + theme->border_width + extended_area)); - /* Initialize parts and set constant values for targeted geometry */ - struct ssd_part *p; - /* Top */ - p = add_extent(part_list, LAB_SSD_PART_CORNER_TOP_LEFT, parent); - p->geometry->width = corner_size; - p->geometry->height = corner_size; - - p = add_extent(part_list, LAB_SSD_PART_TOP, parent); - p->geometry->x = corner_size; - p->geometry->height = extended_area; - - p = add_extent(part_list, LAB_SSD_PART_CORNER_TOP_RIGHT, parent); - p->geometry->width = corner_size; - p->geometry->height = corner_size; - + add_extent(part_list, LAB_SSD_PART_CORNER_TOP_LEFT, parent); + add_extent(part_list, LAB_SSD_PART_TOP, parent); + add_extent(part_list, LAB_SSD_PART_CORNER_TOP_RIGHT, parent); /* Sides */ - p = add_extent(part_list, LAB_SSD_PART_LEFT, parent); - p->geometry->y = corner_size; - p->geometry->width = extended_area; - - p = add_extent(part_list, LAB_SSD_PART_RIGHT, parent); - p->geometry->y = corner_size; - p->geometry->width = extended_area; - + add_extent(part_list, LAB_SSD_PART_LEFT, parent); + add_extent(part_list, LAB_SSD_PART_RIGHT, parent); /* Bottom */ - p = add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_LEFT, parent); - p->geometry->width = corner_size; - p->geometry->height = corner_size; - - p = add_extent(part_list, LAB_SSD_PART_BOTTOM, parent); - p->geometry->x = corner_size; - p->geometry->height = extended_area; - - p = add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, parent); - p->geometry->width = corner_size; - p->geometry->height = corner_size; + add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_LEFT, parent); + add_extent(part_list, LAB_SSD_PART_BOTTOM, parent); + add_extent(part_list, LAB_SSD_PART_CORNER_BOTTOM_RIGHT, parent); /* Initial manual update to keep X11 applications happy */ ssd_extents_update(ssd); @@ -111,7 +77,7 @@ ssd_extents_update(struct ssd *ssd) int full_width = width + 2 * theme->border_width; int extended_area = SSD_EXTENDED_AREA; int corner_size = extended_area + theme->border_width + - theme->window_button_width / 2; + MIN(theme->window_button_width, width) / 2; int side_width = full_width + extended_area * 2 - corner_size * 2; int side_height = full_height + extended_area * 2 - corner_size * 2; @@ -119,6 +85,7 @@ ssd_extents_update(struct ssd *ssd) struct wlr_box result_box; struct ssd_part *part; struct wlr_scene_rect *rect; + struct wlr_box target; /* Make sure we update the y offset based on titlebar shown / hidden */ wlr_scene_node_set_position(&ssd->extents.tree->node, @@ -149,44 +116,69 @@ ssd_extents_update(struct ssd *ssd) int base_x, base_y; wlr_scene_node_coords(&ssd->extents.tree->node, &base_x, &base_y); - struct wlr_box *target; wl_list_for_each(part, &ssd->extents.parts, link) { rect = wlr_scene_rect_from_node(part->node); - target = part->geometry; switch (part->type) { + case LAB_SSD_PART_CORNER_TOP_LEFT: + target.x = 0; + target.y = 0; + target.width = corner_size; + target.height = corner_size; + break; case LAB_SSD_PART_TOP: - target->width = side_width; + target.x = corner_size; + target.y = 0; + target.width = side_width; + target.height = extended_area; break; case LAB_SSD_PART_CORNER_TOP_RIGHT: - target->x = corner_size + side_width; + target.x = corner_size + side_width; + target.y = 0; + target.width = corner_size; + target.height = corner_size; break; case LAB_SSD_PART_LEFT: - target->height = side_height; + target.x = 0; + target.y = corner_size; + target.width = extended_area; + target.height = side_height; break; case LAB_SSD_PART_RIGHT: - target->x = extended_area + full_width; - target->height = side_height; + target.x = extended_area + full_width; + target.y = corner_size; + target.width = extended_area; + target.height = side_height; break; case LAB_SSD_PART_CORNER_BOTTOM_LEFT: - target->y = corner_size + side_height; + target.x = 0; + target.y = corner_size + side_height; + target.width = corner_size; + target.height = corner_size; break; case LAB_SSD_PART_BOTTOM: - target->width = side_width; - target->y = extended_area + full_height; + target.x = corner_size; + target.y = extended_area + full_height; + target.width = side_width; + target.height = extended_area; break; case LAB_SSD_PART_CORNER_BOTTOM_RIGHT: - target->x = corner_size + side_width; - target->y = corner_size + side_height; + target.x = corner_size + side_width; + target.y = corner_size + side_height; + target.width = corner_size; + target.height = corner_size; break; default: - break; + /* not reached */ + assert(false); + /* suppress warnings with NDEBUG */ + target = (struct wlr_box){0}; } /* Get layout geometry of what the part *should* be */ - part_box.x = base_x + target->x; - part_box.y = base_y + target->y; - part_box.width = target->width; - part_box.height = target->height; + part_box.x = base_x + target.x; + part_box.y = base_y + target.y; + part_box.width = target.width; + part_box.height = target.height; /* Constrain part to output->usable_area */ pixman_region32_clear(&intersection); @@ -232,18 +224,12 @@ ssd_extents_update(struct ssd *ssd) wlr_scene_rect_set_size(rect, result_box.width, result_box.height); wlr_scene_node_set_position(part->node, - target->x + (result_box.x - part_box.x), - target->y + (result_box.y - part_box.y)); - continue; - } - - /* Fully visible */ - if (target->x != part->node->x - || target->y != part->node->y) { - wlr_scene_node_set_position(part->node, target->x, target->y); - } - if (target->width != rect->width || target->height != rect->height) { - wlr_scene_rect_set_size(rect, target->width, target->height); + target.x + (result_box.x - part_box.x), + target.y + (result_box.y - part_box.y)); + } else { + /* Fully visible */ + wlr_scene_node_set_position(part->node, target.x, target.y); + wlr_scene_rect_set_size(rect, target.width, target.height); } } pixman_region32_fini(&intersection); diff --git a/src/ssd/ssd-part.c b/src/ssd/ssd-part.c index 9c3294e2..4a5eda72 100644 --- a/src/ssd/ssd-part.c +++ b/src/ssd/ssd-part.c @@ -213,10 +213,6 @@ ssd_destroy_parts(struct wl_list *list) } /* part->buffer will free itself along the scene_buffer node */ part->buffer = NULL; - if (part->geometry) { - free(part->geometry); - part->geometry = NULL; - } wl_list_remove(&part->link); free(part); } diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 407753bb..71864cb0 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -20,6 +20,7 @@ static void set_squared_corners(struct ssd *ssd, bool enable); static void set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable); +static void update_visible_buttons(struct ssd *ssd); static void add_button(struct ssd *ssd, struct ssd_sub_tree *subtree, enum ssd_part_type type, int x) @@ -166,6 +167,8 @@ ssd_titlebar_create(struct ssd *ssd) } } FOR_EACH_END + update_visible_buttons(ssd); + ssd_update_title(ssd); bool maximized = view->maximized == VIEW_AXIS_BOTH; @@ -183,9 +186,9 @@ ssd_titlebar_create(struct ssd *ssd) set_alt_button_icon(ssd, LAB_SSD_BUTTON_OMNIPRESENT, true); } - if (view_is_tiled_and_notify_tiled(view) && !maximized) { + if (ssd_should_be_squared(ssd)) { set_squared_corners(ssd, true); - ssd->state.was_tiled_not_maximized = true; + ssd->state.was_squared = true; } } @@ -241,6 +244,56 @@ set_alt_button_icon(struct ssd *ssd, enum ssd_part_type type, bool enable) } FOR_EACH_END } +/* + * Usually this function just enables all the nodes for buttons, but some + * buttons can be hidden for small windows (e.g. xterm -geometry 1x1). + */ +static void +update_visible_buttons(struct ssd *ssd) +{ + struct view *view = ssd->view; + int width = view->current.width; + int button_width = view->server->theme->window_button_width; + int button_count_left = wl_list_length(&rc.title_buttons_left); + int button_count_right = wl_list_length(&rc.title_buttons_right); + + /* Make sure infinite loop never occurs */ + assert(button_width > 0); + /* + * The corner-left button is lastly removed as it's usually a window + * menu button (or an app icon button in the future). + */ + while (width < button_width * (button_count_left + button_count_right)) { + if (button_count_left > button_count_right) { + button_count_left--; + } else { + button_count_right--; + } + } + + int button_count; + struct ssd_part *part; + struct ssd_sub_tree *subtree; + struct title_button *b; + FOR_EACH_STATE(ssd, subtree) { + button_count = 0; + wl_list_for_each(b, &rc.title_buttons_left, link) { + part = ssd_get_part(&subtree->parts, b->type); + wlr_scene_node_set_enabled(part->node, + button_count < button_count_left); + button_count++; + } + + button_count = 0; + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + part = ssd_get_part(&subtree->parts, b->type); + wlr_scene_node_set_enabled(part->node, + button_count < button_count_right); + button_count++; + } + } FOR_EACH_END +} + void ssd_titlebar_update(struct ssd *ssd) { @@ -249,17 +302,16 @@ ssd_titlebar_update(struct ssd *ssd) struct theme *theme = view->server->theme; bool maximized = view->maximized == VIEW_AXIS_BOTH; - bool tiled_not_maximized = - view_is_tiled_and_notify_tiled(ssd->view) && !maximized; + bool squared = ssd_should_be_squared(ssd); if (ssd->state.was_maximized != maximized - || ssd->state.was_tiled_not_maximized != tiled_not_maximized) { - set_squared_corners(ssd, maximized || tiled_not_maximized); + || ssd->state.was_squared != squared) { + set_squared_corners(ssd, maximized || squared); if (ssd->state.was_maximized != maximized) { set_alt_button_icon(ssd, LAB_SSD_BUTTON_MAXIMIZE, maximized); } ssd->state.was_maximized = maximized; - ssd->state.was_tiled_not_maximized = tiled_not_maximized; + ssd->state.was_squared = squared; } if (ssd->state.was_shaded != view->shaded) { @@ -277,11 +329,13 @@ ssd_titlebar_update(struct ssd *ssd) return; } + update_visible_buttons(ssd); + int x; struct ssd_part *part; struct ssd_sub_tree *subtree; struct title_button *b; - int bg_offset = maximized || tiled_not_maximized ? 0 : theme->window_button_width; + int bg_offset = maximized || squared ? 0 : theme->window_button_width; FOR_EACH_STATE(ssd, subtree) { part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); wlr_scene_rect_set_size( @@ -343,13 +397,11 @@ ssd_titlebar_destroy(struct ssd *ssd) */ static void -ssd_update_title_positions(struct ssd *ssd) +ssd_update_title_positions(struct ssd *ssd, int offset_left, int offset_right) { struct view *view = ssd->view; struct theme *theme = view->server->theme; int width = view->current.width; - int offset_left = theme->window_button_width * wl_list_length(&rc.title_buttons_left); - int offset_right = theme->window_button_width * wl_list_length(&rc.title_buttons_right); int title_bg_width = width - offset_left - offset_right; int x, y; @@ -396,6 +448,33 @@ ssd_update_title_positions(struct ssd *ssd) } FOR_EACH_END } +/* + * Get left/right offsets of the title area based on visible/hidden states of + * buttons set in update_visible_buttons(). + */ +static void +get_title_offsets(struct ssd *ssd, int *offset_left, int *offset_right) +{ + struct ssd_sub_tree *subtree = &ssd->titlebar.active; + int button_width = ssd->view->server->theme->window_button_width; + *offset_left = 0; + *offset_right = 0; + + struct title_button *b; + wl_list_for_each(b, &rc.title_buttons_left, link) { + struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); + if (part->node->enabled) { + *offset_left += button_width; + } + } + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); + if (part->node->enabled) { + *offset_right += button_width; + } + } +} + void ssd_update_title(struct ssd *ssd) { @@ -419,8 +498,9 @@ ssd_update_title(struct ssd *ssd) struct ssd_part *part; struct ssd_sub_tree *subtree; struct ssd_state_title_width *dstate; - int offset_left = theme->window_button_width * wl_list_length(&rc.title_buttons_left); - int offset_right = theme->window_button_width * wl_list_length(&rc.title_buttons_right); + + int offset_left, offset_right; + get_title_offsets(ssd, &offset_left, &offset_right); int title_bg_width = view->current.width - offset_left - offset_right; FOR_EACH_STATE(ssd, subtree) { @@ -477,7 +557,7 @@ ssd_update_title(struct ssd *ssd) } state->text = xstrdup(title); } - ssd_update_title_positions(ssd); + ssd_update_title_positions(ssd, offset_left, offset_right); } static void @@ -519,4 +599,15 @@ disable_old_hover: } } +bool +ssd_should_be_squared(struct ssd *ssd) +{ + struct view *view = ssd->view; + int button_width = view->server->theme->window_button_width; + + return (view_is_tiled_and_notify_tiled(view) + || view->current.width < button_width * 2) + && view->maximized != VIEW_AXIS_BOTH; +} + #undef FOR_EACH_STATE diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 98d40d4f..0085fe3d 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -181,9 +181,9 @@ ssd_create(struct view *view, bool active) ssd_extents_create(ssd); /* * We need to create the borders after the titlebar because it sets - * ssd->state.was_tiled_not_maximized which ssd_border_create() - * reacts to. TODO: Set the state here instead so the order does - * not matter anymore. + * ssd->state.squared which ssd_border_create() reacts to. + * TODO: Set the state here instead so the order does not matter + * anymore. */ ssd_titlebar_create(ssd); ssd_border_create(ssd); @@ -227,33 +227,19 @@ ssd_update_geometry(struct ssd *ssd) struct wlr_box cached = ssd->state.geometry; struct wlr_box current = view->current; - int min_view_width = rc.theme->window_button_width * ( - wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); int eff_width = current.width; int eff_height = view_effective_height(view, /* use_pending */ false); - if (eff_width > 0 && eff_width < min_view_width) { - /* - * Prevent negative values in calculations like - * `width - theme->window_button_width - * * (wl_list_length(&rc.title_buttons_left) - * + wl_list_length(&rc.title_buttons_right))` - */ - wlr_log(WLR_ERROR, - "view width is smaller than its minimal value"); - return; - } - bool update_area = eff_width != cached.width || eff_height != cached.height; bool update_extents = update_area || current.x != cached.x || current.y != cached.y; bool maximized = view->maximized == VIEW_AXIS_BOTH; - bool tiled_not_maximized = view_is_tiled(view) && !maximized; + bool squared = ssd_should_be_squared(ssd); bool state_changed = ssd->state.was_maximized != maximized || ssd->state.was_shaded != view->shaded - || ssd->state.was_tiled_not_maximized != tiled_not_maximized + || ssd->state.was_squared != squared || ssd->state.was_omnipresent != view->visible_on_all_workspaces; if (update_extents) { From f3b27668924aca964eb212e39fd140ed06210a53 Mon Sep 17 00:00:00 2001 From: Weblate Date: Sat, 24 Aug 2024 22:01:18 +0200 Subject: [PATCH 143/232] Translation updates from weblate Co-authored-by: Weblate Co-authored-by: daniel Translate-URL: https://translate.lxqt-project.org/projects/labwc/labwc/pt_BR/ Translation: Labwc/labwc --- po/LINGUAS | 2 +- po/pt_BR.po | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 po/pt_BR.po diff --git a/po/LINGUAS b/po/LINGUAS index 699dfa24..8b56fb2a 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -1 +1 @@ -ar cs da de el es et eu fa fi fr gl hu id it ja ka ko lt nl pa pl pt ru sv tr uk zh_CN +ar cs da de el es et eu fa fi fr gl hu id it ja ka ko lt nl pa pl pt pt_BR ru sv tr uk zh_CN diff --git a/po/pt_BR.po b/po/pt_BR.po new file mode 100644 index 00000000..d53e84e6 --- /dev/null +++ b/po/pt_BR.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-03-17 11:06+1000\n" +"PO-Revision-Date: 2024-08-24 20:01+0000\n" +"Last-Translator: daniel \n" +"Language-Team: Portuguese (Brazil) \n" +"Language: pt_BR\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:829 +msgid "Reconfigure" +msgstr "Reconfigurar" + +#: src/menu/menu.c:831 +msgid "Exit" +msgstr "Sair" + +#: src/menu/menu.c:847 +msgid "Minimize" +msgstr "Minimizar" + +#: src/menu/menu.c:849 +msgid "Maximize" +msgstr "Maximizar" + +#: src/menu/menu.c:851 +msgid "Fullscreen" +msgstr "Tela Cheia" + +#: src/menu/menu.c:853 +msgid "Roll Up/Down" +msgstr "Rolar para Cima/Baixo" + +#: src/menu/menu.c:855 +msgid "Decorations" +msgstr "Decorações" + +#: src/menu/menu.c:857 +msgid "Always on Top" +msgstr "Sempre no Topo" + +#: src/menu/menu.c:862 +msgid "Move Left" +msgstr "Mover à Esquerda" + +#: src/menu/menu.c:869 +msgid "Move Right" +msgstr "Mover à Direita" + +#: src/menu/menu.c:874 +msgid "Always on Visible Workspace" +msgstr "Sempre Visível na Área de Trabalho" + +#: src/menu/menu.c:877 src/config/rcxml.c:1497 +msgid "Workspace" +msgstr "Área de Trabalho" + +#: src/menu/menu.c:880 +msgid "Close" +msgstr "Fechar" From 8850368ab943d49dbd312d34fccd10e336b68ec9 Mon Sep 17 00:00:00 2001 From: Droc Date: Thu, 5 Sep 2024 07:52:23 -0500 Subject: [PATCH 144/232] Add position arguments for menus ...utilizing x,y coordinates where values can be a number, a negative number, a percentage or "center". - (0,0) is top left corner - (-0,-0) is bottom right corner - % is percentage of width and/or height - 'center' centers the menu vertically and/or horizontally root-menu 0 0 Note: both x and y values must be supplied for positioning to work. --- docs/labwc-actions.5.scd | 11 +++++++ src/action.c | 69 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/docs/labwc-actions.5.scd b/docs/labwc-actions.5.scd index 6b9dfe8d..443268e8 100644 --- a/docs/labwc-actions.5.scd +++ b/docs/labwc-actions.5.scd @@ -123,6 +123,17 @@ Actions are used in menus and keyboard/mouse bindings. upper-left corner of the window associated with the action. Default is yes. + *position* Show the menu in the specified position on the monitor + that has cursor focus, see below. + + The position tag has two sub-tags. and specify a position and + take either a pixel value, the string "center" which will center the + menu in that dimension, or a relative value specified as a percentage + A relative value is interpreted in terms of the monitor the menu will + be shown on, and will be relative to the left/top edge of the menu + window and monitor for positive values, and to the right/bottom edge + for negative values. + ** Set decorations of focused window. diff --git a/src/action.c b/src/action.c index 79008566..a157d6e8 100644 --- a/src/action.c +++ b/src/action.c @@ -343,6 +343,14 @@ action_arg_from_xml_node(struct action *action, const char *nodename, const char action_arg_add_bool(action, argument, parse_bool(content, true)); goto cleanup; } + if (!strcasecmp(argument, "x.position")) { + action_arg_add_str(action, argument, content); + goto cleanup; + } + if (!strcasecmp(argument, "y.position")) { + action_arg_add_str(action, argument, content); + goto cleanup; + } break; case ACTION_TYPE_TOGGLE_MAXIMIZE: case ACTION_TYPE_MAXIMIZE: @@ -611,7 +619,8 @@ action_list_free(struct wl_list *action_list) static void show_menu(struct server *server, struct view *view, - const char *menu_name, bool at_cursor) + const char *menu_name, bool at_cursor, + const char *pos_x, const char *pos_y) { if (server->input_mode != LAB_INPUT_STATE_PASSTHROUGH && server->input_mode != LAB_INPUT_STATE_MENU) { @@ -638,8 +647,62 @@ show_menu(struct server *server, struct view *view, y = view->current.y; } + /* + * determine placement by looking at x and y + * x/y can be number, "center" or a %percent of screen dimensions + */ + if (pos_x && pos_y) { + struct output *output = output_nearest_to(server, + server->seat.cursor->x, server->seat.cursor->y); + struct menuitem *item; + struct theme *theme = server->theme; + int max_width = theme->menu_min_width; + + wl_list_for_each(item, &menu->menuitems, link) { + if (item->native_width > max_width) { + max_width = item->native_width < theme->menu_max_width + ? item->native_width : theme->menu_max_width; + } + } + + if (!strcasecmp(pos_x, "center")) { + x = (output->usable_area.width / 2) - (max_width / 2); + } else if (strchr(pos_x, '%')) { + x = (output->usable_area.width * atoi(pos_x)) / 100; + } else { + if (pos_x[0] == '-') { + int neg_x = strtol(pos_x, NULL, 10); + x = output->usable_area.width + neg_x; + } else { + x = atoi(pos_x); + } + } + + if (!strcasecmp(pos_y, "center")) { + y = (output->usable_area.height / 2) - (menu->size.height / 2); + } else if (strchr(pos_y, '%')) { + y = (output->usable_area.height * atoi(pos_y)) / 100; + } else { + if (pos_y[0] == '-') { + int neg_y = strtol(pos_y, NULL, 10); + y = output->usable_area.height + neg_y; + } else { + y = atoi(pos_y); + } + } + /* keep menu from being off screen */ + x = MAX(x, 0); + x = MIN(x, (output->usable_area.width -1)); + y = MAX(y, 0); + y = MIN(y, (output->usable_area.height -1)); + /* adjust for which monitor to appear on */ + x += output->usable_area.x; + y += output->usable_area.y; + } + /* Replaced by next show_menu() or cleaned on view_destroy() */ menu->triggered_by_view = view; + menu->server->menu_current = menu; menu_open_root(menu, x, y); } @@ -843,7 +906,9 @@ actions_run(struct view *activator, struct server *server, case ACTION_TYPE_SHOW_MENU: show_menu(server, view, action_get_str(action, "menu", NULL), - action_get_bool(action, "atCursor", true)); + action_get_bool(action, "atCursor", true), + action_get_str(action, "x.position", NULL), + action_get_str(action, "y.position", NULL)); break; case ACTION_TYPE_TOGGLE_MAXIMIZE: if (view) { From 824b0fa4e32a7f00ed58f094c6b6097bac6d9f6a Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Mon, 9 Sep 2024 17:43:38 +0200 Subject: [PATCH 145/232] theme: add button padding and spacing (#2127) While at it, separate corner width from button width. Both are independed and having them separately improves readability. --- docs/labwc-theme.5.scd | 16 ++++++++++--- docs/themerc | 4 +++- include/ssd.h | 1 + include/theme.h | 9 ++++++++ include/view.h | 1 + src/snap.c | 7 +++--- src/ssd/ssd-border.c | 10 ++++---- src/ssd/ssd-extents.c | 3 ++- src/ssd/ssd-titlebar.c | 52 +++++++++++++++++++++++++++--------------- src/ssd/ssd.c | 7 ++++++ src/theme.c | 12 +++++++++- src/view.c | 16 ++++++++++--- src/xwayland.c | 6 ++--- 13 files changed, 105 insertions(+), 39 deletions(-) diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index 42dd0407..57b7aef9 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -47,12 +47,18 @@ labwc-config(5). # THEME ELEMENTS *border.width* - Line width (integer) of border border drawn around window frames. + Line width (integer) of border drawn around window frames. Default is 1. +*padding.width* + Horizontal padding size, in pixels, between border and first + button on the left/right. + Default is 0. + *padding.height* - Vertical padding size, used for spacing out elements in the window - decorations. Default is 3. + Vertical padding size, in pixels, used for spacing out elements + in the window decorations. + Default is 3. *titlebar.height* Window title bar height. @@ -122,6 +128,10 @@ labwc-config(5). Width of a titlebar button, in pixels. Default is 26. +*window.button.spacing* + Space between titlebar buttons, in pixels. + Default is 0. + *window.active.button.unpressed.image.color* Color of the images in titlebar buttons in their default, unpressed, state. This element is for the focused window. diff --git a/docs/themerc b/docs/themerc index 66e1d09c..1e066074 100644 --- a/docs/themerc +++ b/docs/themerc @@ -7,6 +7,7 @@ # general border.width: 1 +padding.width: 0 padding.height: 3 # The following options has no default, but fallbacks back to @@ -29,8 +30,9 @@ window.active.label.text.color: #000000 window.inactive.label.text.color: #000000 window.label.text.justify: center -# window button width +# window button width and spacing window.button.width: 26 +window.button.spacing: 0 # window buttons window.active.button.unpressed.image.color: #000000 diff --git a/include/ssd.h b/include/ssd.h index 2afd0087..701f24eb 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -79,6 +79,7 @@ struct wlr_scene_node; */ struct ssd *ssd_create(struct view *view, bool active); struct border ssd_get_margin(const struct ssd *ssd); +int ssd_get_corner_width(void); void ssd_update_margin(struct ssd *ssd); void ssd_set_active(struct ssd *ssd, bool active); void ssd_update_title(struct ssd *ssd); diff --git a/include/theme.h b/include/theme.h index 1b15af6b..4705926e 100644 --- a/include/theme.h +++ b/include/theme.h @@ -27,7 +27,14 @@ struct theme_snapping_overlay { struct theme { int border_width; + + /* + * the space between title bar border and + * buttons on the left/right/top + */ + int padding_width; int padding_height; + int title_height; int menu_overlap_x; int menu_overlap_y; @@ -48,6 +55,8 @@ struct theme { /* button width */ int window_button_width; + /* the space between buttons */ + int window_button_spacing; /* button colors */ float window_active_button_menu_unpressed_image_color[4]; diff --git a/include/view.h b/include/view.h index 6f47f2a7..3464b4e9 100644 --- a/include/view.h +++ b/include/view.h @@ -538,6 +538,7 @@ const char *view_get_string_prop(struct view *view, const char *prop); void view_update_title(struct view *view); void view_update_app_id(struct view *view); void view_reload_ssd(struct view *view); +int view_get_min_width(void); void view_set_shade(struct view *view, bool shaded); diff --git a/src/snap.c b/src/snap.c index 3f188f6f..612d7a3f 100644 --- a/src/snap.c +++ b/src/snap.c @@ -220,8 +220,7 @@ snap_shrink_to_next_edge(struct view *view, *geo = view->pending; uint32_t resize_edges; - int min_view_width = rc.theme->window_button_width * ( - wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); + int min_width = view_get_min_width(); /* * First shrink the view along the relevant edge. The maximum shrink @@ -230,12 +229,12 @@ snap_shrink_to_next_edge(struct view *view, */ switch (direction) { case VIEW_EDGE_RIGHT: - geo->width = MAX(geo->width / 2, min_view_width); + geo->width = MAX(geo->width / 2, min_width); geo->x = view->pending.x + view->pending.width - geo->width; resize_edges = WLR_EDGE_LEFT; break; case VIEW_EDGE_LEFT: - geo->width = MAX(geo->width / 2, min_view_width); + geo->width = MAX(geo->width / 2, min_width); resize_edges = WLR_EDGE_RIGHT; break; case VIEW_EDGE_DOWN: diff --git a/src/ssd/ssd-border.c b/src/ssd/ssd-border.c index 3b7b26cb..aa7c4d5f 100644 --- a/src/ssd/ssd-border.c +++ b/src/ssd/ssd-border.c @@ -22,6 +22,7 @@ ssd_border_create(struct ssd *ssd) int width = view->current.width; int height = view_effective_height(view, /* use_pending */ false); int full_width = width + 2 * theme->border_width; + int corner_width = ssd_get_corner_width(); float *color; struct wlr_scene_tree *parent; @@ -48,8 +49,8 @@ ssd_border_create(struct ssd *ssd) add_scene_rect(&subtree->parts, LAB_SSD_PART_BOTTOM, parent, full_width, theme->border_width, 0, height, color); add_scene_rect(&subtree->parts, LAB_SSD_PART_TOP, parent, - width - 2 * theme->window_button_width, theme->border_width, - theme->border_width + theme->window_button_width, + width - 2 * corner_width, theme->border_width, + theme->border_width + corner_width, -(ssd->titlebar.height + theme->border_width), color); } FOR_EACH_END @@ -93,6 +94,7 @@ ssd_border_update(struct ssd *ssd) int width = view->current.width; int height = view_effective_height(view, /* use_pending */ false); int full_width = width + 2 * theme->border_width; + int corner_width = ssd_get_corner_width(); /* * From here on we have to cover the following border scenarios: @@ -121,10 +123,10 @@ ssd_border_update(struct ssd *ssd) : 0; int top_width = ssd->titlebar.height <= 0 || ssd->state.was_squared ? full_width - : width - 2 * theme->window_button_width; + : width - 2 * corner_width; int top_x = ssd->titlebar.height <= 0 || ssd->state.was_squared ? 0 - : theme->border_width + theme->window_button_width; + : theme->border_width + corner_width; struct ssd_part *part; struct wlr_scene_rect *rect; diff --git a/src/ssd/ssd-extents.c b/src/ssd/ssd-extents.c index b91cf94f..1a0ed351 100644 --- a/src/ssd/ssd-extents.c +++ b/src/ssd/ssd-extents.c @@ -76,8 +76,9 @@ ssd_extents_update(struct ssd *ssd) int full_height = height + theme->border_width * 2 + ssd->titlebar.height; int full_width = width + 2 * theme->border_width; int extended_area = SSD_EXTENDED_AREA; + int corner_width = ssd_get_corner_width(); int corner_size = extended_area + theme->border_width + - MIN(theme->window_button_width, width) / 2; + MIN(corner_width, width) / 2; int side_width = full_width + extended_area * 2 - corner_size * 2; int side_height = full_height + extended_area * 2 - corner_size * 2; diff --git a/src/ssd/ssd-titlebar.c b/src/ssd/ssd-titlebar.c index 71864cb0..1b2f56ac 100644 --- a/src/ssd/ssd-titlebar.c +++ b/src/ssd/ssd-titlebar.c @@ -116,6 +116,7 @@ ssd_titlebar_create(struct ssd *ssd) struct view *view = ssd->view; struct theme *theme = view->server->theme; int width = view->current.width; + int corner_width = ssd_get_corner_width(); float *color; struct wlr_scene_tree *parent; @@ -144,25 +145,25 @@ ssd_titlebar_create(struct ssd *ssd) /* Background */ add_scene_rect(&subtree->parts, LAB_SSD_PART_TITLEBAR, parent, - width - theme->window_button_width * 2, theme->title_height, - theme->window_button_width, 0, color); + width - corner_width * 2, theme->title_height, + corner_width, 0, color); add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_LEFT, parent, corner_top_left, -rc.theme->border_width, -rc.theme->border_width); add_scene_buffer(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT, parent, - corner_top_right, width - theme->window_button_width, + corner_top_right, width - corner_width, -rc.theme->border_width); /* Buttons */ struct title_button *b; - int x = 0; + int x = theme->padding_width; wl_list_for_each(b, &rc.title_buttons_left, link) { add_button(ssd, subtree, b->type, x); - x += theme->window_button_width; + x += theme->window_button_width + theme->window_button_spacing; } - x = width; + x = width - theme->padding_width + theme->window_button_spacing; wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { - x -= theme->window_button_width; + x -= theme->window_button_width + theme->window_button_spacing; add_button(ssd, subtree, b->type, x); } } FOR_EACH_END @@ -197,11 +198,12 @@ set_squared_corners(struct ssd *ssd, bool enable) { struct view *view = ssd->view; int width = view->current.width; + int corner_width = ssd_get_corner_width(); struct theme *theme = view->server->theme; struct ssd_part *part; struct ssd_sub_tree *subtree; - int x = enable ? 0 : theme->window_button_width; + int x = enable ? 0 : corner_width; FOR_EACH_STATE(ssd, subtree) { part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); @@ -252,18 +254,23 @@ static void update_visible_buttons(struct ssd *ssd) { struct view *view = ssd->view; - int width = view->current.width; + int width = view->current.width - (2 * view->server->theme->padding_width); int button_width = view->server->theme->window_button_width; + int button_spacing = view->server->theme->window_button_spacing; int button_count_left = wl_list_length(&rc.title_buttons_left); int button_count_right = wl_list_length(&rc.title_buttons_right); /* Make sure infinite loop never occurs */ assert(button_width > 0); + /* * The corner-left button is lastly removed as it's usually a window * menu button (or an app icon button in the future). */ - while (width < button_width * (button_count_left + button_count_right)) { + while (width < + ((button_width * (button_count_left + button_count_right)) + + (MAX((button_count_right - 1), 0) * button_spacing) + + (MAX((button_count_left - 1), 0) * button_spacing))) { if (button_count_left > button_count_right) { button_count_left--; } else { @@ -299,6 +306,7 @@ ssd_titlebar_update(struct ssd *ssd) { struct view *view = ssd->view; int width = view->current.width; + int corner_width = ssd_get_corner_width(); struct theme *theme = view->server->theme; bool maximized = view->maximized == VIEW_AXIS_BOTH; @@ -335,27 +343,29 @@ ssd_titlebar_update(struct ssd *ssd) struct ssd_part *part; struct ssd_sub_tree *subtree; struct title_button *b; - int bg_offset = maximized || squared ? 0 : theme->window_button_width; + int bg_offset = maximized || squared ? 0 : corner_width; FOR_EACH_STATE(ssd, subtree) { part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR); wlr_scene_rect_set_size( wlr_scene_rect_from_node(part->node), width - bg_offset * 2, theme->title_height); - x = 0; + x = theme->padding_width; wl_list_for_each(b, &rc.title_buttons_left, link) { part = ssd_get_part(&subtree->parts, b->type); wlr_scene_node_set_position(part->node, x, 0); - x += theme->window_button_width; + x += theme->window_button_width + theme->window_button_spacing; } - x = width - theme->window_button_width; + x = width - corner_width; part = ssd_get_part(&subtree->parts, LAB_SSD_PART_TITLEBAR_CORNER_RIGHT); wlr_scene_node_set_position(part->node, x, -rc.theme->border_width); + + x = width - theme->padding_width + theme->window_button_spacing; wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { part = ssd_get_part(&subtree->parts, b->type); + x -= theme->window_button_width + theme->window_button_spacing; wlr_scene_node_set_position(part->node, x, 0); - x -= theme->window_button_width; } } FOR_EACH_END ssd_update_title(ssd); @@ -457,19 +467,23 @@ get_title_offsets(struct ssd *ssd, int *offset_left, int *offset_right) { struct ssd_sub_tree *subtree = &ssd->titlebar.active; int button_width = ssd->view->server->theme->window_button_width; - *offset_left = 0; - *offset_right = 0; + int button_spacing = ssd->view->server->theme->window_button_spacing; + int padding_width = ssd->view->server->theme->padding_width; + *offset_left = padding_width; + *offset_right = padding_width; struct title_button *b; wl_list_for_each(b, &rc.title_buttons_left, link) { struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); if (part->node->enabled) { + *offset_left += *offset_left > padding_width ? button_spacing : 0; *offset_left += button_width; } } wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { struct ssd_part *part = ssd_get_part(&subtree->parts, b->type); if (part->node->enabled) { + *offset_right += *offset_right > padding_width ? button_spacing : 0; *offset_right += button_width; } } @@ -603,10 +617,10 @@ bool ssd_should_be_squared(struct ssd *ssd) { struct view *view = ssd->view; - int button_width = view->server->theme->window_button_width; + int corner_width = ssd_get_corner_width(); return (view_is_tiled_and_notify_tiled(view) - || view->current.width < button_width * 2) + || view->current.width < corner_width * 2) && view->maximized != VIEW_AXIS_BOTH; } diff --git a/src/ssd/ssd.c b/src/ssd/ssd.c index 0085fe3d..a54598fe 100644 --- a/src/ssd/ssd.c +++ b/src/ssd/ssd.c @@ -205,6 +205,13 @@ ssd_get_margin(const struct ssd *ssd) return ssd ? ssd->margin : (struct border){ 0 }; } +int +ssd_get_corner_width(void) +{ + /* ensure a minimum corner width */ + return MAX(rc.corner_radius, 5); +} + void ssd_update_margin(struct ssd *ssd) { diff --git a/src/theme.c b/src/theme.c index 28408990..9ba21d43 100644 --- a/src/theme.c +++ b/src/theme.c @@ -566,7 +566,9 @@ theme_builtin(struct theme *theme, struct server *server) theme->window_label_text_justify = parse_justification("Center"); theme->menu_title_text_justify = parse_justification("Center"); + theme->padding_width = 0; theme->window_button_width = 26; + theme->window_button_spacing = 0; parse_hexstr("#000000", theme->window_active_button_menu_unpressed_image_color); @@ -682,6 +684,9 @@ entry(struct theme *theme, const char *key, const char *value) if (match_glob(key, "border.width")) { theme->border_width = atoi(value); } + if (match_glob(key, "padding.width")) { + theme->padding_width = atoi(value); + } if (match_glob(key, "padding.height")) { theme->padding_height = atoi(value); } @@ -745,6 +750,9 @@ entry(struct theme *theme, const char *key, const char *value) theme->window_button_width = 1; } } + if (match_glob(key, "window.button.spacing")) { + theme->window_button_spacing = atoi(value); + } /* universal button */ if (match_glob(key, "window.active.button.unpressed.image.color")) { @@ -1198,10 +1206,12 @@ out: static void create_corners(struct theme *theme) { + int corner_width = ssd_get_corner_width(); + struct wlr_box box = { .x = 0, .y = 0, - .width = theme->window_button_width + theme->border_width, + .width = corner_width + theme->border_width, .height = theme->title_height + theme->border_width, }; diff --git a/src/view.c b/src/view.c index b1b4265a..aeb8e7fe 100644 --- a/src/view.c +++ b/src/view.c @@ -617,8 +617,7 @@ view_adjust_size(struct view *view, int *w, int *h) { assert(view); struct view_size_hints hints = view_get_size_hints(view); - int min_view_width = rc.theme->window_button_width * ( - wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); + int min_width = view_get_min_width(); /* * "If a base size is not provided, the minimum size is to be @@ -641,7 +640,7 @@ view_adjust_size(struct view *view, int *w, int *h) * This is currently always the case for xdg-shell views. */ if (hints.min_width < 1) { - hints.min_width = min_view_width; + hints.min_width = min_width; } if (hints.min_height < 1) { hints.min_height = LAB_MIN_VIEW_HEIGHT; @@ -2279,6 +2278,17 @@ view_reload_ssd(struct view *view) } } +int +view_get_min_width(void) +{ + int button_count_left = wl_list_length(&rc.title_buttons_left); + int button_count_right = wl_list_length(&rc.title_buttons_right); + return (rc.theme->window_button_width * (button_count_left + button_count_right)) + + (rc.theme->window_button_spacing * MAX((button_count_right - 1), 0)) + + (rc.theme->window_button_spacing * MAX((button_count_left - 1), 0)) + + (2 * rc.theme->padding_width); +} + void view_toggle_keybinds(struct view *view) { diff --git a/src/xwayland.c b/src/xwayland.c index fdc0b9e5..09366be2 100644 --- a/src/xwayland.c +++ b/src/xwayland.c @@ -606,15 +606,15 @@ handle_map_request(struct wl_listener *listener, void *data) static void check_natural_geometry(struct view *view) { + int min_width = view_get_min_width(); + /* * Some applications (example: Thonny) don't set a reasonable * un-maximized size when started maximized. Try to detect this * and set a fallback size. */ - int min_view_width = rc.theme->window_button_width * ( - wl_list_length(&rc.title_buttons_left) + wl_list_length(&rc.title_buttons_right)); if (!view_is_floating(view) - && (view->natural_geometry.width < min_view_width + && (view->natural_geometry.width < min_width || view->natural_geometry.height < LAB_MIN_VIEW_HEIGHT)) { view_set_fallback_natural_geometry(view); } From dcd9b47e5b584a2559d41cd255bb043f11970d12 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Tue, 10 Sep 2024 21:15:28 +0200 Subject: [PATCH 146/232] theme: ensure positive values (#2127) Clamp invalid configuration values to zero. --- src/theme.c | 112 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 38 deletions(-) diff --git a/src/theme.c b/src/theme.c index 9ba21d43..bb4948a0 100644 --- a/src/theme.c +++ b/src/theme.c @@ -670,6 +670,18 @@ theme_builtin(struct theme *theme, struct server *server) theme->mag_border_width = 1; } +static int +get_int_if_positive(const char *content, const char *field) +{ + int value = atoi(content); + if (value < 0) { + wlr_log(WLR_ERROR, + "%s cannot be negative, clamping it to 0.", field); + value = 0; + } + return value; +} + static void entry(struct theme *theme, const char *key, const char *value) { @@ -682,31 +694,39 @@ entry(struct theme *theme, const char *key, const char *value) * the first instance, "else if" cannot be used throughout this function */ if (match_glob(key, "border.width")) { - theme->border_width = atoi(value); + theme->border_width = get_int_if_positive( + value, "border.width"); } if (match_glob(key, "padding.width")) { - theme->padding_width = atoi(value); + theme->padding_width = get_int_if_positive( + value, "padding.width"); } if (match_glob(key, "padding.height")) { - theme->padding_height = atoi(value); + theme->padding_height = get_int_if_positive( + value, "padding.height"); } if (match_glob(key, "titlebar.height")) { - theme->title_height = atoi(value); + theme->title_height = get_int_if_positive( + value, "titlebar.height"); } if (match_glob(key, "menu.items.padding.x")) { - theme->menu_item_padding_x = atoi(value); + theme->menu_item_padding_x = get_int_if_positive( + value, "menu.items.padding.x"); } if (match_glob(key, "menu.items.padding.y")) { - theme->menu_item_padding_y = atoi(value); + theme->menu_item_padding_y = get_int_if_positive( + value, "menu.items.padding.y"); } if (match_glob(key, "menu.title.text.justify")) { theme->menu_title_text_justify = parse_justification(value); } if (match_glob(key, "menu.overlap.x")) { - theme->menu_overlap_x = atoi(value); + theme->menu_overlap_x = get_int_if_positive( + value, "menu.overlap.x"); } if (match_glob(key, "menu.overlap.y")) { - theme->menu_overlap_y = atoi(value); + theme->menu_overlap_y = get_int_if_positive( + value, "menu.overlap.y"); } if (match_glob(key, "window.active.border.color")) { @@ -751,7 +771,8 @@ entry(struct theme *theme, const char *key, const char *value) } } if (match_glob(key, "window.button.spacing")) { - theme->window_button_spacing = atoi(value); + theme->window_button_spacing = get_int_if_positive( + value, "window.button.spacing"); } /* universal button */ @@ -836,20 +857,12 @@ entry(struct theme *theme, const char *key, const char *value) /* window drop-shadows */ if (match_glob(key, "window.active.shadow.size")) { - theme->window_active_shadow_size = atoi(value); - if (theme->window_active_shadow_size < 0) { - wlr_log(WLR_ERROR, "window.active.shadow.size cannot " - "be negative, clamping it to 0."); - theme->window_active_shadow_size = 0; - } + theme->window_active_shadow_size = get_int_if_positive( + value, "window.active.shadow.size"); } if (match_glob(key, "window.inactive.shadow.size")) { - theme->window_inactive_shadow_size = atoi(value); - if (theme->window_inactive_shadow_size < 0) { - wlr_log(WLR_ERROR, "window.inactive.shadow.size cannot " - "be negative, clamping it to 0."); - theme->window_inactive_shadow_size = 0; - } + theme->window_inactive_shadow_size = get_int_if_positive( + value, "window.inactive.shadow.size"); } if (match_glob(key, "window.active.shadow.color")) { parse_hexstr(value, theme->window_active_shadow_color); @@ -859,10 +872,12 @@ entry(struct theme *theme, const char *key, const char *value) } if (match_glob(key, "menu.width.min")) { - theme->menu_min_width = atoi(value); + theme->menu_min_width = get_int_if_positive( + value, "menu.width.min"); } if (match_glob(key, "menu.width.max")) { - theme->menu_max_width = atoi(value); + theme->menu_max_width = get_int_if_positive( + value, "menu.width.max"); } if (match_glob(key, "menu.items.bg.color")) { @@ -879,13 +894,16 @@ entry(struct theme *theme, const char *key, const char *value) } if (match_glob(key, "menu.separator.width")) { - theme->menu_separator_line_thickness = atoi(value); + theme->menu_separator_line_thickness = get_int_if_positive( + value, "menu.separator.width"); } if (match_glob(key, "menu.separator.padding.width")) { - theme->menu_separator_padding_width = atoi(value); + theme->menu_separator_padding_width = get_int_if_positive( + value, "menu.separator.padding.width"); } if (match_glob(key, "menu.separator.padding.height")) { - theme->menu_separator_padding_height = atoi(value); + theme->menu_separator_padding_height = get_int_if_positive( + value, "menu.separator.padding.height"); } if (match_glob(key, "menu.separator.color")) { parse_hexstr(value, theme->menu_separator_color); @@ -903,7 +921,8 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->osd_bg_color); } if (match_glob(key, "osd.border.width")) { - theme->osd_border_width = atoi(value); + theme->osd_border_width = get_int_if_positive( + value, "osd.border.width"); } if (match_glob(key, "osd.border.color")) { parse_hexstr(value, theme->osd_border_color); @@ -914,31 +933,45 @@ entry(struct theme *theme, const char *key, const char *value) } else { theme->osd_window_switcher_width_is_percent = false; } - theme->osd_window_switcher_width = MAX(atoi(value), 0); + theme->osd_window_switcher_width = get_int_if_positive( + value, "osd.window-switcher.width"); } if (match_glob(key, "osd.window-switcher.padding")) { - theme->osd_window_switcher_padding = atoi(value); + theme->osd_window_switcher_padding = get_int_if_positive( + value, "osd.window-switcher.padding"); } if (match_glob(key, "osd.window-switcher.item.padding.x")) { - theme->osd_window_switcher_item_padding_x = atoi(value); + theme->osd_window_switcher_item_padding_x = + get_int_if_positive( + value, "osd.window-switcher.item.padding.x"); } if (match_glob(key, "osd.window-switcher.item.padding.y")) { - theme->osd_window_switcher_item_padding_y = atoi(value); + theme->osd_window_switcher_item_padding_y = + get_int_if_positive( + value, "osd.window-switcher.item.padding.y"); } if (match_glob(key, "osd.window-switcher.item.active.border.width")) { - theme->osd_window_switcher_item_active_border_width = atoi(value); + theme->osd_window_switcher_item_active_border_width = + get_int_if_positive( + value, "osd.window-switcher.item.active.border.width"); } if (match_glob(key, "osd.window-switcher.preview.border.width")) { - theme->osd_window_switcher_preview_border_width = atoi(value); + theme->osd_window_switcher_preview_border_width = + get_int_if_positive( + value, "osd.window-switcher.preview.border.width"); } if (match_glob(key, "osd.window-switcher.preview.border.color")) { parse_hexstrs(value, theme->osd_window_switcher_preview_border_color); } if (match_glob(key, "osd.workspace-switcher.boxes.width")) { - theme->osd_workspace_switcher_boxes_width = atoi(value); + theme->osd_workspace_switcher_boxes_width = + get_int_if_positive( + value, "osd.workspace-switcher.boxes.width"); } if (match_glob(key, "osd.workspace-switcher.boxes.height")) { - theme->osd_workspace_switcher_boxes_height = atoi(value); + theme->osd_workspace_switcher_boxes_height = + get_int_if_positive( + value, "osd.workspace-switcher.boxes.height"); } if (match_glob(key, "osd.label.text.color")) { parse_hexstr(value, theme->osd_label_text_color); @@ -962,10 +995,12 @@ entry(struct theme *theme, const char *key, const char *value) parse_hexstr(value, theme->snapping_overlay_edge.bg_color); } if (match_glob(key, "snapping.overlay.region.border.width")) { - theme->snapping_overlay_region.border_width = atoi(value); + theme->snapping_overlay_region.border_width = get_int_if_positive( + value, "snapping.overlay.region.border.width"); } if (match_glob(key, "snapping.overlay.edge.border.width")) { - theme->snapping_overlay_edge.border_width = atoi(value); + theme->snapping_overlay_edge.border_width = get_int_if_positive( + value, "snapping.overlay.edge.border.width"); } if (match_glob(key, "snapping.overlay.region.border.color")) { parse_hexstrs(value, theme->snapping_overlay_region.border_color); @@ -975,7 +1010,8 @@ entry(struct theme *theme, const char *key, const char *value) } if (match_glob(key, "magnifier.border.width")) { - theme->mag_border_width = atoi(value); + theme->mag_border_width = get_int_if_positive( + value, "magnifier.border.width"); } if (match_glob(key, "magnifier.border.color")) { parse_hexstr(value, theme->mag_border_color); From 9395348c55a2b0fd017f13d70309c27d3c8c6ae5 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Mon, 9 Sep 2024 17:44:03 +0200 Subject: [PATCH 147/232] theme: add circle button hover effect (#2127) --- docs/labwc-theme.5.scd | 4 +++ docs/themerc | 3 ++ include/theme.h | 7 +++++ src/theme.c | 64 +++++++++++++++++++++++++++++------------- 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/docs/labwc-theme.5.scd b/docs/labwc-theme.5.scd index 57b7aef9..e1b7bcb7 100644 --- a/docs/labwc-theme.5.scd +++ b/docs/labwc-theme.5.scd @@ -132,6 +132,10 @@ labwc-config(5). Space between titlebar buttons, in pixels. Default is 0. +*window.button.hover.bg.shape* + The shape of the hover effect of a titlebar button: "rectangle" or "circle". + Default is "rectangle". + *window.active.button.unpressed.image.color* Color of the images in titlebar buttons in their default, unpressed, state. This element is for the focused window. diff --git a/docs/themerc b/docs/themerc index 1e066074..49064207 100644 --- a/docs/themerc +++ b/docs/themerc @@ -34,6 +34,9 @@ window.label.text.justify: center window.button.width: 26 window.button.spacing: 0 +# window button hover effect +window.button.hover.bg.shape: rectangle + # window buttons window.active.button.unpressed.image.color: #000000 window.inactive.button.unpressed.image.color: #000000 diff --git a/include/theme.h b/include/theme.h index 4705926e..7f3eac90 100644 --- a/include/theme.h +++ b/include/theme.h @@ -17,6 +17,11 @@ enum lab_justification { LAB_JUSTIFY_RIGHT, }; +enum lab_shape { + LAB_RECTANGLE, + LAB_CIRCLE, +}; + struct theme_snapping_overlay { bool bg_enabled; bool border_enabled; @@ -57,6 +62,8 @@ struct theme { int window_button_width; /* the space between buttons */ int window_button_spacing; + /* the shape of the hover effect */ + enum lab_shape window_button_hover_bg_shape; /* button colors */ float window_active_button_menu_unpressed_image_color[4]; diff --git a/src/theme.c b/src/theme.c index bb4948a0..431b653e 100644 --- a/src/theme.c +++ b/src/theme.c @@ -70,6 +70,9 @@ struct rounded_corner_ctx { static struct lab_data_buffer *rounded_rect(struct rounded_corner_ctx *ctx); +/* 1 degree in radians (=2π/360) */ +static const double deg = 0.017453292519943295; + static void zdrop(struct lab_data_buffer **buffer) { @@ -162,26 +165,36 @@ create_hover_fallback(struct theme *theme, const char *icon_name, /* Overlay (pre-multiplied alpha) */ float overlay_color[4] = { 0.15f, 0.15f, 0.15f, 0.3f}; + int radius = MIN(width, height) / 2; enum corner corner = corner_from_icon_name(icon_name); - if (corner == LAB_CORNER_UNKNOWN) { + switch (theme->window_button_hover_bg_shape) { + case LAB_CIRCLE: set_cairo_color(cairo, overlay_color); - cairo_rectangle(cairo, 0, 0, width, height); + cairo_arc(cairo, width / 2, height / 2, radius, 0 * deg, 360 * deg); cairo_fill(cairo); - } else { - struct rounded_corner_ctx rounded_ctx = { - .box = &(struct wlr_box) { .width = width, .height = height }, - .radius = rc.corner_radius, - .line_width = theme->border_width, - .fill_color = overlay_color, - .border_color = overlay_color, - .corner = corner - }; - struct lab_data_buffer *overlay_buffer = rounded_rect(&rounded_ctx); - cairo_set_source_surface(cairo, - cairo_get_target(overlay_buffer->cairo), 0, 0); - cairo_paint(cairo); - wlr_buffer_drop(&overlay_buffer->base); + break; + case LAB_RECTANGLE: + if (corner == LAB_CORNER_UNKNOWN) { + set_cairo_color(cairo, overlay_color); + cairo_rectangle(cairo, 0, 0, width, height); + cairo_fill(cairo); + } else { + struct rounded_corner_ctx rounded_ctx = { + .box = &(struct wlr_box){.width = width, .height = height}, + .radius = rc.corner_radius, + .line_width = theme->border_width, + .fill_color = overlay_color, + .border_color = overlay_color, + .corner = corner}; + struct lab_data_buffer *overlay_buffer = + rounded_rect(&rounded_ctx); + cairo_set_source_surface(cairo, + cairo_get_target(overlay_buffer->cairo), 0, 0); + cairo_paint(cairo); + wlr_buffer_drop(&overlay_buffer->base); + } + break; } cairo_surface_flush(surf); @@ -532,6 +545,18 @@ parse_justification(const char *str) } } +static enum lab_shape +parse_shape(const char *str) +{ + if (!strcasecmp(str, "Rectangle")) { + return LAB_RECTANGLE; + } else if (!strcasecmp(str, "Circle")) { + return LAB_CIRCLE; + } else { + return LAB_RECTANGLE; + } +} + /* * We generally use Openbox defaults, but if no theme file can be found it's * better to populate the theme variables with some sane values as no-one @@ -569,6 +594,7 @@ theme_builtin(struct theme *theme, struct server *server) theme->padding_width = 0; theme->window_button_width = 26; theme->window_button_spacing = 0; + theme->window_button_hover_bg_shape = LAB_RECTANGLE; parse_hexstr("#000000", theme->window_active_button_menu_unpressed_image_color); @@ -774,6 +800,9 @@ entry(struct theme *theme, const char *key, const char *value) theme->window_button_spacing = get_int_if_positive( value, "window.button.spacing"); } + if (match_glob(key, "window.button.hover.bg.shape")) { + theme->window_button_hover_bg_shape = parse_shape(value); + } /* universal button */ if (match_glob(key, "window.active.button.unpressed.image.color")) { @@ -1077,9 +1106,6 @@ theme_read(struct theme *theme, struct wl_list *paths) static struct lab_data_buffer * rounded_rect(struct rounded_corner_ctx *ctx) { - /* 1 degree in radians (=2π/360) */ - double deg = 0.017453292519943295; - if (ctx->corner == LAB_CORNER_UNKNOWN) { return NULL; } From 21e92632a656a2b3e9584b4d2e64bdf912fed522 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Wed, 11 Sep 2024 17:52:26 +0200 Subject: [PATCH 148/232] theme: compensate the hover corner radius with padding When padding.width is set, the corner radius of the rectangle hover effect can be smaller since there is more space available. --- src/theme.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/theme.c b/src/theme.c index 431b653e..ab43a2e3 100644 --- a/src/theme.c +++ b/src/theme.c @@ -167,6 +167,7 @@ create_hover_fallback(struct theme *theme, const char *icon_name, float overlay_color[4] = { 0.15f, 0.15f, 0.15f, 0.3f}; int radius = MIN(width, height) / 2; enum corner corner = corner_from_icon_name(icon_name); + int corner_radius = MAX(rc.corner_radius - theme->padding_width, 0); switch (theme->window_button_hover_bg_shape) { case LAB_CIRCLE: @@ -182,7 +183,7 @@ create_hover_fallback(struct theme *theme, const char *icon_name, } else { struct rounded_corner_ctx rounded_ctx = { .box = &(struct wlr_box){.width = width, .height = height}, - .radius = rc.corner_radius, + .radius = corner_radius, .line_width = theme->border_width, .fill_color = overlay_color, .border_color = overlay_color, From e53ec5e5a04125dad5aa8a10e851f4cfb2b16c35 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Mon, 16 Sep 2024 00:16:30 +0900 Subject: [PATCH 149/232] theme: round the hover effect along the window border --- src/theme.c | 44 +++++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/theme.c b/src/theme.c index ab43a2e3..6965d39e 100644 --- a/src/theme.c +++ b/src/theme.c @@ -167,7 +167,6 @@ create_hover_fallback(struct theme *theme, const char *icon_name, float overlay_color[4] = { 0.15f, 0.15f, 0.15f, 0.3f}; int radius = MIN(width, height) / 2; enum corner corner = corner_from_icon_name(icon_name); - int corner_radius = MAX(rc.corner_radius - theme->padding_width, 0); switch (theme->window_button_hover_bg_shape) { case LAB_CIRCLE: @@ -176,24 +175,43 @@ create_hover_fallback(struct theme *theme, const char *icon_name, cairo_fill(cairo); break; case LAB_RECTANGLE: + set_cairo_color(cairo, overlay_color); + cairo_rectangle(cairo, 0, 0, width, height); if (corner == LAB_CORNER_UNKNOWN) { - set_cairo_color(cairo, overlay_color); - cairo_rectangle(cairo, 0, 0, width, height); cairo_fill(cairo); } else { + /* + * Round the hover overlay of corner buttons by + * cropping the region within the window border. + */ + float white[4] = {1, 1, 1, 1}; struct rounded_corner_ctx rounded_ctx = { - .box = &(struct wlr_box){.width = width, .height = height}, - .radius = corner_radius, + .box = &(struct wlr_box){ + .width = theme->padding_width + width, + .height = height, + }, + .radius = rc.corner_radius, .line_width = theme->border_width, - .fill_color = overlay_color, - .border_color = overlay_color, - .corner = corner}; - struct lab_data_buffer *overlay_buffer = + .fill_color = white, + .border_color = white, + .corner = corner, + }; + struct lab_data_buffer *mask_buffer = rounded_rect(&rounded_ctx); - cairo_set_source_surface(cairo, - cairo_get_target(overlay_buffer->cairo), 0, 0); - cairo_paint(cairo); - wlr_buffer_drop(&overlay_buffer->base); + cairo_pattern_t *mask_pattern = + cairo_pattern_create_for_surface( + cairo_get_target(mask_buffer->cairo)); + int mask_offset; + if (corner == LAB_CORNER_TOP_LEFT) { + mask_offset = -theme->padding_width; + } else { + mask_offset = 0; + } + cairo_save(cairo); + cairo_translate(cairo, mask_offset, 0); + cairo_mask(cairo, mask_pattern); + cairo_restore(cairo); + wlr_buffer_drop(&mask_buffer->base); } break; } From 8b1831c7311914ffb793c31773ce34151b37dae5 Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Sun, 15 Sep 2024 22:29:05 +0200 Subject: [PATCH 150/232] action: open the client menu underneath the window menu button It looks slightly awkward when the client menu shows up in the left corner of the view and the window menu button is configured to be on the right side. --- src/action.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/src/action.c b/src/action.c index a157d6e8..73ef48a5 100644 --- a/src/action.c +++ b/src/action.c @@ -617,6 +617,38 @@ action_list_free(struct wl_list *action_list) } } +static int +get_window_menu_button_offset(struct server *server, struct view *view) +{ + int padding_width = server->theme->padding_width; + int button_width = server->theme->window_button_width; + int button_spacing = server->theme->window_button_spacing; + + struct title_button *b; + + int offset = padding_width; + wl_list_for_each(b, &rc.title_buttons_left, link) { + if (b->type == LAB_SSD_BUTTON_WINDOW_MENU) { + return offset; + } else { + offset += button_width + button_spacing; + } + } + + offset = view->current.width - padding_width; + wl_list_for_each_reverse(b, &rc.title_buttons_right, link) { + if (b->type == LAB_SSD_BUTTON_WINDOW_MENU) { + offset -= button_width; + return offset; + } else { + offset -= button_width + button_spacing; + } + } + + /* no menu button */ + return 0; +} + static void show_menu(struct server *server, struct view *view, const char *menu_name, bool at_cursor, @@ -637,13 +669,17 @@ show_menu(struct server *server, struct view *view, int y = server->seat.cursor->y; /* The client menu needs an active client */ - if (!view && strcasecmp(menu_name, "client-menu") == 0) { + bool is_client_menu = !strcasecmp(menu_name, "client-menu"); + if (!view && is_client_menu) { return; } /* Place menu in the view corner if desired (and menu is not root-menu) */ if (!at_cursor && view) { - x = view->current.x; + /* push the client menu underneath the window menu button */ + int offset = is_client_menu + ? get_window_menu_button_offset(server, view) : 0; + x = view->current.x + offset; y = view->current.y; } From fc4bbca6f29377bc19d961fe78a76609b241d38d Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Thu, 12 Sep 2024 21:14:10 +0100 Subject: [PATCH 151/232] NEWS.md: interim update --- NEWS.md | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/NEWS.md b/NEWS.md index 1e28fb84..84a63412 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,6 +31,72 @@ The format is based on [Keep a Changelog] | 2021-04-15 | [0.2.0] | 0.13.0 | 5011 | | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | +## [unreleased] + +### Added + +- Add theme options for circular button hover effect, button padding and button + spacing. Written-by: @jp7677 (#2127) + +``` +window.button.hover.bg.shape: circle +padding.width: 4 +window.button.spacing: 3 +``` + +- Add position arguments for menus. Written-by: @droc12345 (#2102) + +``` + + root-menu + + 0 + 0 + + +``` + +- Allow interactive window movement when horizontally or vertically maximized + and add associated config option `` (#2052) +- Add optional Shade (shade.xbm) and AllDesktops (desk.xbm) buttons and theme + options: + +``` +window.active.button.desk.unpressed.image.color +window.inactive.button.desk.unpressed.image.color +window.active.button.shade.unpressed.image.color +window.inactive.button.shade.unpressed.image.color +``` + +- Make action `FocusOutput` behave like `MoveToOutput` by adding direction and + wrap arguments. Written-by: @orfeasxyz (#2100) +- Add config option `titleLayout`. Written-by: @xi (#2088) +- Add `Oblique` option to `