Fix focus transfer when clients unmap
116a58f91d9dc075d66586d58360a17229df7a71
1 parent
3ce65d3d
swm.c
+10 -4
| 1162 | 1162 | return; |
|
| 1163 | 1163 | } |
|
| 1164 | 1164 | wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel); |
|
| 1165 | 1165 | } |
|
| 1166 | 1166 | ||
| 1167 | - | /* Set the color of every client border. */ |
|
| 1167 | + | /* Set the color of every managed client border while its scene exists. */ |
|
| 1168 | 1168 | static inline void client_set_border_color(client_t *c, const float color[static 4]) { |
|
| 1169 | 1169 | int i; |
|
| 1170 | 1170 | ||
| 1171 | + | if (!c->scene || client_is_unmanaged(c)) |
|
| 1172 | + | return; |
|
| 1173 | + | ||
| 1171 | 1174 | for (i = 0; i < 4; i++) |
|
| 1172 | 1175 | wlr_scene_rect_set_color(c->border[i], color); |
|
| 1173 | 1176 | } |
|
| 1174 | 1177 | ||
| 1175 | 1178 | /* Notify a client of its fullscreen state. */ |
| 2954 | 2957 | return; |
|
| 2955 | 2958 | } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) { |
|
| 2956 | 2959 | return; |
|
| 2957 | 2960 | /* Don't deactivate old client if the new one wants focus, as this |
|
| 2958 | 2961 | * avoids breaking applications such as winecfg. */ |
|
| 2959 | - | } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) { |
|
| 2962 | + | } else if ( |
|
| 2963 | + | old_c && old_c->scene && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c)) |
|
| 2964 | + | ) { |
|
| 2960 | 2965 | client_set_border_color(old_c, bordercolor); |
|
| 2961 | 2966 | client_activate_surface(old, 0); |
|
| 2962 | 2967 | } |
|
| 2963 | 2968 | } |
|
| 2964 | 2969 | print_status(); |
| 5975 | 5980 | void unmap_notify(struct wl_listener *listener, void *data) { |
|
| 5976 | 5981 | /* Called when the surface is unmapped, and should no longer be shown. */ |
|
| 5977 | 5982 | client_t *c = wl_container_of(listener, c, unmap); |
|
| 5978 | 5983 | client_t *next = nullptr, *under = nullptr; |
|
| 5979 | 5984 | monitor_t *oldmon = c->mon; |
|
| 5985 | + | bool hadfocus = client_surface(c) == seat->keyboard_state.focused_surface; |
|
| 5980 | 5986 | int wasfocused = !client_is_unmanaged(c) && c == focus_top(c->mon); |
|
| 5981 | 5987 | ||
| 5982 | 5988 | if (c == grabc) { |
|
| 5983 | 5989 | if (cursor_mode == CURSOR_RESIZE) |
|
| 5984 | 5990 | client_set_resizing(c, 0); |
| 6000 | 6006 | next = focus_close(c); |
|
| 6001 | 6007 | wl_list_remove(&c->link); |
|
| 6002 | 6008 | wl_list_remove(&c->flink); |
|
| 6003 | 6009 | c->mon = nullptr; |
|
| 6004 | 6010 | ||
| 6005 | - | if (wasfocused && !sloppyfocus) |
|
| 6011 | + | if (hadfocus && !sloppyfocus) |
|
| 6006 | 6012 | focus_client(next ? next : focus_top(selmon), 1); |
|
| 6007 | 6013 | ||
| 6008 | 6014 | if (oldmon) |
|
| 6009 | 6015 | arrange(oldmon); |
|
| 6010 | 6016 | } |
| 6024 | 6030 | c->resize_edges = WLR_EDGE_NONE; |
|
| 6025 | 6031 | /* Hide the old scene before hit-testing, but keep its borders alive until |
|
| 6026 | 6032 | * focus_client() has finished deactivating the old surface. */ |
|
| 6027 | 6033 | wlr_scene_node_set_enabled(&c->scene->node, 0); |
|
| 6028 | 6034 | ||
| 6029 | - | if (wasfocused && sloppyfocus) { |
|
| 6035 | + | if (hadfocus && sloppyfocus) { |
|
| 6030 | 6036 | /* Hit-test after hiding the old scene node so focus follows the |
|
| 6031 | 6037 | * surface newly exposed beneath the stationary cursor. */ |
|
| 6032 | 6038 | point_to_node(cursor->x, cursor->y, nullptr, &under, nullptr, nullptr, nullptr); |
|
| 6033 | 6039 | ||
| 6034 | 6040 | if (under && !client_is_unmanaged(under)) |
test/clients.py
+3 -1
| 201 | 201 | window.destroy() |
|
| 202 | 202 | connection.close() |
|
| 203 | 203 | ||
| 204 | 204 | ||
| 205 | 205 | def xdg_lifecycle(arguments: list[str]) -> None: |
|
| 206 | - | """Repeatedly map and unmap one XDG toplevel.""" |
|
| 206 | + | """Repeatedly map and unmap a focused XDG toplevel.""" |
|
| 207 | 207 | ||
| 208 | 208 | count = int(arguments[0]) if arguments else 8 |
|
| 209 | 209 | connection = Connection(WlCompositor, WlShm, XdgWmBase) |
|
| 210 | + | fallback = Window(connection, "swm-xdg-fallback", 0xFF446622) |
|
| 210 | 211 | window = Window(connection, "swm-xdg-lifecycle", 0xFF224466) |
|
| 211 | 212 | connection.roundtrips(3) |
|
| 212 | 213 | for index in range(count): |
|
| 213 | 214 | window.unmap() |
|
| 214 | 215 | connection.roundtrips(2) |
|
| 215 | 216 | window.color += 0x00050311 |
|
| 216 | 217 | window.surface.commit() |
|
| 217 | 218 | connection.roundtrips(2) |
|
| 218 | 219 | window.destroy() |
|
| 220 | + | fallback.destroy() |
|
| 219 | 221 | connection.close() |
|
| 220 | 222 | ||
| 221 | 223 | ||
| 222 | 224 | def transient(arguments: list[str]) -> None: |
|
| 223 | 225 | """Exercise popup, parented, and fullscreen XDG surfaces.""" |
test/unit.c
+11 -3
| 207 | 207 | pool_release(&client_pool, client); |
|
| 208 | 208 | } |
|
| 209 | 209 | ||
| 210 | 210 | /* Verify state-file paths and client border-width policy. */ |
|
| 211 | 211 | static void test_paths_and_border_policy(void) { |
|
| 212 | - | const char *directory = getenv("SWM_TEST_DIR"); |
|
| 213 | - | client_t client = {}; |
|
| 214 | - | char path[PATH_MAX], expected[PATH_MAX]; |
|
| 212 | + | const char *directory = getenv("SWM_TEST_DIR"); |
|
| 213 | + | client_t client = {}; |
|
| 214 | + | struct wlr_scene_tree scene = {}; |
|
| 215 | + | struct wlr_xwayland_surface xsurface = { .override_redirect = true }; |
|
| 216 | + | char path[PATH_MAX], expected[PATH_MAX]; |
|
| 215 | 217 | ||
| 216 | 218 | assert(directory); |
|
| 217 | 219 | assert(snprintf(path, sizeof(path), "%s/state", directory) < (int)sizeof(path)); |
|
| 218 | 220 | assert(setenv("XDG_STATE_HOME", path, 1) == 0); |
|
| 219 | 221 | assert(snprintf(expected, sizeof(expected), "%s/swm/windows", path) < (int)sizeof(expected)); |
| 224 | 226 | assert( |
|
| 225 | 227 | snprintf(expected, sizeof(expected), "%s/.local/state/swm/windows", path) < |
|
| 226 | 228 | (int)sizeof(expected) |
|
| 227 | 229 | ); |
|
| 228 | 230 | assert(!strcmp(window_state_path(), expected)); |
|
| 231 | + | client_set_border_color(&client, bordercolor); |
|
| 232 | + | client.type = X11; |
|
| 233 | + | client.surface.xwayland = &xsurface; |
|
| 234 | + | client.scene = &scene; |
|
| 235 | + | client_set_border_color(&client, bordercolor); |
|
| 236 | + | client = (client_t){}; |
|
| 229 | 237 | client.bw = 9; |
|
| 230 | 238 | assert(client_border_width(&client) == borderwidth); |
|
| 231 | 239 | client.is_fullscreen = 1; |
|
| 232 | 240 | assert(client_border_width(&client) == 0); |
|
| 233 | 241 | } |