diff --git a/src/action.c b/src/action.c index faa7e73..3e6ac12 100644 --- a/src/action.c +++ b/src/action.c @@ -104,3 +104,9 @@ void toggle_client_maximize(const user_action_arg_t *arg) { client_set_maximize(wm.client_focused, !wm.client_focused->maximize); } + +void kill_client(const user_action_arg_t *arg) { + if (wm.client_focused) { + client_kill(wm.client_focused); + } +} diff --git a/src/action.h b/src/action.h index d20c367..0273998 100644 --- a/src/action.h +++ b/src/action.h @@ -60,3 +60,4 @@ void change_volume(const user_action_arg_t *arg); void toggle_client_floating(const user_action_arg_t *arg); void toggle_client_fullscreen(const user_action_arg_t *arg); void toggle_client_maximize(const user_action_arg_t *arg); +void kill_client(const user_action_arg_t *arg); diff --git a/src/atoms-list.txt b/src/atoms-list.txt index 1875eb9..1fc8d1b 100644 --- a/src/atoms-list.txt +++ b/src/atoms-list.txt @@ -1,5 +1,6 @@ _XKB_RULES_NAMES +WM_DELETE_WINDOW WM_PROTOCOLS WM_TAKE_FOCUS WM_NAME diff --git a/src/client.c b/src/client.c index a4125f4..0f92ed0 100644 --- a/src/client.c +++ b/src/client.c @@ -172,6 +172,17 @@ static void client_init_tag_by_wm_desktop(client_t *client) { } } +static void set_window_event_mask(xcb_window_t window, bool clean) { + xcb_cw_t change_mask = XCB_CW_EVENT_MASK; + uint32_t init_event_mask = + XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE | + XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY; + uint32_t event_mask = clean ? XCB_EVENT_MASK_NO_EVENT : init_event_mask; + xcb_params_cw_t params = {.event_mask = event_mask}; + xcb_aux_change_window_attributes(wm.xcb_conn, window, change_mask, ¶ms); +} + void client_manage(xcb_window_t window, xcb_get_geometry_reply_t *geometry_reply) { client_t *c = p_new(client_t, 1); @@ -184,14 +195,7 @@ void client_manage(xcb_window_t window, c->old_border_width = geometry_reply->border_width; { - const xcb_params_cw_t params = { - .event_mask = XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_FOCUS_CHANGE | - XCB_EVENT_MASK_PROPERTY_CHANGE | - XCB_EVENT_MASK_STRUCTURE_NOTIFY | - XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, - }; - xcb_aux_change_window_attributes(wm.xcb_conn, window, XCB_CW_EVENT_MASK, - ¶ms); + set_window_event_mask(window, false); xcb_map_window(wm.xcb_conn, window); } client_update_names(c); @@ -238,6 +242,7 @@ void client_manage(xcb_window_t window, xcb_change_property(wm.xcb_conn, XCB_PROP_MODE_APPEND, wm.screen->root, _NET_CLIENT_LIST, XCB_ATOM_WINDOW, 32, 1, &window); + xwindow_set_state(window, XCB_ICCCM_WM_STATE_NORMAL); monitor_arrange(c->monitor); monitor_draw_bar(c->monitor); @@ -477,7 +482,13 @@ static void update_client_list(void) { } } -void client_unmanage(client_t *client) { +void client_kill(client_t *client) { + if (!xwindow_send_event(client->window, WM_DELETE_WINDOW)) { + xwindow_kill_window(client->window); + } +} + +void client_unmanage(client_t *client, bool destroyed) { monitor_t *m = client->monitor; client_detach_list(client); @@ -487,6 +498,13 @@ void client_unmanage(client_t *client) { if (tag->mask & client->tags) client_remove_from_tag(client, tag); } + if (!destroyed) { + set_window_event_mask(client->window, true); + client_change_border_width(client, client->old_border_width); + xwindow_set_state(client->window, XCB_ICCCM_WM_STATE_WITHDRAWN); + xcb_flush(wm.xcb_conn); + } + client_wipe(client); update_client_list(); diff --git a/src/client.h b/src/client.h index c77a437..19d7c27 100644 --- a/src/client.h +++ b/src/client.h @@ -50,7 +50,7 @@ void client_set_transient_for_window(client_t *client, void client_set_leader_window(client_t *client, xcb_window_t leader_window); void client_kill(client_t *client); -void client_unmanage(client_t *client); +void client_unmanage(client_t *client, bool destroyed); void client_apply_geometry(client_t *client, area_t geometry); void client_apply_workarea_geometry(client_t *client, area_t geometry); void client_apply_rules(client_t *client, const rule_t rules[], diff --git a/src/default_config.h b/src/default_config.h index bc73600..421b195 100644 --- a/src/default_config.h +++ b/src/default_config.h @@ -99,6 +99,7 @@ static const keyboard_t key_list[] = { {modifier_super | modifier_control, XK_space, toggle_client_floating, {0}}, {modifier_super, XK_f, toggle_client_fullscreen, {0}}, {modifier_super, XK_m, toggle_client_maximize, {0}}, + {modifier_super | modifier_shift, XK_c, kill_client, {0}}, {modifier_super, XK_Up, change_volume, {.i = 1}}, {modifier_super, XK_Down, change_volume, {.i = -1}}, diff --git a/src/event.c b/src/event.c index ddba3e0..297aaf7 100644 --- a/src/event.c +++ b/src/event.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -201,12 +202,18 @@ static void property_notify(xcb_property_notify_event_t *ev) { static void destroy_notify(xcb_destroy_notify_event_t *ev) { client_t *client = client_get_by_window(ev->window); - if (client) client_unmanage(client); + if (client) client_unmanage(client, true); } static void unmap_notify(xcb_unmap_notify_event_t *ev) { client_t *client = client_get_by_window(ev->window); - if (client) client_unmanage(client); + if (client) { + if (XCB_EVENT_SENT(ev)) { + xwindow_set_state(ev->window, XCB_ICCCM_WM_STATE_WITHDRAWN); + } else { + client_unmanage(client, false); + } + } } static void map_notify(xcb_map_notify_event_t *ev) { diff --git a/src/wm.c b/src/wm.c index 8c47380..84ec9b8 100644 --- a/src/wm.c +++ b/src/wm.c @@ -314,6 +314,16 @@ void wm_scan_clients(void) { void wm_clean(void) { clean_status(); text_clean_pango_layout(); + + { + client_t *c = wm.client_stack_list; + while (c) { + client_t *next_client = c->stack_next; + client_unmanage(c, false); + c = next_client; + } + } + monitor_clean(wm.monitor_list); for (int i = 0; i < countof(sources); i++) { @@ -321,7 +331,6 @@ void wm_clean(void) { if (source_id) g_source_remove(source_id); } - xcb_delete_property(wm.xcb_conn, wm.screen->root, _NET_CLIENT_LIST); xcb_delete_property(wm.xcb_conn, wm.screen->root, _NET_ACTIVE_WINDOW); xcb_delete_property(wm.xcb_conn, wm.screen->root, _NET_SUPPORTING_WM_CHECK); xcb_aux_sync(wm.xcb_conn); diff --git a/src/xwindow.c b/src/xwindow.c index 607bcc7..ed5b1ca 100644 --- a/src/xwindow.c +++ b/src/xwindow.c @@ -228,6 +228,14 @@ bool xwindow_get_wm_desktop(xcb_window_t window, uint32_t *desktop) { return success; } +void xwindow_kill_window(xcb_window_t window) { + xcb_grab_server(wm.xcb_conn); + xcb_set_close_down_mode(wm.xcb_conn, XCB_CLOSE_DOWN_DESTROY_ALL); + xcb_kill_client(wm.xcb_conn, window); + xcb_flush(wm.xcb_conn); + xcb_ungrab_server(wm.xcb_conn); +} + xcb_window_t xwindow_get_transient_for(xcb_window_t window) { xcb_connection_t *conn = wm.xcb_conn; xcb_window_t t_window = XCB_WINDOW_NONE; @@ -250,6 +258,23 @@ int32_t xwindow_get_state(xcb_window_t window) { return hints.initial_state; } +void xwindow_set_state(xcb_window_t window, xcb_icccm_wm_state_t state) { + xcb_icccm_wm_hints_t hints; + xcb_icccm_wm_hints_set_none(&hints); + switch (state) { + case XCB_ICCCM_WM_STATE_WITHDRAWN: + xcb_icccm_wm_hints_set_withdrawn(&hints); + break; + case XCB_ICCCM_WM_STATE_NORMAL: + xcb_icccm_wm_hints_set_normal(&hints); + break; + case XCB_ICCCM_WM_STATE_ICONIC: + xcb_icccm_wm_hints_set_iconic(&hints); + break; + } + xcb_icccm_set_wm_hints(wm.xcb_conn, window, &hints); +} + xcb_get_geometry_reply_t *xwindow_get_geometry_reply(xcb_window_t window) { xcb_get_geometry_cookie_t cookie = xcb_get_geometry(wm.xcb_conn, window); return xcb_get_geometry_reply(wm.xcb_conn, cookie, nullptr); diff --git a/src/xwindow.h b/src/xwindow.h index 9f70e41..82a1b7e 100644 --- a/src/xwindow.h +++ b/src/xwindow.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "action.h" @@ -22,9 +23,11 @@ void xwindow_get_text_property(xcb_window_t window, xcb_atom_t property, char **out); void xwindow_set_wm_desktop(xcb_window_t window, uint32_t desktop); bool xwindow_get_wm_desktop(xcb_window_t window, uint32_t *desktop); +void xwindow_kill_window(xcb_window_t window); xcb_window_t xwindow_get_transient_for(xcb_window_t window); int32_t xwindow_get_state(xcb_window_t window); +void xwindow_set_state(xcb_window_t window, xcb_icccm_wm_state_t state); xcb_get_geometry_reply_t *xwindow_get_geometry_reply(xcb_window_t window); xcb_get_window_attributes_reply_t *xwindow_get_attributes_reply( xcb_window_t window);