Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package labwc for openSUSE:Factory checked in at 2026-08-22 21:35:34 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/labwc (Old) and /work/SRC/openSUSE:Factory/.labwc.new.1258 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "labwc" Sat Aug 22 21:35:34 2026 rev:19 rq:1373020 version:0.20.2 Changes: -------- --- /work/SRC/openSUSE:Factory/labwc/labwc.changes 2026-06-16 18:30:36.084553525 +0200 +++ /work/SRC/openSUSE:Factory/.labwc.new.1258/labwc.changes 2026-08-22 21:37:41.816551630 +0200 @@ -1,0 +2,15 @@ +Fri Aug 21 18:34:01 UTC 2026 - zeus <[email protected]> + +- Update to version 0.20.2: + * Display config errors in labnag or optionally another client. + Also support associated option <core><errorCommand> + * Destroy scene-output before scene cleanup to avoid rare abort. + * Do not leave a failed output render format on the pending state + * When window-cycling is cancelled give back focus to the window + that had it before cycling started + * Fix missing panel icon with Firefox running under X11 + * abnag(1): document defaults + * Force the initial modeset commit to fix an issue with + QEMU/virtio-gpu and WLR_NO_HARDWARE_CURSORS=1 + +------------------------------------------------------------------- Old: ---- labwc-0.20.1.tar.gz New: ---- labwc-0.20.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ labwc.spec ++++++ --- /var/tmp/diff_new_pack.QdmMDm/_old 2026-08-22 21:37:42.389572113 +0200 +++ /var/tmp/diff_new_pack.QdmMDm/_new 2026-08-22 21:37:42.391572184 +0200 @@ -24,7 +24,7 @@ %define slname libsfdo %define slver 0.1.4 Name: labwc -Version: 0.20.1 +Version: 0.20.2 Release: 0 Summary: A Wayland window-stacking compositor License: GPL-2.0-only ++++++ labwc-0.20.1.tar.gz -> labwc-0.20.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/NEWS.md new/labwc-0.20.2/NEWS.md --- old/labwc-0.20.1/NEWS.md 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/NEWS.md 2026-08-21 19:24:33.000000000 +0200 @@ -9,6 +9,7 @@ | Date | All Changes | wlroots version | lines-of-code | |------------|---------------|-----------------|---------------| +| 2026-08-21 | [0.20.2] | 0.20.2 | 28553 | | 2026-06-15 | [0.20.1] | 0.20.1 | 28337 | | 2026-05-25 | [0.20.0] | 0.20.1 | 28313 | | 2026-06-11 | [0.9.8] | 0.19.3 | 29284 | @@ -47,6 +48,7 @@ | 2021-03-05 | [0.1.0] | 0.12.0 | 4627 | [unreleased]: NEWS.md#unreleased +[0.20.2]: NEWS.md#0202---2026-08-21 [0.20.1]: NEWS.md#0201---2026-06-15 [0.20.0]: NEWS.md#0200---2026-05-25 [0.9.8]: NEWS.md#098---2026-06-11 @@ -92,14 +94,39 @@ There are some regression warnings worth noting for the switch to wlroots 0.20: -- lxqt-panel auto-hiding does not work with a panel size greater than 40 [#3600] - [wlroots-5392] +- With wlroots < 0.20.2 lxqt-panel auto-hiding does not work with a panel size + greater than 40 [#3600] [wlroots-5392] [wlroots-5407] - Some Wine game windows disappear after alt-tab. It is not yet clear where the bug is, but the issue manifests itself when running wlroots-0.20 [#3615] [wlroots-4103] +- `wl_output` make/model properties are no longer sent because these are legacy + and should not be relied on [wlroots-5045] [wlroots-5392]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5392 +[wlroots-5407]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5407 [wlroots-4103]: https://gitlab.freedesktop.org/wlroots/wlroots/-/work_items/4103 +[wlroots-5045]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5045 + +## 0.20.2 - 2026-08-21 + +[0.20.2-commits] + +### Added + +- Display config errors in labnag or optionally another client. Also support + associated option `<core><errorCommand>`. @elviosak @Consolatis [#3697] + +### Fixed + +- Destroy scene-output before scene cleanup to avoid rare abort. @jlvihv [#3701] +- Do not leave a failed output render format on the pending state @Andy1210 + [#3685] +- When window-cycling is cancelled, for example by pressing escape, give back + focus to the window that had it before cycling started @elviosak [#3667] +- Fix missing panel icon with Firefox running under X11 @jlindgren90 [#3641] +- labnag(1): document defaults @cunlem [#3654] +- Force the initial modeset commit to fix an issue with QEMU/virtio-gpu + and `WLR_NO_HARDWARE_CURSORS=1`. @KiritakeKumi [#3656] ## 0.20.1 - 2026-06-15 @@ -1945,7 +1972,7 @@ - Add `ToggleOmnipresent` action and add an "Always on Visible Workspace" entry for it in the client-menu under the Workspaces submenu. Written-by: @bnason - Account for space taken up by XWayland clients with `_NET_WM_STRUT_PARTIAL` - property in the `usable_area` calculation. This increases inter-operability + property in the `usable_area` calculation. This increases interoperability with X11 desktop components. - Set XWayland's `_NET_WORKAREA` property based on usable area. XWayland clients use the `_NET_WORKAREA` root window property to determine how much of @@ -2813,7 +2840,8 @@ ShowMenu [Keep a Changelog]: https://keepachangelog.com/en/1.0.0/ -[unreleased-commits]: https://github.com/labwc/labwc/compare/0.20.1...HEAD +[unreleased-commits]: https://github.com/labwc/labwc/compare/0.20.2...HEAD +[0.20.2-commits]: https://github.com/labwc/labwc/compare/0.20.1..0.20.2 [0.20.1-commits]: https://github.com/labwc/labwc/compare/0.20.0..0.20.1 [0.20.0-commits]: https://github.com/labwc/labwc/compare/0.9.5..0.20.0 [0.9.8-commits]: https://github.com/labwc/labwc/compare/0.9.7...0.9.8 @@ -3401,3 +3429,10 @@ [#3625]: https://github.com/labwc/labwc/pull/3625 [#3630]: https://github.com/labwc/labwc/pull/3630 [#3631]: https://github.com/labwc/labwc/pull/3631 +[#3641]: https://github.com/labwc/labwc/pull/3641 +[#3654]: https://github.com/labwc/labwc/pull/3654 +[#3656]: https://github.com/labwc/labwc/pull/3656 +[#3667]: https://github.com/labwc/labwc/pull/3667 +[#3685]: https://github.com/labwc/labwc/pull/3685 +[#3697]: https://github.com/labwc/labwc/pull/3697 +[#3701]: https://github.com/labwc/labwc/pull/3701 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/docs/labnag.1.scd new/labwc-0.20.2/docs/labnag.1.scd --- old/labwc-0.20.1/docs/labnag.1.scd 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/docs/labnag.1.scd 2026-08-21 19:24:33.000000000 +0200 @@ -26,16 +26,16 @@ Enable debugging. *-e, --edge* top|bottom - Set the edge to use. + Set the edge to use. Default is top. *-y, --layer* overlay|top|bottom|background - Set the layer to use. + Set the layer to use. Default is top. *-k, --keyboard-focus none|exclusive|on-demand* - Set the policy for keyboard focus. + Set the policy for keyboard focus. Default is none. *-f, --font* <font> - Set the font to use. + Set the font to use. Default is _Sans 10_. *-h, --help* Show help message and quit. @@ -66,55 +66,57 @@ # APPEARANCE OPTIONS *--background-color* <RRGGBB[AA]> - Set the color of the background. + Set the color of the background. Default is 900000. *--button-border-color* <RRGGBB[AA]> - Set the color of the button border. + Set the color of the button border. Default is D92424. *--border-bottom-color* <RRGGBB[AA]> - Set the color of the bottom border. + Set the color of the bottom border. Default is 470909. *--button-background-color* <RRGGBB[AA]> - Set the color for the background for buttons. + Set the color for the background for buttons. Default is 680A0A. *--text-color* <RRGGBB[AA]> - Set the text color. + Set the text color. Default is FFFFFF. *--button-text-color* <RRGGBB[AA]> - Set the button text color. + Set the button text color. Default is FFFFFF. *--border-bottom-size* <size> - Set the thickness of the bottom border. + Set the thickness of the bottom border. Default is 3. *--message-padding* <padding> - Set the padding for the message. + Set the padding for the message. Default is 8. *--details-background-color* <RRGGBB[AA]> - Set the color for the background for details. + Set the color for the background for details. Default is 680A0A. *--details-border-size* <size> - Set the thickness for the details border. + Set the thickness for the details border. Default is 3. *--details-border-color* <RRGGBB[AA]> - Set the color of the details border. + Set the color of the details border. Default is 680A0A. *--details-margin* <margin> - Set the margin for the details. + Set the margin for the details. Default is 11. *--button-border-size* <size> - Set the thickness for the button border. + Set the thickness for the button border. Default is 3. *--button-gap* <gap> - Set the size of the gap between buttons. + Set the size of the gap between buttons. Default is 20. *--button-dismiss-gap* <gap> Set the size of the gap between the dismiss button and another button. + Default is 15. *--button-margin-right* <margin> Set the margin from the right of the dismiss button to edge. + Default is 2. *--button-padding* <padding> - Set the padding for the button text. + Set the padding for the button text. Default is 3. # EXAMPLE diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/docs/labwc-actions.5.scd new/labwc-0.20.2/docs/labwc-actions.5.scd --- old/labwc-0.20.1/docs/labwc-actions.5.scd 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/docs/labwc-actions.5.scd 2026-08-21 19:24:33.000000000 +0200 @@ -63,6 +63,10 @@ top or bottom tell how much to resize on that edge of window, positive values grow window, negative shrink window. + Note that some applications like terminal emulators may refuse growing + or shrinking by small amounts to keep the window size aligned to their + internal cell size. + *<action name="GrowToEdge" direction="value" />* Resize window to fill the space between its edge and any other window edge. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/docs/labwc-config.5.scd new/labwc-0.20.2/docs/labwc-config.5.scd --- old/labwc-0.20.1/docs/labwc-config.5.scd 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/docs/labwc-config.5.scd 2026-08-21 19:24:33.000000000 +0200 @@ -180,6 +180,7 @@ <xwaylandPersistence>no</xwaylandPersistence> <primarySelection>yes</primarySelection> <promptCommand>[see details below]</promptCommand> + <errorCommand>[see details below]</errorCommand> </core> ``` @@ -316,6 +317,29 @@ --cancel-label="%n" ``` +*<core><errorCommand>* + Set command to be invoked for displaying errors in the config files, + it is executed when errors are detected on startup and reconfigure. + The errors are sent to STDIN of the program, and a SIGTERM is sent to + it if the process is still running when a reconfigure is triggered. + + The default error command is: + ``` + labnag \\ + --message 'Config errors detected' \\ + --button-dismiss 'Close' \\ + --layer overlay \\ + --timeout 0 \\ + --detailed-message + ``` + + Example using `zenity`: + ``` + <core> + <errorCommand>zenity --title='Config Error' --text-info</errorCommand> + </core> + ``` + ## PLACEMENT ``` diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/docs/rc.xml.all new/labwc-0.20.2/docs/rc.xml.all --- old/labwc-0.20.1/docs/rc.xml.all 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/docs/rc.xml.all 2026-08-21 19:24:33.000000000 +0200 @@ -20,6 +20,8 @@ <!-- # See labwc-config(5) for details <promptCommand></promptCommand> + # See labwc-config(5) for details + <errorCommand></errorCommand> --> </core> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/include/common/nag.h new/labwc-0.20.2/include/common/nag.h --- old/labwc-0.20.1/include/common/nag.h 1970-01-01 01:00:00.000000000 +0100 +++ new/labwc-0.20.2/include/common/nag.h 2026-08-21 19:24:33.000000000 +0200 @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef LABWC_NAG_H +#define LABWC_NAG_H + +#include <stdbool.h> +#include <sys/wait.h> +#include <wlr/util/log.h> +#include "common/buf.h" + +/* May return NULL if the buffer is currently written to a client */ +struct buf *nag_get_buf(void); + +void nag_set_error(enum wlr_log_importance importance); +bool nag_check_pid(pid_t exited_pid); +void nag_reset(void); +void nag_show_callback(void *data); +void nag_finish(void); + +#define nag_log(verbosity, fmt, ...) \ +do { \ + wlr_log(verbosity, fmt, ##__VA_ARGS__); \ + nag_set_error(verbosity); \ + if (nag_get_buf()) { \ + buf_add_fmt(nag_get_buf(), fmt "\n", ##__VA_ARGS__); \ + } \ +} while (0) + +#endif /* LABWC_NAG_H */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/include/common/spawn.h new/labwc-0.20.2/include/common/spawn.h --- old/labwc-0.20.1/include/common/spawn.h 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/include/common/spawn.h 2026-08-21 19:24:33.000000000 +0200 @@ -23,6 +23,19 @@ void spawn_sync_no_shell(char const *command); /** + * spawn_piped_async_no_shell - execute asynchronously + * @command: command to be executed + * @pipe_fd_w: set to the write end of a pipe + * connected to stdin of the command + * Notes: + * The returned pid_t has to be waited for to + * not produce zombies and the pipe_fd_w has to + * be closed. spawn_piped_close() can be used + * to ensure both. + */ +pid_t spawn_piped_async_no_shell(const char *command, int *pipe_fd_w); + +/** * spawn_piped - execute asynchronously * @command: command to be executed * @pipe_fd: set to the read end of a pipe diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/include/config/rcxml.h new/labwc-0.20.2/include/config/rcxml.h --- old/labwc-0.20.1/include/config/rcxml.h 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/include/config/rcxml.h 2026-08-21 19:24:33.000000000 +0200 @@ -87,6 +87,7 @@ bool xwayland_persistence; bool primary_selection; char *prompt_command; + char *error_command; /* placement */ enum lab_placement_policy placement_policy; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/meson.build new/labwc-0.20.2/meson.build --- old/labwc-0.20.1/meson.build 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/meson.build 2026-08-21 19:24:33.000000000 +0200 @@ -1,7 +1,7 @@ project( 'labwc', 'c', - version: '0.20.1', + version: '0.20.2', license: 'GPL-2.0-only', meson_version: '>=0.59.0', default_options: [ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/po/LINGUAS new/labwc-0.20.2/po/LINGUAS --- old/labwc-0.20.1/po/LINGUAS 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/po/LINGUAS 2026-08-21 19:24:33.000000000 +0200 @@ -1 +1 @@ -ar ca cs da de el es et eu fa fi fr gl he hr hu id it ja ka kab kk ko lt ms nl pa pl pt pt_BR ru sk sr_Latn sv tr uk vi zh_CN zh_TW +ar ca cs da de el es et eu fa fi fr gl he hr hu id it ja ka kab kk ko lt ms nl pa pl pt pt_BR ru sk sr_Latn sv te tr uk vi zh_CN zh_TW diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/po/el.po new/labwc-0.20.2/po/el.po --- old/labwc-0.20.1/po/el.po 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/po/el.po 2026-08-21 19:24:33.000000000 +0200 @@ -8,7 +8,7 @@ "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2025-08-02 08:01+0000\n" +"PO-Revision-Date: 2026-07-14 03:29+0000\n" "Last-Translator: Dimitrios Glentadakis <[email protected]>\n" "Language-Team: Greek <https://translate.lxqt-project.org/projects/labwc/" "labwc/el/>\n" @@ -21,7 +21,7 @@ #: src/menu/menu.c:1016 msgid "Go there..." -msgstr "Πήγαινε εκεί..." +msgstr "Πήγαινε..." #: src/menu/menu.c:1034 msgid "Terminal" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/po/id.po new/labwc-0.20.2/po/id.po --- old/labwc-0.20.1/po/id.po 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/po/id.po 2026-08-21 19:24:33.000000000 +0200 @@ -8,9 +8,10 @@ "Project-Id-Version: labwc\n" "Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" "POT-Creation-Date: 2024-09-19 21:09+1000\n" -"PO-Revision-Date: 2025-05-02 13:26+0000\n" -"Last-Translator: Syahmin Sukhairi <[email protected]>\n" -"Language-Team: Indonesian <https://translate.lxqt-project.org/projects/labwc/labwc/id/>\n" +"PO-Revision-Date: 2026-07-27 07:29+0000\n" +"Last-Translator: Zycresth <[email protected]>\n" +"Language-Team: Indonesian <https://translate.lxqt-project.org/projects/labwc/" +"labwc/id/>\n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -24,7 +25,7 @@ #: src/menu/menu.c:1034 msgid "Terminal" -msgstr "" +msgstr "Terminal" #: src/menu/menu.c:1040 msgid "Reconfigure" @@ -48,7 +49,7 @@ #: src/menu/menu.c:1062 msgid "Roll Up/Down" -msgstr "Gulir atas/bawah" +msgstr "Gulir Atas/Bawah" #: src/menu/menu.c:1064 msgid "Decorations" @@ -56,15 +57,15 @@ #: src/menu/menu.c:1066 msgid "Always on Top" -msgstr "Selalu di Muka" +msgstr "Selalu di Atas" #: src/menu/menu.c:1071 msgid "Move Left" -msgstr "Geser ke Kiri" +msgstr "Geser Kiri" #: src/menu/menu.c:1078 msgid "Move Right" -msgstr "Geser ke Kanan" +msgstr "Geser Kanan" #: src/menu/menu.c:1083 msgid "Always on Visible Workspace" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/po/te.po new/labwc-0.20.2/po/te.po --- old/labwc-0.20.1/po/te.po 1970-01-01 01:00:00.000000000 +0100 +++ new/labwc-0.20.2/po/te.po 2026-08-21 19:24:33.000000000 +0200 @@ -0,0 +1,80 @@ +# Labwc pot file +# Copyright (C) 2024 +# This file is distributed under the same license as the labwc package. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +msgid "" +msgstr "" +"Project-Id-Version: labwc\n" +"Report-Msgid-Bugs-To: https://github.com/labwc/labwc/issues\n" +"POT-Creation-Date: 2024-09-19 21:09+1000\n" +"PO-Revision-Date: 2026-07-16 08:01+0000\n" +"Last-Translator: Gowtham Reddy Somala <[email protected]>\n" +"Language-Team: Telugu <https://translate.lxqt-project.org/projects/labwc/" +"labwc/te/>\n" +"Language: te\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:1016 +msgid "Go there..." +msgstr "అక్కడికి వెళ్ళు..." + +#: src/menu/menu.c:1034 +msgid "Terminal" +msgstr "టెర్మినల్" + +#: src/menu/menu.c:1040 +msgid "Reconfigure" +msgstr "పునర్వ్యవస్థీకరించు" + +#: src/menu/menu.c:1042 +msgid "Exit" +msgstr "నిష్క్రమించు" + +#: src/menu/menu.c:1056 +msgid "Minimize" +msgstr "కుదించండి" + +#: src/menu/menu.c:1058 +msgid "Maximize" +msgstr "గరిష్ఠం చేయండి" + +#: src/menu/menu.c:1060 +msgid "Fullscreen" +msgstr "పూర్తి స్క్రీన్" + +#: src/menu/menu.c:1062 +msgid "Roll Up/Down" +msgstr "పైకి/కిందకు జరపండి" + +#: src/menu/menu.c:1064 +msgid "Decorations" +msgstr "అలంకరణలు" + +#: src/menu/menu.c:1066 +msgid "Always on Top" +msgstr "ఎల్లప్పుడూ పైన" + +#: src/menu/menu.c:1071 +msgid "Move Left" +msgstr "ఎడమ వైపుకు" + +#: src/menu/menu.c:1078 +msgid "Move Right" +msgstr "కుడి వైపుకు" + +#: src/menu/menu.c:1083 +msgid "Always on Visible Workspace" +msgstr "ఎల్లప్పుడూ కనిపించే పని ప్రదేశం" + +#: src/menu/menu.c:1086 +msgid "Workspace" +msgstr "పని ప్రదేశం" + +#: src/menu/menu.c:1089 +msgid "Close" +msgstr "మూసివేయి" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/action.c new/labwc-0.20.2/src/action.c --- old/labwc-0.20.1/src/action.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/action.c 2026-08-21 19:24:33.000000000 +0200 @@ -14,6 +14,7 @@ #include "common/macros.h" #include "common/list.h" #include "common/mem.h" +#include "common/nag.h" #include "common/parse-bool.h" #include "common/spawn.h" #include "common/string-helpers.h" @@ -347,7 +348,7 @@ } else if (!strcasecmp(content, "current")) { action_arg_add_int(action, argument, CYCLE_WORKSPACE_CURRENT); } else { - wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", + nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } goto cleanup; @@ -360,7 +361,7 @@ } else if (!strcasecmp(content, "focused")) { action_arg_add_int(action, argument, CYCLE_OUTPUT_FOCUSED); } else { - wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", + nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } goto cleanup; @@ -371,7 +372,7 @@ } else if (!strcasecmp(content, "current")) { action_arg_add_int(action, argument, CYCLE_APP_ID_CURRENT); } else { - wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", + nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } goto cleanup; @@ -401,7 +402,7 @@ if (!strcmp(argument, "direction")) { enum view_axis axis = view_axis_parse(content); if (axis == VIEW_AXIS_NONE || axis == VIEW_AXIS_INVALID) { - wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", + nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { action_arg_add_int(action, argument, axis); @@ -415,7 +416,7 @@ if (mode != LAB_SSD_MODE_INVALID) { action_arg_add_int(action, argument, mode); } else { - wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", + nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } goto cleanup; @@ -430,7 +431,7 @@ enum lab_edge edge = lab_edge_parse(content, /*tiled*/ true, /*any*/ false); if (edge == LAB_EDGE_NONE || edge == LAB_EDGE_CENTER) { - wlr_log(WLR_ERROR, + nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { @@ -497,7 +498,7 @@ enum lab_edge edge = lab_edge_parse(content, /*tiled*/ false, /*any*/ false); if (edge == LAB_EDGE_NONE) { - wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", + nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { action_arg_add_int(action, argument, edge); @@ -521,7 +522,7 @@ enum lab_placement_policy policy = view_placement_parse(content); if (policy == LAB_PLACE_INVALID) { - wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", + nag_log(WLR_ERROR, "Invalid argument for action %s: '%s' (%s)", action_names[action->type], argument, content); } else { action_arg_add_int(action, argument, policy); @@ -542,7 +543,7 @@ goto cleanup; } - wlr_log(WLR_ERROR, "Invalid argument for action %s: '%s'", + nag_log(WLR_ERROR, "Invalid argument for action %s: '%s'", action_names[action->type], argument); cleanup: @@ -557,7 +558,7 @@ return i; } } - wlr_log(WLR_ERROR, "Invalid action: %s", action_name); + nag_log(WLR_ERROR, "Invalid action: %s", action_name); return ACTION_TYPE_INVALID; } @@ -565,7 +566,7 @@ action_create(const char *action_name) { if (!action_name) { - wlr_log(WLR_ERROR, "action name not specified"); + nag_log(WLR_ERROR, "action name not specified"); return NULL; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/common/buf.c new/labwc-0.20.2/src/common/buf.c --- old/labwc-0.20.1/src/common/buf.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/common/buf.c 2026-08-21 19:24:33.000000000 +0200 @@ -15,7 +15,7 @@ buf_expand_tilde(struct buf *s) { struct buf tmp = BUF_INIT; - for (int i = 0 ; i < s->len ; i++) { + for (int i = 0; i < s->len; i++) { if (s->data[i] == '~') { buf_add(&tmp, getenv("HOME")); } else { @@ -49,8 +49,8 @@ struct buf tmp = BUF_INIT; struct buf environment_variable = BUF_INIT; - for (int i = 0 ; i < s->len ; i++) { - if (s->data[i] == '$' && isvalid(s->data[i+1])) { + for (int i = 0; i < s->len; i++) { + if (s->data[i] == '$' && isvalid(s->data[i + 1])) { /* expand environment variable */ buf_clear(&environment_variable); int len = 0; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/common/dir.c new/labwc-0.20.2/src/common/dir.c --- old/labwc-0.20.1/src/common/dir.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/common/dir.c 2026-08-21 19:24:33.000000000 +0200 @@ -62,8 +62,15 @@ struct wl_list *list; }; -struct wl_list *paths_get_prev(struct wl_list *elm) { return elm->prev; } -struct wl_list *paths_get_next(struct wl_list *elm) { return elm->next; } +struct wl_list *paths_get_prev(struct wl_list *elm) +{ + return elm->prev; +} + +struct wl_list *paths_get_next(struct wl_list *elm) +{ + return elm->next; +} static void build_config_path(struct ctx *ctx, char *prefix, const char *path) @@ -117,9 +124,9 @@ * separated paths and that we have structured the * .default_prefix in the same way. */ - gchar * *prefixes; + gchar **prefixes; prefixes = g_strsplit(prefix.data, ":", -1); - for (gchar * *p = prefixes; *p; p++) { + for (gchar **p = prefixes; *p; p++) { ctx->build_path_fn(ctx, *p, d.path); if (debug) { fprintf(stderr, "%s\n", ctx->buf); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/common/meson.build new/labwc-0.20.2/src/common/meson.build --- old/labwc-0.20.1/src/common/meson.build 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/common/meson.build 2026-08-21 19:24:33.000000000 +0200 @@ -10,6 +10,7 @@ 'lab-scene-rect.c', 'match.c', 'mem.c', + 'nag.c', 'nodename.c', 'node-type.c', 'parse-bool.c', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/common/nag.c new/labwc-0.20.2/src/common/nag.c --- old/labwc-0.20.1/src/common/nag.c 1970-01-01 01:00:00.000000000 +0100 +++ new/labwc-0.20.2/src/common/nag.c 2026-08-21 19:24:33.000000000 +0200 @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0-only +#define _POSIX_C_SOURCE 200809L +#include "common/nag.h" +#include <assert.h> +#include <unistd.h> +#include <wlr/util/log.h> +#include <wayland-server-core.h> +#include "common/buf.h" +#include "config/rcxml.h" +#include "common/spawn.h" +#include "labwc.h" + +static struct buf log_buf = BUF_INIT; +static bool has_error = false; +static pid_t pid = 0; +static int pipe_w = -1; +static struct wl_event_source *write_notifier = NULL; +static size_t remaining = 0; + +struct buf * +nag_get_buf(void) +{ + if (write_notifier) { + /* + * Ensure we are not accidentally modifying + * the buffer while writing it to a client. + * + * This may happend when using nag_log() after + * nag_show() without a nag_reset() in between. + */ + wlr_log(WLR_ERROR, "Not writing to log buffer while sending to client"); + return NULL; + } + return &log_buf; +} + +void +nag_set_error(enum wlr_log_importance importance) +{ + if (!has_error && importance == WLR_ERROR) { + has_error = true; + } +} + +bool +nag_check_pid(pid_t exited_pid) +{ + if (pid && pid == exited_pid) { + pid = 0; + return true; + } + return false; +} + +void +nag_reset(void) +{ + if (pid > 0) { + kill(pid, SIGTERM); + /* waitpid() is done in a generic SIGCHLD handler in src/server.c */ + pid = 0; + } + if (write_notifier) { + wl_event_source_remove(write_notifier); + write_notifier = NULL; + } + if (pipe_w != -1) { + close(pipe_w); + pipe_w = -1; + } + has_error = false; + buf_clear(&log_buf); +} + +static int +handle_writable(int fd, uint32_t mask, void *data) +{ + assert(write_notifier); + assert(fd == pipe_w); + assert(remaining > 0); + assert(remaining <= (size_t)log_buf.len); + + ssize_t bytes = write(fd, log_buf.data + (log_buf.len - remaining), remaining); + if (bytes < 0) { + wlr_log_errno(WLR_ERROR, "Failed to write errors to process %s", rc.error_command); + } else { + remaining -= bytes; + if (remaining > 0) { + /* Keep waiting */ + return 0; + } + } + + wl_event_source_remove(write_notifier); + write_notifier = NULL; + + close(pipe_w); + pipe_w = -1; + return 0; +} + +static void +nag_show(void) +{ + if (!has_error || log_buf.len == 0) { + return; + } + + pid = spawn_piped_async_no_shell(rc.error_command, &pipe_w); + if (pid < 0) { + wlr_log_errno(WLR_ERROR, "Failed to launch process: %s", rc.error_command); + return; + } + + assert(!write_notifier); + remaining = log_buf.len; + write_notifier = wl_event_loop_add_fd(server.wl_event_loop, + pipe_w, WL_EVENT_WRITABLE, handle_writable, NULL); +} + +void +nag_show_callback(void *data) +{ + nag_show(); +} + +void +nag_finish(void) +{ + nag_reset(); + buf_reset(&log_buf); +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/common/spawn.c new/labwc-0.20.2/src/common/spawn.c --- old/labwc-0.20.1/src/common/spawn.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/common/spawn.c 2026-08-21 19:24:33.000000000 +0200 @@ -30,13 +30,30 @@ int flags = fcntl(fd, F_GETFD); if (flags == -1) { wlr_log_errno(WLR_ERROR, - "Unable to set the CLOEXEC flag: fnctl failed"); + "Unable to get the CLOEXEC flag: fcntl failed"); return false; } flags = flags | FD_CLOEXEC; if (fcntl(fd, F_SETFD, flags) == -1) { wlr_log_errno(WLR_ERROR, - "Unable to set the CLOEXEC flag: fnctl failed"); + "Unable to set the CLOEXEC flag: fcntl failed"); + return false; + } + return true; +} + +static bool +set_nonblock(int fd) +{ + int flags = fcntl(fd, F_GETFL); + if (flags == -1) { + wlr_log_errno(WLR_ERROR, + "Unable to get the O_NONBLOCK flag: fcntl failed"); + return false; + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { + wlr_log_errno(WLR_ERROR, + "Unable to set the O_NONBLOCK flag: fcntl failed"); return false; } return true; @@ -156,6 +173,89 @@ } pid_t +spawn_piped_async_no_shell(const char *command, int *pipe_fd_w) +{ + assert(command); + + GError *err = NULL; + gchar **argv = NULL; + + /* Use glib's shell-parse to mimic Openbox's behaviour */ + g_shell_parse_argv((gchar *)command, NULL, &argv, &err); + if (err) { + g_message("%s", err->message); + g_error_free(err); + return -1; + } + + int pipe_rw[2]; + if (pipe(pipe_rw) != 0) { + wlr_log(WLR_ERROR, "unable to pipe()"); + g_strfreev(argv); + return -1; + } + + pid_t child = 0; + child = fork(); + if (child < 0) { + wlr_log(WLR_ERROR, "unable to fork() child"); + close(pipe_rw[0]); + close(pipe_rw[1]); + g_strfreev(argv); + return child; + } + + if (child == 0) { + /* Child */ + reset_signals_and_limits(); + + /* + * replace stdout and stderr with /dev/null + * and stdin with the read end of the pipe + */ + close(pipe_rw[1]); + dup2(pipe_rw[0], STDIN_FILENO); + close(pipe_rw[0]); + + int dev_null = open("/dev/null", O_WRONLY); + if (dev_null < 0) { + wlr_log_errno(WLR_ERROR, "opening /dev/null failed"); + /* + * Just close stdout and stderr and + * hope $command can deal with that. + */ + close(STDOUT_FILENO); + close(STDERR_FILENO); + } else { + dup2(dev_null, STDOUT_FILENO); + dup2(dev_null, STDERR_FILENO); + close(dev_null); + } + execvp(argv[0], argv); + _exit(1); + } + + /* labwc */ + close(pipe_rw[0]); + g_strfreev(argv); + + /* + * Prevent blocking of the labwc process when + * writing more than the pipe buffer can hold. + */ + set_nonblock(pipe_rw[1]); + + /* + * Prevent leaking the write end of the pipe to further + * children forked during the lifetime of the descriptor. + */ + set_cloexec(pipe_rw[1]); + *pipe_fd_w = pipe_rw[1]; + + return child; +} + +pid_t spawn_piped(const char *command, int *pipe_fd) { assert(command); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/common/string-helpers.c new/labwc-0.20.2/src/common/string-helpers.c --- old/labwc-0.20.1/src/common/string-helpers.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/common/string-helpers.c 2026-08-21 19:24:33.000000000 +0200 @@ -57,6 +57,8 @@ char * strdup_printf(const char *fmt, ...) { + assert(fmt); + size_t size = 0; char *p = NULL; va_list ap; @@ -84,7 +86,7 @@ } char * -str_join(const char *const parts[], const char *fmt, const char *sep) +str_join(const char * const parts[], const char *fmt, const char *sep) { assert(parts); @@ -102,7 +104,7 @@ size_t sep_len = strlen(sep); /* Count the length of each formatted string */ - for (const char *const *s = parts; *s; ++s) { + for (const char * const *s = parts; *s; ++s) { int n = snprintf(NULL, 0, fmt, *s); if (n < 0) { return NULL; @@ -121,7 +123,7 @@ /* Concatenate the strings and separators */ char *buf = xzalloc(size); char *p = buf; - for (const char *const *s = parts; *s; ++s) { + for (const char * const *s = parts; *s; ++s) { int n = 0; if (p != buf) { @@ -152,7 +154,7 @@ } static bool -_str_endswith(const char *const string, const char *const suffix, uint32_t flags) +_str_endswith(const char * const string, const char * const suffix, uint32_t flags) { size_t len_str = string ? strlen(string) : 0; size_t len_sfx = suffix ? strlen(suffix) : 0; @@ -173,13 +175,13 @@ } bool -str_endswith(const char *const string, const char *const suffix) +str_endswith(const char * const string, const char * const suffix) { return _str_endswith(string, suffix, STR_FLAG_NONE); } bool -str_endswith_ignore_case(const char *const string, const char *const suffix) +str_endswith_ignore_case(const char * const string, const char * const suffix) { return _str_endswith(string, suffix, STR_FLAG_IGNORE_CASE); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/config/keybind.c new/labwc-0.20.2/src/config/keybind.c --- old/labwc-0.20.1/src/config/keybind.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/config/keybind.c 2026-08-21 19:24:33.000000000 +0200 @@ -161,7 +161,7 @@ * "-". In order to avoid such duplications, we perform * a lookahead on the tokens to treat that edge-case. */ - if (symnames[i+1] && !symnames[i+1][0]) { + if (symnames[i + 1] && !symnames[i + 1][0]) { continue; } symname = "-"; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/config/rcxml.c new/labwc-0.20.2/src/config/rcxml.c --- old/labwc-0.20.1/src/config/rcxml.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/config/rcxml.c 2026-08-21 19:24:33.000000000 +0200 @@ -17,6 +17,7 @@ #include "common/list.h" #include "common/macros.h" #include "common/mem.h" +#include "common/nag.h" #include "common/nodename.h" #include "common/parse-bool.h" #include "common/parse-double.h" @@ -1167,6 +1168,8 @@ } else if (!strcasecmp(nodename, "promptCommand.core")) { xstrdup_replace(rc.prompt_command, content); + } else if (!strcasecmp(nodename, "errorCommand.core")) { + xstrdup_replace(rc.error_command, content); } else if (!strcmp(nodename, "policy.placement")) { enum lab_placement_policy policy = view_placement_parse(content); @@ -1476,7 +1479,7 @@ int options = 0; xmlDoc *d = xmlReadMemory(b->data, b->len, NULL, NULL, options); if (!d) { - wlr_log(WLR_ERROR, "error parsing config file"); + nag_log(WLR_ERROR, "error parsing config file"); return; } xmlNode *root = xmlDocGetRootElement(d); @@ -1802,6 +1805,15 @@ "--layer overlay " "--timeout 0"); } + if (!rc.error_command) { + rc.error_command = + xstrdup("labnag " + "--message 'Config errors detected' " + "--button-dismiss 'Close' " + "--layer overlay " + "--timeout 0 " + "--detailed-message"); + } if (!rc.fallback_app_icon_name) { rc.fallback_app_icon_name = xstrdup("labwc"); } @@ -2030,7 +2042,7 @@ continue; } - wlr_log(WLR_INFO, "read config file %s", path->string); + nag_log(WLR_INFO, "read config file %s", path->string); rcxml_parse_xml(&b); buf_reset(&b); @@ -2052,6 +2064,7 @@ zfree(rc.font_menuitem.name); zfree(rc.font_osd.name); zfree(rc.prompt_command); + zfree(rc.error_command); zfree(rc.theme_name); zfree(rc.icon_theme_name); zfree(rc.fallback_app_icon_name); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/config/session.c new/labwc-0.20.2/src/config/session.c --- old/labwc-0.20.1/src/config/session.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/config/session.c 2026-08-21 19:24:33.000000000 +0200 @@ -26,7 +26,7 @@ #define wlr_backend_is_drm(backend) (false) #endif -static const char *const env_vars[] = { +static const char * const env_vars[] = { "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP", "XCURSOR_SIZE", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/cycle/cycle.c new/labwc-0.20.2/src/cycle/cycle.c --- old/labwc-0.20.1/src/cycle/cycle.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/cycle/cycle.c 2026-08-21 19:24:33.000000000 +0200 @@ -204,7 +204,7 @@ struct view *selected_view = server.cycle.selected_view; destroy_cycle(); - seat_focus_override_end(&server.seat, /*restore_focus*/ false); + seat_focus_override_end(&server.seat, /*restore_focus*/ !switch_focus); /* Hiding OSD may need a cursor change */ cursor_update_focus(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/img/img.c new/labwc-0.20.2/src/img/img.c --- old/labwc-0.20.1/src/img/img.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/img/img.c 2026-08-21 19:24:33.000000000 +0200 @@ -105,7 +105,7 @@ } void -lab_img_add_modifier(struct lab_img *img, lab_img_modifier_func_t modifier) +lab_img_add_modifier(struct lab_img *img, lab_img_modifier_func_t modifier) { lab_img_modifier_func_t *mod = wl_array_add(&img->modifiers, sizeof(*mod)); *mod = modifier; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/input/tablet.c new/labwc-0.20.2/src/input/tablet.c --- old/labwc-0.20.1/src/input/tablet.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/input/tablet.c 2026-08-21 19:24:33.000000000 +0200 @@ -199,7 +199,7 @@ *dy = *dy * motion_sensitivity; } -static struct wlr_surface* +static struct wlr_surface * tablet_get_coords(struct drawing_tablet *tablet, struct drawing_tablet_tool *tool, double *x, double *y, double *dx, double *dy) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/input/touch.c new/labwc-0.20.2/src/input/touch.c --- old/labwc-0.20.1/src/input/touch.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/input/touch.c 2026-08-21 19:24:33.000000000 +0200 @@ -27,7 +27,7 @@ struct wl_list link; /* seat.touch_points */ }; -static struct wlr_surface* +static struct wlr_surface * touch_get_coords(struct seat *seat, struct wlr_touch *touch, double x, double y, double *x_offset, double *y_offset) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/interactive.c new/labwc-0.20.2/src/interactive.c --- old/labwc-0.20.1/src/interactive.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/interactive.c 2026-08-21 19:24:33.000000000 +0200 @@ -267,7 +267,7 @@ } else if (right < rc.snap_edge_corner_range) { *edge2 = LAB_EDGE_RIGHT; } - } else if (*edge1 == LAB_EDGE_LEFT || *edge1 == LAB_EDGE_RIGHT) { + } else if (*edge1 == LAB_EDGE_LEFT || *edge1 == LAB_EDGE_RIGHT) { if (top < rc.snap_edge_corner_range) { *edge2 = LAB_EDGE_TOP; } else if (bottom < rc.snap_edge_corner_range) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/output.c new/labwc-0.20.2/src/output.c --- old/labwc-0.20.1/src/output.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/output.c 2026-08-21 19:24:33.000000000 +0200 @@ -85,7 +85,8 @@ }; static bool -output_set_render_format(struct output *output, uint32_t candidates[], size_t count) +output_set_render_format(struct output *output, uint32_t candidates[], + size_t count, bool silent) { for (size_t i = 0; i < count; i++) { wlr_output_state_set_render_format(&output->pending, candidates[i]); @@ -93,6 +94,16 @@ return true; } } + if (!silent) { + wlr_log(WLR_DEBUG, "output %s supports none of the %zu candidate" + " render formats", output->wlr_output->name, count); + } + /* + * Leaving the last candidate tried on the pending state means no + * swapchain can be created for this output at all, so clear the flag + * and leave the format alone instead. + */ + output->pending.committed &= ~WLR_OUTPUT_STATE_RENDER_FORMAT; return false; } @@ -174,14 +185,14 @@ hdr_succeeded = true; } else if (render_bit_depth == LAB_RENDER_BIT_DEPTH_10) { hdr_succeeded = output_set_render_format(output, output_formats_10bit, - ARRAY_SIZE(output_formats_10bit)); + ARRAY_SIZE(output_formats_10bit), silent); if (!hdr_succeeded) { if (!silent) { wlr_log(WLR_INFO, "No 10 bit color formats" " supported, HDR disabled."); } if (!output_set_render_format(output, output_formats_8bit, - ARRAY_SIZE(output_formats_8bit))) { + ARRAY_SIZE(output_formats_8bit), silent)) { if (!silent) { wlr_log(WLR_ERROR, "No 8 bit color formats" " supported either!"); @@ -190,7 +201,7 @@ } } else { if (!output_set_render_format(output, output_formats_8bit, - ARRAY_SIZE(output_formats_8bit)) && !silent) { + ARRAY_SIZE(output_formats_8bit), silent) && !silent) { wlr_log(WLR_ERROR, "No 8 bit color formats supported!"); } } @@ -280,6 +291,21 @@ if (seat->overlay.active.output == output) { overlay_finish(seat); } + + /* + * Destroy the scene output before changing the scene graph below. + * Otherwise those changes can send a surface-enter for this output while + * its destroy signal is already being emitted. The new listener then + * misses that signal and makes wlr_output_finish() abort because + * output->events.bind is not empty. + * + * See https://gitlab.freedesktop.org/wlroots/wlroots/-/issues/4096 + */ + if (output->scene_output) { + wlr_scene_output_destroy(output->scene_output); + output->scene_output = NULL; + } + wl_list_remove(&output->link); wl_list_remove(&output->frame.link); wl_list_remove(&output->destroy.link); @@ -327,10 +353,6 @@ wl_display_terminate(server.wl_display); } - /* - * output->scene_output (if still around at this point) is - * destroyed automatically when the wlr_output is destroyed - */ free(output); } @@ -394,9 +416,9 @@ return; } /* - * Note: wlr_scene_output_layout_add_output() is not - * safe to call twice, so we call it only when initially - * creating the scene_output. + * Note: wlr_scene_output_layout_add_output() is safe to + * call multiple times, but we only need to add the output + * when initially creating the scene_output. */ wlr_scene_output_layout_add_output(server.scene_layout, layout_output, output->scene_output); @@ -530,6 +552,30 @@ server.pending_output_layout_change--; /* + * Force the initial modeset commit to actually happen. + * + * lab_wlr_scene_output_commit() skips the real commit whenever the + * scene reports neither damage nor needs_frame. But on this very + * first commit the output has not been modeset yet, so its resolution + * is still 0x0: scene damage is clipped against that empty area and + * can never accumulate, and needs_frame is never raised on its own. + * As a result the enabled=true state set above would never be + * committed and the output would stay black until some unrelated + * event (VT switch, hotplug, display-manager restart) happens to + * raise needs_frame. + * + * With hardware cursors this is masked, because uploading the cursor + * image raises needs_frame as a side effect. With + * WLR_NO_HARDWARE_CURSORS=1 (software cursors) that path is gone and + * the missing initial commit becomes reliably reproducible, e.g. + * under QEMU/virtio-gpu. + * + * Schedule a frame here so needs_frame is raised and this enable is + * always committed, independent of the cursor mode. + */ + wlr_output_schedule_frame(wlr_output); + + /* * Commit the output this way instead, HDR needs a buffer, and * this commit must be called after the output is added to the * layout above. @@ -869,12 +915,9 @@ server.workspaces.ext_group, output->wlr_output); /* - * At time of writing, wlr_output_layout_remove() - * indirectly destroys the wlr_scene_output, but - * this behavior may change in future. To remove - * doubt and avoid either a leak or double-free, - * explicitly destroy the wlr_scene_output before - * calling wlr_output_layout_remove(). + * wlr_output_layout_remove() only unbinds the + * wlr_scene_output from the layout; it does not + * destroy it. Destroy explicitly to avoid a leak. */ wlr_scene_output_destroy(output->scene_output); wlr_output_layout_remove(server.output_layout, o); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/server.c new/labwc-0.20.2/src/server.c --- old/labwc-0.20.1/src/server.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/server.c 2026-08-21 19:24:33.000000000 +0200 @@ -54,6 +54,7 @@ #include "action.h" #include "common/macros.h" #include "common/mem.h" +#include "common/nag.h" #include "common/scene-helpers.h" #include "config/rcxml.h" #include "config/session.h" @@ -98,6 +99,7 @@ scaled_buffer_invalidate_sharing(); rcxml_finish(); + nag_reset(); rcxml_read(rc.config_file); theme_finish(rc.theme); theme_init(rc.theme, rc.theme_name); @@ -119,6 +121,8 @@ resize_indicator_reconfigure(); kde_server_decoration_update_default(); workspaces_reconfigure(); + + wl_event_loop_add_idle(server.wl_event_loop, nag_show_callback, NULL); } static int @@ -146,62 +150,90 @@ siginfo_t info; info.si_pid = 0; - /* First call waitid() with NOWAIT which doesn't consume the zombie */ - if (waitid(P_ALL, /*id*/ 0, &info, WEXITED | WNOHANG | WNOWAIT) == -1) { - return 0; - } + /* + * We may get a single SIGCHILD event although there are + * multiple childs terminating so we loop through them. + * + * A 'while true; labwc --reconfigure; done' loop along + * with some config error which causes labnag to re-spawn + * constantly is a good test for this handler. + */ - if (info.si_pid == 0) { - /* No children in waitable state */ - return 0; - } + /* First call waitid() with NOWAIT which doesn't consume the zombie. */ + while (waitid(P_ALL, /*id*/ 0, &info, WEXITED | WNOHANG | WNOWAIT) != -1) { + if (info.si_pid == 0) { + /* No children in waitable state */ + return 0; + } -#if HAVE_XWAYLAND - /* Ensure that we do not break xwayland lazy initialization */ - if (server.xwayland && server.xwayland->server - && info.si_pid == server.xwayland->server->pid) { - return 0; - } -#endif + #if HAVE_XWAYLAND + /* Ensure that we do not break xwayland lazy initialization */ + if (server.xwayland && server.xwayland->server + && info.si_pid == server.xwayland->server->pid) { + /* + * We need to completely return here to prevent running + * into an endless loop without giving the wlroots internal + * xwayland startup handler a chance to use its own waitid(). + * + * Further queued up child process terminatations will be + * dealt with on the next SIGCHLD handler invocation and + * float around as zombies until that point. + */ + return 0; + } + #endif - /* And then do the actual (consuming) lookup again */ - int ret = waitid(P_PID, info.si_pid, &info, WEXITED); - if (ret == -1) { - wlr_log(WLR_ERROR, "blocking waitid() for %ld failed: %d", - (long)info.si_pid, ret); - return 0; - } + /* And then do the actual (consuming) lookup again */ + int ret = waitid(P_PID, info.si_pid, &info, WEXITED); + if (ret == -1) { + wlr_log(WLR_ERROR, "blocking waitid() for %ld failed: %d", + (long)info.si_pid, ret); + goto check_next; + } - const char *signame; - switch (info.si_code) { - case CLD_EXITED: - if (!action_check_prompt_result(info.si_pid, info.si_status)) { - wlr_log(info.si_status == 0 ? WLR_DEBUG : WLR_ERROR, - "spawned child %ld exited with %d", - (long)info.si_pid, info.si_status); + const char *signame; + switch (info.si_code) { + case CLD_EXITED: + if (!action_check_prompt_result(info.si_pid, info.si_status) + && !nag_check_pid(info.si_pid)) { + wlr_log(info.si_status == 0 ? WLR_DEBUG : WLR_ERROR, + "spawned child %ld exited with %d", + (long)info.si_pid, info.si_status); + } + break; + case CLD_KILLED: + case CLD_DUMPED: + signame = strsignal(info.si_status); + wlr_log(WLR_ERROR, + "spawned child %ld terminated with signal %d (%s)", + (long)info.si_pid, info.si_status, + signame ? signame : "unknown"); + /* Allow cleanup of killed prompt */ + action_check_prompt_result(info.si_pid, -info.si_status); + nag_check_pid(info.si_pid); + break; + default: + wlr_log(WLR_ERROR, + "spawned child %ld terminated unexpectedly: %d" + " please report", (long)info.si_pid, info.si_code); } - break; - case CLD_KILLED: - case CLD_DUMPED: - signame = strsignal(info.si_status); - wlr_log(WLR_ERROR, - "spawned child %ld terminated with signal %d (%s)", - (long)info.si_pid, info.si_status, - signame ? signame : "unknown"); - /* Allow cleanup of killed prompt */ - action_check_prompt_result(info.si_pid, -info.si_status); - break; - default: - wlr_log(WLR_ERROR, - "spawned child %ld terminated unexpectedly: %d" - " please report", (long)info.si_pid, info.si_code); - } - - if (info.si_pid == server.primary_client_pid) { - wlr_log(WLR_INFO, "primary client %ld exited", (long)info.si_pid); - wl_display_terminate(server.wl_display); - } + if (info.si_pid == server.primary_client_pid) { + wlr_log(WLR_INFO, "primary client %ld exited", (long)info.si_pid); + wl_display_terminate(server.wl_display); + } +check_next: + /* + * We need each initial WNOHANG | WNOWAIT call to clearly identify + * if there are no more children to wait for so we reset si_pid + * as recommended for portability in the waitid man page. + * + * Note that at least on one of the devs linux system the call + * actually returns with -1 in that case, contrary to what is + * written in the man page. We do handle both cases. + */ + info.si_pid = 0; + } return 0; } @@ -809,6 +841,7 @@ #if HAVE_XWAYLAND xwayland_server_init(server.compositor); #endif + wl_event_loop_add_idle(server.wl_event_loop, nag_show_callback, NULL); } void @@ -856,6 +889,7 @@ wl_display_destroy_clients(server.wl_display); + nag_finish(); seat_finish(); output_finish(); xdg_shell_finish(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/workspaces.c new/labwc-0.20.2/src/workspaces.c --- old/labwc-0.20.1/src/workspaces.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/workspaces.c 2026-08-21 19:24:33.000000000 +0200 @@ -115,7 +115,7 @@ if (!hide_boxes) { x = (width - marker_width) / 2; wl_list_for_each(workspace, &server.workspaces.all, link) { - bool active = workspace == server.workspaces.current; + bool active = workspace == server.workspaces.current; set_cairo_color(cairo, rc.theme->osd_label_text_color); struct wlr_fbox fbox = { .x = x, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/labwc-0.20.1/src/xwayland.c new/labwc-0.20.2/src/xwayland.c --- old/labwc-0.20.1/src/xwayland.c 2026-06-15 20:43:52.000000000 +0200 +++ new/labwc-0.20.2/src/xwayland.c 2026-08-21 19:24:33.000000000 +0200 @@ -590,15 +590,13 @@ struct view *view = &xwayland_view->base; /* - * Use the WM_CLASS 'instance' (1st string) for the app_id. Per - * ICCCM, this is usually "the trailing part of the name used to - * invoke the program (argv[0] stripped of any directory names)". - * - * In most cases, the 'class' (2nd string) is the same as the - * 'instance' except for being capitalized. We want lowercase - * here since we use the app_id for icon lookups. + * Use the WM_CLASS 'class' (2nd string) for the app_id. (Note + * that this is often capitalized, which must be accounted for + * in icon lookups.) We used to use the 'instance' (1st string) + * but this caused missing icons with Firefox, which has an + * instance of 'Navigator' for historical reasons. */ - view_set_app_id(view, xwayland_view->xwayland_surface->instance); + view_set_app_id(view, xwayland_view->xwayland_surface->class); } static void
