From 4d0eaf14631a0ef429cba002e63acc15fcfebc32 Mon Sep 17 00:00:00 2001 From: Dave Davenport Date: Fri, 3 Sep 2021 13:53:43 +0200 Subject: [PATCH] [Window] Issue 1406 fix the broken close-on-delete (#1421) * [1406] Add watcher for new/removed windows and reload based on that. * [1406] Add a small timeout before reloading to avoid reloading several times in a row. * [1406] close rofi if you receive a destroy window on rofi. --- include/dialogs/window.h | 2 ++ source/dialogs/window.c | 70 ++++++++++++++++++++++++++++++++-------- source/xcb.c | 24 ++++++++++++++ 3 files changed, 82 insertions(+), 14 deletions(-) diff --git a/include/dialogs/window.h b/include/dialogs/window.h index c25c1368..145628b3 100644 --- a/include/dialogs/window.h +++ b/include/dialogs/window.h @@ -40,6 +40,8 @@ extern Mode window_mode; extern Mode window_mode_cd; + +void window_client_handle_signal(xcb_window_t win, gboolean create); #endif // WINDOW_MODE /** @}*/ #endif // ROFI_DIALOG_WINDOW_H diff --git a/source/dialogs/window.c b/source/dialogs/window.c index d1d9cd63..fd393172 100644 --- a/source/dialogs/window.c +++ b/source/dialogs/window.c @@ -147,7 +147,7 @@ typedef struct { unsigned int title_len; unsigned int role_len; GRegex *window_regex; -} ModeModePrivateData; +} WindowModePrivateData; winlist *cache_client = NULL; @@ -236,6 +236,9 @@ static void winlist_free(winlist *l) { * @returns -1 if failed, index is successful. */ static int winlist_find(winlist *l, xcb_window_t w) { + if (l == NULL) { + return -1; + } // iterate backwards. Theory is: windows most often accessed will be // nearer the end. Testing with kcachegrind seems to support this... int i; @@ -305,7 +308,7 @@ static int client_has_window_type(client *c, xcb_atom_t type) { return 0; } -static client *window_client(ModeModePrivateData *pd, xcb_window_t win) { +static client *window_client(WindowModePrivateData *pd, xcb_window_t win) { if (win == XCB_WINDOW_NONE) { return NULL; } @@ -378,9 +381,35 @@ static client *window_client(ModeModePrivateData *pd, xcb_window_t win) { g_free(attr); return c; } + +guint window_reload_timeout = 0; +static gboolean window_client_reload(G_GNUC_UNUSED void *data) { + window_reload_timeout = 0; + if (window_mode.private_data) { + window_mode._destroy(&window_mode); + window_mode._init(&window_mode); + } + if (window_mode_cd.private_data) { + window_mode._destroy(&window_mode_cd); + window_mode._init(&window_mode_cd); + } + if (window_mode.private_data || window_mode_cd.private_data) { + rofi_view_reload(); + } + return G_SOURCE_REMOVE; +} +void window_client_handle_signal(xcb_window_t win, gboolean create) { + // g_idle_add_full(G_PRIORITY_HIGH_IDLE, window_client_reload, NULL, NULL); + if (window_reload_timeout > 0) { + g_source_remove(window_reload_timeout); + window_reload_timeout = 0; + } + window_reload_timeout = g_timeout_add(100, window_client_reload, NULL); +} static int window_match(const Mode *sw, rofi_int_matcher **tokens, unsigned int index) { - ModeModePrivateData *rmpd = (ModeModePrivateData *)mode_get_private_data(sw); + WindowModePrivateData *rmpd = + (WindowModePrivateData *)mode_get_private_data(sw); int match = 1; const winlist *ids = (winlist *)rmpd->ids; // Want to pull directly out of cache, X calls are not thread safe. @@ -466,8 +495,8 @@ static void window_mode_parse_fields() { } static unsigned int window_mode_get_num_entries(const Mode *sw) { - const ModeModePrivateData *pd = - (const ModeModePrivateData *)mode_get_private_data(sw); + const WindowModePrivateData *pd = + (const WindowModePrivateData *)mode_get_private_data(sw); return pd->ids ? pd->ids->len : 0; } @@ -492,7 +521,8 @@ static const char *_window_name_list_entry(const char *str, uint32_t length, return &str[offset]; } static void _window_mode_load_data(Mode *sw, unsigned int cd) { - ModeModePrivateData *pd = (ModeModePrivateData *)mode_get_private_data(sw); + WindowModePrivateData *pd = + (WindowModePrivateData *)mode_get_private_data(sw); // find window list xcb_window_t curr_win_id; int found = 0; @@ -640,7 +670,7 @@ static void _window_mode_load_data(Mode *sw, unsigned int cd) { } static int window_mode_init(Mode *sw) { if (mode_get_private_data(sw) == NULL) { - ModeModePrivateData *pd = g_malloc0(sizeof(*pd)); + WindowModePrivateData *pd = g_malloc0(sizeof(*pd)); pd->window_regex = g_regex_new("{[-\\w]+(:-?[0-9]+)?}", 0, 0, NULL); mode_set_private_data(sw, (void *)pd); _window_mode_load_data(sw, FALSE); @@ -652,7 +682,7 @@ static int window_mode_init(Mode *sw) { } static int window_mode_init_cd(Mode *sw) { if (mode_get_private_data(sw) == NULL) { - ModeModePrivateData *pd = g_malloc0(sizeof(*pd)); + WindowModePrivateData *pd = g_malloc0(sizeof(*pd)); pd->window_regex = g_regex_new("{[-\\w]+(:-?[0-9]+)?}", 0, 0, NULL); mode_set_private_data(sw, (void *)pd); _window_mode_load_data(sw, TRUE); @@ -696,7 +726,8 @@ static inline int act_on_window(xcb_window_t window) { static ModeMode window_mode_result(Mode *sw, int mretv, G_GNUC_UNUSED char **input, unsigned int selected_line) { - ModeModePrivateData *rmpd = (ModeModePrivateData *)mode_get_private_data(sw); + WindowModePrivateData *rmpd = + (WindowModePrivateData *)mode_get_private_data(sw); ModeMode retv = MODE_EXIT; if ((mretv & (MENU_OK))) { if (mretv & MENU_CUSTOM_ACTION) { @@ -750,6 +781,13 @@ static ModeMode window_mode_result(Mode *sw, int mretv, &(xcb->ewmh), xcb->screen_nbr, rmpd->ids->array[selected_line], XCB_CURRENT_TIME, XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER); xcb_flush(xcb->connection); + ThemeWidget *wid = rofi_config_find_widget(sw->name, NULL, TRUE); + Property *p = + rofi_theme_find_property(wid, P_BOOLEAN, "close-on-delete", TRUE); + if (p && p->type == P_BOOLEAN && p->value.b == FALSE) { + + return RELOAD_DIALOG; + } } else if ((mretv & MENU_CUSTOM_INPUT) && *input != NULL && *input[0] != '\0') { GError *error = NULL; @@ -776,7 +814,8 @@ static ModeMode window_mode_result(Mode *sw, int mretv, } static void window_mode_destroy(Mode *sw) { - ModeModePrivateData *rmpd = (ModeModePrivateData *)mode_get_private_data(sw); + WindowModePrivateData *rmpd = + (WindowModePrivateData *)mode_get_private_data(sw); if (rmpd != NULL) { winlist_free(rmpd->ids); x11_cache_free(); @@ -787,7 +826,7 @@ static void window_mode_destroy(Mode *sw) { } } struct arg { - const ModeModePrivateData *pd; + const WindowModePrivateData *pd; client *c; }; @@ -852,7 +891,7 @@ static gboolean helper_eval_cb(const GMatchInfo *info, GString *str, } return FALSE; } -static char *_generate_display_string(const ModeModePrivateData *pd, +static char *_generate_display_string(const WindowModePrivateData *pd, client *c) { struct arg d = {pd, c}; char *res = g_regex_replace_eval(pd->window_regex, config.window_format, -1, @@ -863,7 +902,7 @@ static char *_generate_display_string(const ModeModePrivateData *pd, static char *_get_display_value(const Mode *sw, unsigned int selected_line, int *state, G_GNUC_UNUSED GList **list, int get_entry) { - ModeModePrivateData *rmpd = mode_get_private_data(sw); + WindowModePrivateData *rmpd = mode_get_private_data(sw); client *c = window_client(rmpd, rmpd->ids->array[selected_line]); if (c == NULL) { return get_entry ? g_strdup("Window has vanished") : NULL; @@ -980,8 +1019,11 @@ static cairo_surface_t *get_net_wm_icon(xcb_window_t xid, } static cairo_surface_t *_get_icon(const Mode *sw, unsigned int selected_line, int size) { - ModeModePrivateData *rmpd = mode_get_private_data(sw); + WindowModePrivateData *rmpd = mode_get_private_data(sw); client *c = window_client(rmpd, rmpd->ids->array[selected_line]); + if (c == NULL) { + return NULL; + } if (config.window_thumbnail && c->thumbnail_checked == FALSE) { c->icon = x11_helper_get_screenshot_surface_window(c->window, size); c->thumbnail_checked = TRUE; diff --git a/source/xcb.c b/source/xcb.c index 6a4ae45c..c2bf52b8 100644 --- a/source/xcb.c +++ b/source/xcb.c @@ -63,6 +63,9 @@ #include "xcb.h" #include +#include "dialogs/window.h" +#include "mode.h" + #include /** Minimal randr preferred for running rofi (1.5) (Major version number) */ @@ -1083,6 +1086,22 @@ static void main_loop_x11_event_handler_view(xcb_generic_event_t *event) { } break; } + case XCB_DESTROY_NOTIFY: { + xcb_window_t win = ((xcb_destroy_notify_event_t *)event)->window; + if (win != rofi_view_get_window()) { + window_client_handle_signal(win, FALSE); + } else { + g_main_loop_quit(xcb->main_loop); + } + break; + } + case XCB_CREATE_NOTIFY: { + xcb_window_t win = ((xcb_create_notify_event_t *)event)->window; + if (win != rofi_view_get_window()) { + window_client_handle_signal(win, TRUE); + } + break; + } case XCB_EXPOSE: rofi_view_frame_callback(); break; @@ -1484,6 +1503,11 @@ gboolean display_setup(GMainLoop *main_loop, NkBindings *bindings) { return FALSE; } + uint32_t val[] = {XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY}; + + xcb_change_window_attributes(xcb->connection, xcb_stuff_get_root_window(), + XCB_CW_EVENT_MASK, val); + // startup not. xcb->sndisplay = sn_xcb_display_new(xcb->connection, error_trap_push, error_trap_pop);