diff --git a/NEWS.md b/NEWS.md
index 79a3c918..97e65de9 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -9,6 +9,8 @@ The format is based on [Keep a Changelog]
| Date | All Changes | wlroots version | lines-of-code |
|------------|---------------|-----------------|---------------|
+| 2026-04-17 | [0.9.7] | 0.19.2 | 29277 |
+| 2026-03-15 | [0.9.6] | 0.19.2 | 29271 |
| 2026-03-04 | [0.9.5] | 0.19.2 | 29251 |
| 2026-02-27 | [0.9.4] | 0.19.2 | 29225 |
| 2025-12-19 | [0.9.3] | 0.19.2 | 28968 |
@@ -41,7 +43,8 @@ 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]: NEWS.md#unreleased
+[0.9.7]: NEWS.md#097---2026-04-17
+[0.9.6]: NEWS.md#096---2026-03-15
[0.9.5]: NEWS.md#095---2026-03-04
[0.9.4]: NEWS.md#094---2026-02-27
[0.9.3]: NEWS.md#093---2025-12-19
@@ -108,9 +111,64 @@ There are some regression warnings worth noting for the switch to wlroots 0.19:
[wlroots-5098]:https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5098
[gtk-8792]: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/8792
-## unreleased
+## 0.9.7 - 2026-04-17
-[unreleased-commits]
+[0.9.7-commits]
+
+This is a small bug fix release.
+
+```
+ 0.9.6--------0.9.7 <--- v0.9 branch with bug-fixes only
+ /
+ /
+0.9.4--------0.9.5-------- <-- master (built with wlroots-0.20)
+```
+
+## Fixed
+
+- Fix intermittent failed-to-get-texture issue with some clients (e.g. foot)
+ when using the window-switcher in the thumbnail mode. [#3511] @yuiiio
+- Fix tablet tool tilt motion. [#3494] @jp7677
+- Handle window-switcher buffer allocation failure when in 'thumbnail' mode.
+ This is believed to be very unlikely to happen, but has been reported by one
+ user and is believed to be GPU driver related. [#3490] @Consolatis
+
+## 0.9.6 - 2026-03-15
+
+[0.9.6-commits]
+
+This is an earlier-than-usual release containing bug fixes only. It has been
+done on a separate branch (v0.9) to avoid the inclusion of refactoring and new
+features.
+
+```
+ 0.9.6 <--- bug-fixes only
+ /
+ /
+0.9.4--------0.9.5-------- <-- master
+```
+
+### Fixed
+
+- Disable outputs where all modes fail [#3428] [#3429] @Consolatis @kode54
+- Fix regression in `0.9.4` that causes `NextWindow` action to segfault when
+ no outputs are connected. This fixes a window-switcher crash with some
+ Nvidia GPUs/drivers after suspend [#3425] [#3430] @Consolatis
+- Fix typo to allow `xdg-dialog-v1` global [#3426] @xi
+
+### Changed
+
+- Disallow X11 window always-on-top requests by default to fix an issue whereby
+ Alt+Tab cannot be used to switch to other windows when using some XWayland
+ Wine games [#3441]. Add window-rule property `allowAlwaysOnTop` to optionally
+ allow this always-on-top requests. Add the snippet below to the `rc.xml` file
+ to restore the previous behaviour. [#3445] @Consolatis
+
+```
+
+
+
+```
## 0.9.5 - 2026-03-04
@@ -2607,7 +2665,8 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
ShowMenu
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
-[unreleased-commits]: https://github.com/labwc/labwc/compare/0.9.5...HEAD
+[0.9.7-commits]: https://github.com/labwc/labwc/compare/0.9.6...0.9.7
+[0.9.6-commits]: https://github.com/labwc/labwc/compare/0.9.5...0.9.6
[0.9.5-commits]: https://github.com/labwc/labwc/compare/0.9.4...0.9.5
[0.9.4-commits]: https://github.com/labwc/labwc/compare/0.9.3...0.9.4
[0.9.3-commits]: https://github.com/labwc/labwc/compare/0.9.2...0.9.3
@@ -3135,8 +3194,21 @@ Compile with wlroots 0.12.0 and wayland-server >=1.16
[#3365]: https://github.com/labwc/labwc/pull/3365
[#3372]: https://github.com/labwc/labwc/pull/3372
[#3373]: https://github.com/labwc/labwc/pull/3373
+[#3387]: https://github.com/labwc/labwc/pull/3387
[#3400]: https://github.com/labwc/labwc/pull/3400
[#3406]: https://github.com/labwc/labwc/pull/3406
[#3410]: https://github.com/labwc/labwc/pull/3410
[#3411]: https://github.com/labwc/labwc/pull/3411
[#3412]: https://github.com/labwc/labwc/pull/3412
+[#3425]: https://github.com/labwc/labwc/pull/3425
+[#3426]: https://github.com/labwc/labwc/pull/3426
+[#3428]: https://github.com/labwc/labwc/pull/3428
+[#3429]: https://github.com/labwc/labwc/pull/3429
+[#3430]: https://github.com/labwc/labwc/pull/3430
+[#3440]: https://github.com/labwc/labwc/pull/3440
+[#3441]: https://github.com/labwc/labwc/pull/3441
+[#3443]: https://github.com/labwc/labwc/pull/3443
+[#3445]: https://github.com/labwc/labwc/pull/3445
+[#3490]: https://github.com/labwc/labwc/pull/3490
+[#3494]: https://github.com/labwc/labwc/pull/3494
+[#3511]: https://github.com/labwc/labwc/pull/3511
diff --git a/docs/labwc-config.5.scd b/docs/labwc-config.5.scd
index e72354a3..03d5fc24 100644
--- a/docs/labwc-config.5.scd
+++ b/docs/labwc-config.5.scd
@@ -1354,6 +1354,13 @@ situation.
*ignoreConfigureRequest* prevents a X11 window to position and size
itself.
+** [yes|no|default]
+ *allowAlwaysOnTop* allows a X11 window to control its always-on-top
+ state ('above' in X11 terms).
+
+ Note: X11 window always-on-top requests are disallowed by default.
+ This window rule offers a means of allowing it.
+
** [yes|no|default]
*fixedPosition* disallows interactive move/resize and prevents
re-positioning in response to changes in reserved output space, which
@@ -1429,9 +1436,25 @@ situation.
## ENVIRONMENT VARIABLES
-*XCURSOR_THEME* and *XCURSOR_SIZE* are supported to set cursor theme
-and size respectively. The default size is 24. System cursor themes can
-typically be found with a command such as:
+*XCURSOR_PATH*
+ Specify a colon-separated list of paths to look for mouse cursors in.
+ Default
+ ~/.local/share/icons:
+ ~/.icons:
+ /usr/share/icons:
+ /usr/share/pixmaps:
+ ~/.cursors:
+ /usr/share/cursors/xorg-x11:
+ /usr/X11R6/lib/X11/icons:
+
+*XCURSOR_SIZE*
+ Specify an alternative mouse cursor size in pixels. Requires
+ XCURSOR_THEME to be set also. Default 24.
+
+*XCURSOR_THEME*
+ Specify a mouse cursor theme within XCURSOR_PATH.
+
+System cursor themes can typically be found with a command such as:
```
find /usr/share/icons/ -type d -name "cursors"
diff --git a/docs/labwc.1.scd b/docs/labwc.1.scd
index 4840c0fa..2dab30a5 100644
--- a/docs/labwc.1.scd
+++ b/docs/labwc.1.scd
@@ -141,7 +141,7 @@ example: *LABWC_DEBUG_FOO=1 labwc*.
*LABWC_DEBUG_KEY_STATE*
Enable logging of press and release events for bound keys (generally
- key-combinations like *Ctrl-Alt-t*)
+ key-combinations like *Ctrl-Alt-t*).
# SEE ALSO
diff --git a/include/window-rules.h b/include/window-rules.h
index 1bee4c09..624f3b85 100644
--- a/include/window-rules.h
+++ b/include/window-rules.h
@@ -40,6 +40,7 @@ struct window_rule {
enum property ignore_configure_request;
enum property fixed_position;
enum property icon_prefer_client;
+ enum property allow_always_on_top;
struct wl_list link; /* struct rcxml.window_rules */
};
diff --git a/meson.build b/meson.build
index ddb3d42b..63dd63c8 100644
--- a/meson.build
+++ b/meson.build
@@ -1,7 +1,7 @@
project(
'labwc',
'c',
- version: '0.9.5',
+ version: '0.9.7',
license: 'GPL-2.0-only',
meson_version: '>=0.59.0',
default_options: [
diff --git a/src/config/rcxml.c b/src/config/rcxml.c
index 95285776..45093781 100644
--- a/src/config/rcxml.c
+++ b/src/config/rcxml.c
@@ -300,6 +300,8 @@ fill_window_rule(xmlNode *node)
set_property(content, &window_rule->ignore_configure_request);
} else if (!strcasecmp(key, "fixedPosition")) {
set_property(content, &window_rule->fixed_position);
+ } else if (!strcasecmp(key, "allowAlwaysOnTop")) {
+ set_property(content, &window_rule->allow_always_on_top);
}
}
diff --git a/src/cycle/cycle.c b/src/cycle/cycle.c
index cec5cac5..8d1c7612 100644
--- a/src/cycle/cycle.c
+++ b/src/cycle/cycle.c
@@ -346,8 +346,10 @@ init_cycle(struct server *server, struct cycle_filter filter)
struct view *view;
for_each_view(view, &server->views, criteria) {
- if (!(cycle_outputs & view->output->id_bit)) {
- continue;
+ if (filter.output != CYCLE_OUTPUT_ALL) {
+ if (!view->output || !(cycle_outputs & view->output->id_bit)) {
+ continue;
+ }
}
if (cycle_app_id && strcmp(view->app_id, cycle_app_id) != 0) {
continue;
diff --git a/src/cycle/osd-thumbnail.c b/src/cycle/osd-thumbnail.c
index d5a0bf3e..464201bc 100644
--- a/src/cycle/osd-thumbnail.c
+++ b/src/cycle/osd-thumbnail.c
@@ -2,6 +2,7 @@
#include
#include
#include
+#include
#include
#include
#include "config/rcxml.h"
@@ -45,8 +46,12 @@ render_node(struct server *server, struct wlr_render_pass *pass,
if (!scene_buffer->buffer) {
break;
}
- struct wlr_texture *texture = wlr_texture_from_buffer(
- server->renderer, scene_buffer->buffer);
+ struct wlr_texture *texture = NULL;
+ struct wlr_client_buffer *client_buffer =
+ wlr_client_buffer_get(scene_buffer->buffer);
+ if (client_buffer) {
+ texture = client_buffer->texture;
+ }
if (!texture) {
break;
}
@@ -61,7 +66,6 @@ render_node(struct server *server, struct wlr_render_pass *pass,
},
.transform = scene_buffer->transform,
});
- wlr_texture_destroy(texture);
break;
}
case WLR_SCENE_NODE_RECT:
@@ -85,6 +89,10 @@ render_thumb(struct output *output, struct view *view)
struct wlr_buffer *buffer = wlr_allocator_create_buffer(server->allocator,
view->current.width, view->current.height,
&output->wlr_output->swapchain->format);
+ if (!buffer) {
+ wlr_log(WLR_ERROR, "failed to allocate buffer for thumbnail");
+ return NULL;
+ }
struct wlr_render_pass *pass = wlr_renderer_begin_buffer_pass(
server->renderer, buffer, NULL);
render_node(server, pass, &view->content_tree->node, 0, 0);
diff --git a/src/input/tablet.c b/src/input/tablet.c
index 319c06b3..bc4426d6 100644
--- a/src/input/tablet.c
+++ b/src/input/tablet.c
@@ -402,8 +402,6 @@ handle_tablet_tool_axis(struct wl_listener *listener, void *data)
*/
tool->dx = 0;
tool->dy = 0;
- tool->tilt_x = 0;
- tool->tilt_y = 0;
if (ev->updated_axes & WLR_TABLET_TOOL_AXIS_X) {
tool->x = ev->x;
diff --git a/src/output.c b/src/output.c
index f62dd87e..2e78ccaa 100644
--- a/src/output.c
+++ b/src/output.c
@@ -362,8 +362,10 @@ output_test_auto(struct wlr_output *wlr_output, struct wlr_output_state *state,
}
/* Reset mode if none worked (we may still try to commit) */
- wlr_output_state_set_mode(state, NULL);
- return false;
+ wlr_log(WLR_DEBUG, "no working fixed mode found for output %s", wlr_output->name);
+ state->committed &= ~WLR_OUTPUT_STATE_MODE;
+
+ return wlr_output_test_state(wlr_output, state);
}
static void
@@ -378,10 +380,8 @@ configure_new_output(struct server *server, struct output *output)
/* is_client_request */ false)) {
wlr_log(WLR_INFO, "mode test failed for output %s",
wlr_output->name);
- /*
- * Continue anyway. For some reason, the test fails when
- * running nested, yet the following commit succeeds.
- */
+ wlr_output_state_set_enabled(&output->pending, false);
+ return;
}
if (rc.adaptive_sync == LAB_ADAPTIVE_SYNC_ENABLED) {
diff --git a/src/server.c b/src/server.c
index 93d95503..b8abc633 100644
--- a/src/server.c
+++ b/src/server.c
@@ -277,7 +277,7 @@ allow_for_sandbox(const struct wlr_security_context_v1_state *security_state,
"zxdg_importer_v1",
"zxdg_importer_v2",
"xdg_toplevel_icon_manager_v1",
- "xdg_dialog_v1",
+ "xdg_wm_dialog_v1",
/* plus */
"wp_alpha_modifier_v1",
"wp_linux_drm_syncobj_manager_v1",
diff --git a/src/view.c b/src/view.c
index cb82ab22..ab9c67d5 100644
--- a/src/view.c
+++ b/src/view.c
@@ -1541,6 +1541,9 @@ void
view_set_layer(struct view *view, enum view_layer layer)
{
assert(view);
+ if (view->layer == layer) {
+ return;
+ }
view->layer = layer;
wlr_scene_node_reparent(&view->scene_tree->node,
view->workspace->view_trees[layer]);
@@ -2499,9 +2502,6 @@ view_destroy(struct view *view)
wl_list_remove(&view->set_title.link);
wl_list_remove(&view->destroy.link);
- zfree(view->title);
- zfree(view->app_id);
-
if (view->foreign_toplevel) {
foreign_toplevel_destroy(view->foreign_toplevel);
view->foreign_toplevel = NULL;
@@ -2556,6 +2556,9 @@ view_destroy(struct view *view)
assert(wl_list_empty(&view->events.set_icon.listener_list));
assert(wl_list_empty(&view->events.destroy.listener_list));
+ zfree(view->title);
+ zfree(view->app_id);
+
/* Remove view from server->views */
wl_list_remove(&view->link);
free(view);
diff --git a/src/window-rules.c b/src/window-rules.c
index 0b8f1101..bfeacfe7 100644
--- a/src/window-rules.c
+++ b/src/window-rules.c
@@ -110,6 +110,10 @@ window_rules_get_property(struct view *view, const char *property)
&& !strcasecmp(property, "iconPreferClient")) {
return rule->icon_prefer_client;
}
+ if (rule->allow_always_on_top
+ && !strcasecmp(property, "allowAlwaysOnTop")) {
+ return rule->allow_always_on_top;
+ }
}
}
return LAB_PROP_UNSPECIFIED;
diff --git a/src/xwayland.c b/src/xwayland.c
index 707ab390..fa3941d5 100644
--- a/src/xwayland.c
+++ b/src/xwayland.c
@@ -431,6 +431,11 @@ handle_request_above(struct wl_listener *listener, void *data)
wl_container_of(listener, xwayland_view, request_above);
struct view *view = &xwayland_view->base;
+ if (window_rules_get_property(view, "allowAlwaysOnTop") != LAB_PROP_TRUE) {
+ wlr_log(WLR_INFO, "X11 client side always on top request rejected");
+ return;
+ }
+
view_set_layer(view, xwayland_view->xwayland_surface->above
? VIEW_LAYER_ALWAYS_ON_TOP : VIEW_LAYER_NORMAL);
}
@@ -727,8 +732,11 @@ handle_map_request(struct wl_listener *listener, void *data)
axis |= VIEW_AXIS_VERTICAL;
}
view_maximize(view, axis);
- view_set_layer(view, xsurface->above
- ? VIEW_LAYER_ALWAYS_ON_TOP : VIEW_LAYER_NORMAL);
+
+ if (window_rules_get_property(view, "allowAlwaysOnTop") == LAB_PROP_TRUE) {
+ view_set_layer(view, xsurface->above
+ ? VIEW_LAYER_ALWAYS_ON_TOP : VIEW_LAYER_NORMAL);
+ }
/*
* We could also call set_initial_position() here, but it's not
* really necessary until the view is actually mapped (and at