From d55f4b41c6ebf00e36a5e91ddc962a753f6c9ef8 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Mon, 14 Dec 2009 15:40:09 -0500 Subject: [PATCH 1/7] Allow the user to bind more than one keycode to a keysym for Ob Menus/Move/Resize If the user has escape bound to more than one keycode then they can use any of them to close a menu. This change applies to the hardcoded keys in openbox, which are used for the menus and for move/resize, and maybe other places. --- openbox/actions/cyclewindows.c | 4 +-- openbox/actions/directionalwindows.c | 4 +-- openbox/event.c | 12 ++++---- openbox/modkeys.c | 18 ++++++++---- openbox/modkeys.h | 5 ++-- openbox/moveresize.c | 44 ++++++++++++++-------------- openbox/openbox.c | 21 +++++++++++-- openbox/openbox.h | 2 +- openbox/prompt.c | 16 +++++----- 9 files changed, 74 insertions(+), 52 deletions(-) diff --git a/openbox/actions/cyclewindows.c b/openbox/actions/cyclewindows.c index 6fba17ff..32d6a453 100644 --- a/openbox/actions/cyclewindows.c +++ b/openbox/actions/cyclewindows.c @@ -146,13 +146,13 @@ static gboolean i_input_func(guint initial_state, { if (e->type == KeyPress) { /* Escape cancels no matter what */ - if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) { + if (ob_keycode_match(e->xkey.keycode, OB_KEY_ESCAPE)) { end_cycle(TRUE, e->xkey.state, options); return FALSE; } /* There were no modifiers and they pressed enter */ - else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN) && + else if (ob_keycode_match(e->xkey.keycode, OB_KEY_RETURN) && !initial_state) { end_cycle(FALSE, e->xkey.state, options); diff --git a/openbox/actions/directionalwindows.c b/openbox/actions/directionalwindows.c index d9f24d61..6559e44c 100644 --- a/openbox/actions/directionalwindows.c +++ b/openbox/actions/directionalwindows.c @@ -169,13 +169,13 @@ static gboolean i_input_func(guint initial_state, { if (e->type == KeyPress) { /* Escape cancels no matter what */ - if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) { + if (ob_keycode_match(e->xkey.keycode, OB_KEY_ESCAPE)) { end_cycle(TRUE, e->xkey.state, options); return FALSE; } /* There were no modifiers and they pressed enter */ - else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN) && + else if (ob_keycode_match(e->xkey.keycode, OB_KEY_RETURN) && !initial_state) { end_cycle(FALSE, e->xkey.state, options); diff --git a/openbox/event.c b/openbox/event.c index b4bd8270..881c9f8f 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -1693,30 +1693,30 @@ static gboolean event_handle_menu_keyboard(XEvent *ev) else if (ev->type == KeyPress && (state & ~ControlMask) == 0) { frame->got_press = TRUE; - if (keycode == ob_keycode(OB_KEY_ESCAPE)) { + if (ob_keycode_match(keycode, OB_KEY_ESCAPE)) { menu_frame_hide_all(); ret = TRUE; } - else if (keycode == ob_keycode(OB_KEY_LEFT)) { + else if (ob_keycode_match(keycode, OB_KEY_LEFT)) { /* Left goes to the parent menu */ if (frame->parent) menu_frame_select(frame, NULL, TRUE); ret = TRUE; } - else if (keycode == ob_keycode(OB_KEY_RIGHT)) { + else if (ob_keycode_match(keycode, OB_KEY_RIGHT)) { /* Right goes to the selected submenu */ if (frame->child) menu_frame_select_next(frame->child); ret = TRUE; } - else if (keycode == ob_keycode(OB_KEY_UP)) { + else if (ob_keycode_match(keycode, OB_KEY_UP)) { menu_frame_select_previous(frame); ret = TRUE; } - else if (keycode == ob_keycode(OB_KEY_DOWN)) { + else if (ob_keycode_match(keycode, OB_KEY_DOWN)) { menu_frame_select_next(frame); ret = TRUE; } @@ -1729,7 +1729,7 @@ static gboolean event_handle_menu_keyboard(XEvent *ev) else if (ev->type == KeyRelease && (state & ~ControlMask) == 0 && frame->entries && frame->got_press) { - if (keycode == ob_keycode(OB_KEY_RETURN)) { + if (ob_keycode_match(keycode, OB_KEY_RETURN)) { /* Enter runs the active item or goes into the submenu. Control-Enter runs it without closing the menu. */ if (frame->child) diff --git a/openbox/modkeys.c b/openbox/modkeys.c index c52cbef1..e897ccb3 100644 --- a/openbox/modkeys.c +++ b/openbox/modkeys.c @@ -176,14 +176,22 @@ static void set_modkey_mask(guchar mask, KeySym sym) /* CapsLock, Shift, and Control are special and hard-coded */ } -KeyCode modkeys_sym_to_code(KeySym sym) +KeyCode* modkeys_sym_to_code(KeySym sym) { - gint i, j; + KeyCode *ret; + gint i, j, n; + + ret = g_new(KeyCode, 1); + n = 0; + ret[n] = 0; /* go through each keycode and look for the keysym */ for (i = min_keycode; i <= max_keycode; ++i) for (j = 0; j < keysyms_per_keycode; ++j) - if (sym == keymap[(i-min_keycode) * keysyms_per_keycode + j]) - return i; - return 0; + if (sym == keymap[(i-min_keycode) * keysyms_per_keycode + j]) { + ret = g_renew(KeyCode, ret, ++n); + ret[n-1] = i; + ret[n] = 0; + } + return ret; } diff --git a/openbox/modkeys.h b/openbox/modkeys.h index dc72f28e..8e795f78 100644 --- a/openbox/modkeys.h +++ b/openbox/modkeys.h @@ -53,8 +53,7 @@ guint modkeys_only_modifier_masks(guint mask); right keys when there are both. */ guint modkeys_key_to_mask(ObModkeysKey key); -/*! Convert a KeySym to a KeyCode, because the X function is terrible - says - valgrind. */ -KeyCode modkeys_sym_to_code(KeySym sym); +/*! Convert a KeySym to all the KeyCodes which generate it. */ +KeyCode* modkeys_sym_to_code(KeySym sym); #endif diff --git a/openbox/moveresize.c b/openbox/moveresize.c index f324818d..95d64734 100644 --- a/openbox/moveresize.c +++ b/openbox/moveresize.c @@ -601,13 +601,13 @@ static void move_with_keys(gint keycode, gint state) gint x, y; ObDirection dir; - if (keycode == ob_keycode(OB_KEY_RIGHT)) + if (ob_keycode_match(keycode, OB_KEY_RIGHT)) dir = OB_DIRECTION_EAST; - else if (keycode == ob_keycode(OB_KEY_LEFT)) + else if (ob_keycode_match(keycode, OB_KEY_LEFT)) dir = OB_DIRECTION_WEST; - else if (keycode == ob_keycode(OB_KEY_DOWN)) + else if (ob_keycode_match(keycode, OB_KEY_DOWN)) dir = OB_DIRECTION_SOUTH; - else /* if (keycode == ob_keycode(OB_KEY_UP)) */ + else /* if (ob_keycode_match(keycode, OB_KEY_UP)) */ dir = OB_DIRECTION_NORTH; client_find_move_directional(moveresize_client, dir, &x, &y); @@ -620,13 +620,13 @@ static void move_with_keys(gint keycode, gint state) else dist = KEY_DIST; - if (keycode == ob_keycode(OB_KEY_RIGHT)) + if (ob_keycode_match(keycode, OB_KEY_RIGHT)) dx = dist; - else if (keycode == ob_keycode(OB_KEY_LEFT)) + else if (ob_keycode_match(keycode, OB_KEY_LEFT)) dx = -dist; - else if (keycode == ob_keycode(OB_KEY_DOWN)) + else if (ob_keycode_match(keycode, OB_KEY_DOWN)) dy = dist; - else /* if (keycode == ob_keycode(OB_KEY_UP)) */ + else /* if (ob_keycode_match(keycode, OB_KEY_UP)) */ dy = -dist; } @@ -659,7 +659,7 @@ static void resize_with_keys(gint keycode, gint state) ObDirection dir; /* pick the edge if it needs to move */ - if (keycode == ob_keycode(OB_KEY_RIGHT)) { + if (ob_keycode_match(keycode, OB_KEY_RIGHT)) { dir = OB_DIRECTION_EAST; if (key_resize_edge != OB_DIRECTION_WEST && key_resize_edge != OB_DIRECTION_EAST) @@ -668,7 +668,7 @@ static void resize_with_keys(gint keycode, gint state) return; } } - if (keycode == ob_keycode(OB_KEY_LEFT)) { + if (ob_keycode_match(keycode, OB_KEY_LEFT)) { dir = OB_DIRECTION_WEST; if (key_resize_edge != OB_DIRECTION_WEST && key_resize_edge != OB_DIRECTION_EAST) @@ -677,7 +677,7 @@ static void resize_with_keys(gint keycode, gint state) return; } } - if (keycode == ob_keycode(OB_KEY_UP)) { + if (ob_keycode_match(keycode, OB_KEY_UP)) { dir = OB_DIRECTION_NORTH; if (key_resize_edge != OB_DIRECTION_NORTH && key_resize_edge != OB_DIRECTION_SOUTH) @@ -686,7 +686,7 @@ static void resize_with_keys(gint keycode, gint state) return; } } - if (keycode == ob_keycode(OB_KEY_DOWN)) { + if (ob_keycode_match(keycode, OB_KEY_DOWN)) { dir = OB_DIRECTION_SOUTH; if (key_resize_edge != OB_DIRECTION_NORTH && key_resize_edge != OB_DIRECTION_SOUTH) @@ -700,13 +700,13 @@ static void resize_with_keys(gint keycode, gint state) if (state & modkeys_key_to_mask(OB_MODKEY_KEY_SHIFT)) { gint x, y, w, h; - if (keycode == ob_keycode(OB_KEY_RIGHT)) + if (ob_keycode_match(keycode, OB_KEY_RIGHT)) dir = OB_DIRECTION_EAST; - else if (keycode == ob_keycode(OB_KEY_LEFT)) + else if (ob_keycode_match(keycode, OB_KEY_LEFT)) dir = OB_DIRECTION_WEST; - else if (keycode == ob_keycode(OB_KEY_DOWN)) + else if (ob_keycode_match(keycode, OB_KEY_DOWN)) dir = OB_DIRECTION_SOUTH; - else /* if (keycode == ob_keycode(OB_KEY_UP)) */ + else /* if (ob_keycode_match(keycode, OB_KEY_UP)) */ dir = OB_DIRECTION_NORTH; client_find_resize_directional(moveresize_client, key_resize_edge, @@ -901,16 +901,16 @@ gboolean moveresize_event(XEvent *e) } used = TRUE; } else if (e->type == KeyPress) { - if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) { + if (ob_keycode_match(e->xkey.keycode, OB_KEY_ESCAPE)) { moveresize_end(TRUE); used = TRUE; - } else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN)) { + } else if (ob_keycode_match(e->xkey.keycode, OB_KEY_RETURN)) { moveresize_end(FALSE); used = TRUE; - } else if (e->xkey.keycode == ob_keycode(OB_KEY_RIGHT) || - e->xkey.keycode == ob_keycode(OB_KEY_LEFT) || - e->xkey.keycode == ob_keycode(OB_KEY_DOWN) || - e->xkey.keycode == ob_keycode(OB_KEY_UP)) + } else if (ob_keycode_match(e->xkey.keycode, OB_KEY_RIGHT) || + ob_keycode_match(e->xkey.keycode, OB_KEY_LEFT) || + ob_keycode_match(e->xkey.keycode, OB_KEY_DOWN) || + ob_keycode_match(e->xkey.keycode, OB_KEY_UP)) { if (corner == prop_atoms.net_wm_moveresize_size_keyboard) { resize_with_keys(e->xkey.keycode, e->xkey.state); diff --git a/openbox/openbox.c b/openbox/openbox.c index 60e147c3..0e339782 100644 --- a/openbox/openbox.c +++ b/openbox/openbox.c @@ -104,7 +104,7 @@ static gboolean reconfigure = FALSE; static gboolean restart = FALSE; static gchar *restart_path = NULL; static Cursor cursors[OB_NUM_CURSORS]; -static KeyCode keys[OB_NUM_KEYS]; +static KeyCode *keys[OB_NUM_KEYS]; static gint exitcode = 0; static guint remote_control = 0; static gboolean being_replaced = FALSE; @@ -417,6 +417,17 @@ gint main(gint argc, gchar **argv) event_shutdown(reconfigure); config_shutdown(); actions_shutdown(reconfigure); + + /* Free the key codes for built in keys */ + g_free(keys[OB_KEY_RETURN]); + g_free(keys[OB_KEY_ESCAPE]); + g_free(keys[OB_KEY_LEFT]); + g_free(keys[OB_KEY_RIGHT]); + g_free(keys[OB_KEY_UP]); + g_free(keys[OB_KEY_DOWN]); + g_free(keys[OB_KEY_TAB]); + g_free(keys[OB_KEY_SPACE]); + modkeys_shutdown(reconfigure); } while (reconfigure); } @@ -732,10 +743,14 @@ Cursor ob_cursor(ObCursor cursor) return cursors[cursor]; } -KeyCode ob_keycode(ObKey key) +gboolean ob_keycode_match(KeyCode code, ObKey key) { + KeyCode *k; + g_assert(key < OB_NUM_KEYS); - return keys[key]; + for (k = keys[key]; *k; ++k) + if (*k == code) return TRUE; + return FALSE; } ObState ob_state() diff --git a/openbox/openbox.h b/openbox/openbox.h index 06d38297..76b04ea2 100644 --- a/openbox/openbox.h +++ b/openbox/openbox.h @@ -65,6 +65,6 @@ void ob_exit_with_error(const gchar *msg); Cursor ob_cursor(ObCursor cursor); -KeyCode ob_keycode(ObKey key); +gboolean ob_keycode_match(KeyCode code, ObKey key); #endif diff --git a/openbox/prompt.c b/openbox/prompt.c index 53ed2d7d..bef06a74 100644 --- a/openbox/prompt.c +++ b/openbox/prompt.c @@ -532,23 +532,23 @@ gboolean prompt_key_event(ObPrompt *self, XEvent *e) if (e->xkey.state != 0 && e->xkey.state != shift_mask) return FALSE; - if (e->xkey.keycode == ob_keycode(OB_KEY_ESCAPE)) + if (ob_keycode_match(e->xkey.keycode, OB_KEY_ESCAPE)) prompt_cancel(self); - else if (e->xkey.keycode == ob_keycode(OB_KEY_RETURN) || - e->xkey.keycode == ob_keycode(OB_KEY_SPACE)) + else if (ob_keycode_match(e->xkey.keycode, OB_KEY_RETURN) || + ob_keycode_match(e->xkey.keycode, OB_KEY_SPACE)) { prompt_run_callback(self, self->focus->result); } - else if (e->xkey.keycode == ob_keycode(OB_KEY_TAB) || - e->xkey.keycode == ob_keycode(OB_KEY_LEFT) || - e->xkey.keycode == ob_keycode(OB_KEY_RIGHT)) + else if (ob_keycode_match(e->xkey.keycode, OB_KEY_TAB) || + ob_keycode_match(e->xkey.keycode, OB_KEY_LEFT) || + ob_keycode_match(e->xkey.keycode, OB_KEY_RIGHT)) { gint i; gboolean left; ObPromptElement *oldfocus; - left = e->xkey.keycode == ob_keycode(OB_KEY_LEFT) || - (e->xkey.keycode == ob_keycode(OB_KEY_TAB) && shift); + left = ob_keycode_match(e->xkey.keycode, OB_KEY_LEFT) || + (ob_keycode_match(e->xkey.keycode, OB_KEY_TAB) && shift); oldfocus = self->focus; for (i = 0; i < self->n_buttons; ++i) From 111465b7373cdcdd791b603aefd882ae06d5bf0b Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Mon, 14 Dec 2009 16:08:30 -0500 Subject: [PATCH 2/7] Let menus place themselves on monitors where the mouse is not present This fixes a bug which forced menus to show up on the same monitor as the mouse pointer. --- openbox/menuframe.c | 14 ++------------ openbox/screen.c | 16 ++++++++++------ openbox/screen.h | 3 +++ 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/openbox/menuframe.c b/openbox/menuframe.c index a9b54408..3b53a63e 100644 --- a/openbox/menuframe.c +++ b/openbox/menuframe.c @@ -192,6 +192,7 @@ static void menu_entry_frame_free(ObMenuEntryFrame *self) void menu_frame_move(ObMenuFrame *self, gint x, gint y) { RECT_SET_POINT(self->area, x, y); + self->monitor = screen_find_monitor_point(x, y); XMoveWindow(ob_display, self->window, self->area.x, self->area.y); } @@ -294,7 +295,7 @@ void menu_frame_move_on_screen(ObMenuFrame *self, gint x, gint y, *dx = *dy = 0; - a = screen_physical_area_monitor(self->monitor); + a = screen_physical_area_monitor(screen_find_monitor_point(x, y)); half = g_list_length(self->entries) / 2; pos = g_list_index(self->entries, self->selected); @@ -953,17 +954,6 @@ gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y, if (!menu_frame_show(self)) return FALSE; - /* find the monitor the menu is on */ - for (i = 0; i < screen_num_monitors; ++i) { - Rect *a = screen_physical_area_monitor(i); - gboolean contains = RECT_CONTAINS(*a, x, y); - g_free(a); - if (contains) { - self->monitor = i; - break; - } - } - if (self->menu->place_func) self->menu->place_func(self, &x, &y, mouse, self->menu->data); else diff --git a/openbox/screen.c b/openbox/screen.c index 8d0460d5..35c9f543 100644 --- a/openbox/screen.c +++ b/openbox/screen.c @@ -1719,17 +1719,21 @@ void screen_set_root_cursor(void) ob_cursor(OB_CURSOR_POINTER)); } -guint screen_monitor_pointer() +guint screen_find_monitor_point(guint x, guint y) { Rect mon; - gint x, y; - if (screen_pointer_pos(&x, &y)) - RECT_SET(mon, x, y, 1, 1); - else - RECT_SET(mon, 0, 0, 1, 1); + RECT_SET(mon, x, y, 1, 1); return screen_find_monitor(&mon); } +guint screen_monitor_pointer() +{ + gint x, y; + if (!screen_pointer_pos(&x, &y)) + x = y = 0; + return screen_find_monitor_point(x, y); +} + gboolean screen_pointer_pos(gint *x, gint *y) { Window w; diff --git a/openbox/screen.h b/openbox/screen.h index 7df47f39..4a8d8a1e 100644 --- a/openbox/screen.h +++ b/openbox/screen.h @@ -134,6 +134,9 @@ gboolean screen_physical_area_monitor_contains(guint head, Rect *search); */ guint screen_find_monitor(Rect *search); +/*! Finds the monitor which contains the point @x, @y */ +guint screen_find_monitor_point(guint x, guint y); + /*! Sets the root cursor. This function decides which cursor to use, but you gotta call it to let it know it should change. */ void screen_set_root_cursor(); From 828c095c8b5a2df96a38faaeb8a0df504e68e70f Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Mon, 14 Dec 2009 18:18:10 -0500 Subject: [PATCH 3/7] Don't hide submenus immediately when unselecting the parent's entry This allows users to move to the submenu across other menu items (the same as they already could across other menu items that were submenus). This uses the same config delay for hiding submenus as it does for showing new ones. Based off the ideas in bug #3762. --- openbox/event.c | 7 ++++- openbox/menuframe.c | 76 ++++++++++++++++++++++++++++++++++++++------- openbox/menuframe.h | 3 ++ 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/openbox/event.c b/openbox/event.c index 881c9f8f..d5791a03 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -1841,7 +1841,12 @@ static gboolean event_handle_menu(XEvent *ev) (f = find_active_menu()) && f->selected == e && e->entry->type != OB_MENU_ENTRY_TYPE_SUBMENU) { - menu_frame_select(e->frame, NULL, FALSE); + ObMenuEntryFrame *u = menu_entry_frame_under(ev->xcrossing.x_root, + ev->xcrossing.y_root); + /* if we're just going from one entry in the menu to the next, + don't unselect stuff first */ + if (!u || e->frame != u->frame) + menu_frame_select(e->frame, NULL, FALSE); } break; case MotionNotify: diff --git a/openbox/menuframe.c b/openbox/menuframe.c index 3b53a63e..ad692d2e 100644 --- a/openbox/menuframe.c +++ b/openbox/menuframe.c @@ -48,7 +48,8 @@ static ObMenuEntryFrame* menu_entry_frame_new(ObMenuEntry *entry, ObMenuFrame *frame); static void menu_entry_frame_free(ObMenuEntryFrame *self); static void menu_frame_update(ObMenuFrame *self); -static gboolean menu_entry_frame_submenu_timeout(gpointer data); +static gboolean menu_entry_frame_submenu_hide_timeout(gpointer data); +static gboolean menu_entry_frame_submenu_show_timeout(gpointer data); static void menu_frame_hide(ObMenuFrame *self); static Window createWindow(Window parent, gulong mask, @@ -94,6 +95,7 @@ ObMenuFrame* menu_frame_new(ObMenu *menu, guint show_from, ObClient *client) self->type = Window_Menu; self->menu = menu; self->selected = NULL; + self->open_submenu = NULL; self->client = client; self->direction_right = TRUE; self->show_from = show_from; @@ -984,6 +986,7 @@ gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent, self->monitor = parent->monitor; self->parent = parent; self->parent_entry = parent_entry; + parent->open_submenu = parent_entry; /* set up parent's child to be us */ if (parent->child) @@ -1028,8 +1031,10 @@ static void menu_frame_hide(ObMenuFrame *self) if (self->child) menu_frame_hide(self->child); - if (self->parent) + if (self->parent && self->parent->child == self) { self->parent->child = NULL; + self->parent->open_submenu = NULL; + } self->parent = NULL; self->parent_entry = NULL; @@ -1053,7 +1058,10 @@ void menu_frame_hide_all(void) if (config_submenu_show_delay) { /* remove any submenu open requests */ ob_main_loop_timeout_remove(ob_main_loop, - menu_entry_frame_submenu_timeout); + menu_entry_frame_submenu_show_timeout); + /* remove any submenu close delays */ + ob_main_loop_timeout_remove(ob_main_loop, + menu_entry_frame_submenu_hide_timeout); } if ((it = g_list_last(menu_frame_visible))) menu_frame_hide(it->data); @@ -1067,8 +1075,13 @@ void menu_frame_hide_all_client(ObClient *client) if (f->client == client) { if (config_submenu_show_delay) { /* remove any submenu open requests */ - ob_main_loop_timeout_remove(ob_main_loop, - menu_entry_frame_submenu_timeout); + ob_main_loop_timeout_remove + (ob_main_loop, + menu_entry_frame_submenu_show_timeout); + /* remove any submenu close delays */ + ob_main_loop_timeout_remove + (ob_main_loop, + menu_entry_frame_submenu_hide_timeout); } menu_frame_hide(f); } @@ -1103,7 +1116,6 @@ ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y) for (it = frame->entries; it; it = g_list_next(it)) { ObMenuEntryFrame *e = it->data; - if (RECT_CONTAINS(e->area, x, y)) { ret = e; break; @@ -1113,7 +1125,15 @@ ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y) return ret; } -static gboolean menu_entry_frame_submenu_timeout(gpointer data) +static gboolean menu_entry_frame_submenu_hide_timeout(gpointer data) +{ + g_assert(menu_frame_visible); + g_assert(((ObMenuFrame*)data)->parent != NULL); + menu_frame_hide((ObMenuFrame*)data); + return FALSE; +} + +static gboolean menu_entry_frame_submenu_show_timeout(gpointer data) { g_assert(menu_frame_visible); menu_entry_frame_show_submenu((ObMenuEntryFrame*)data); @@ -1134,25 +1154,57 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry, if (config_submenu_show_delay) { /* remove any submenu open requests */ ob_main_loop_timeout_remove(ob_main_loop, - menu_entry_frame_submenu_timeout); + menu_entry_frame_submenu_show_timeout); + } + + if (!entry && self->open_submenu) { + entry = self->open_submenu; + oldchild = NULL; + + /* remove any submenu close delays */ + ob_main_loop_timeout_remove(ob_main_loop, + menu_entry_frame_submenu_hide_timeout); } self->selected = entry; if (old) menu_entry_frame_render(old); - if (oldchild) - menu_frame_hide(oldchild); + + if (oldchild) { + /* there is an open submenu */ + + if (config_submenu_show_delay && !immediate) { + if (old == self->open_submenu) { + /* close the open submenu after a delay if we don't have + it selected */ + ob_main_loop_timeout_remove + (ob_main_loop, + menu_entry_frame_submenu_hide_timeout); + ob_main_loop_timeout_add(ob_main_loop, + config_submenu_show_delay * 1000, + menu_entry_frame_submenu_hide_timeout, + self->child, g_direct_equal, + NULL); + } + } + else + menu_frame_hide(oldchild); + } if (self->selected) { menu_entry_frame_render(self->selected); - if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU) { + /* if we've selected a submenu and it wasn't always open, then + show it */ + if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU && + self->selected != self->open_submenu) + { if (config_submenu_show_delay && !immediate) { /* initiate a new submenu open request */ ob_main_loop_timeout_add(ob_main_loop, config_submenu_show_delay * 1000, - menu_entry_frame_submenu_timeout, + menu_entry_frame_submenu_show_timeout, self->selected, g_direct_equal, NULL); } else { diff --git a/openbox/menuframe.h b/openbox/menuframe.h index 1b1dcc70..624c3f49 100644 --- a/openbox/menuframe.h +++ b/openbox/menuframe.h @@ -53,6 +53,9 @@ struct _ObMenuFrame GList *entries; ObMenuEntryFrame *selected; + /* if a submenu was selected, then this holds the entry for that submenu + until it is closed */ + ObMenuEntryFrame *open_submenu; /* show entries from the menu starting at this index */ guint show_from; From 3c688bc4a75436a457d3ce693eda6bd6b329412f Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Tue, 15 Dec 2009 14:53:29 -0500 Subject: [PATCH 4/7] Make the NET_WORKAREA hint work the way it used to, across all monitors The spec is not going to change this hint, but rather add a new one. So for now just revert to the old behaviour. --- openbox/focus_cycle_popup.c | 4 +- openbox/keyboard.c | 2 +- openbox/screen.c | 129 ++++++++++++++---------------------- openbox/screen.h | 14 +++- 4 files changed, 64 insertions(+), 85 deletions(-) diff --git a/openbox/focus_cycle_popup.c b/openbox/focus_cycle_popup.c index a544bf14..740d6688 100644 --- a/openbox/focus_cycle_popup.c +++ b/openbox/focus_cycle_popup.c @@ -261,7 +261,7 @@ static void popup_render(ObFocusCyclePopup *p, const ObClient *c) const ObFocusCyclePopupTarget *newtarget; gint newtargetx, newtargety; - screen_area = screen_physical_area_primary(); + screen_area = screen_physical_area_primary(FALSE); /* get the outside margins */ RrMargins(p->a_bg, &ml, &mt, &mr, &mb); @@ -515,7 +515,7 @@ void focus_cycle_popup_single_show(struct _ObClient *c, g_assert(popup.targets == NULL); /* position the popup */ - a = screen_physical_area_primary(); + a = screen_physical_area_primary(FALSE); icon_popup_position(single_popup, CenterGravity, a->x + a->width / 2, a->y + a->height / 2); icon_popup_height(single_popup, POPUP_HEIGHT); diff --git a/openbox/keyboard.c b/openbox/keyboard.c index aebee293..0d234857 100644 --- a/openbox/keyboard.c +++ b/openbox/keyboard.c @@ -91,7 +91,7 @@ static void set_curpos(KeyBindingTree *newpos) g_free(oldtext); } - a = screen_physical_area_primary(); + a = screen_physical_area_primary(FALSE); popup_position(popup, NorthWestGravity, a->x + 10, a->y + 10); /* 1 second delay for the popup to show */ popup_delay_show(popup, G_USEC_PER_SEC, text); diff --git a/openbox/screen.c b/openbox/screen.c index 35c9f543..9b1a7724 100644 --- a/openbox/screen.c +++ b/openbox/screen.c @@ -945,7 +945,7 @@ void screen_show_desktop_popup(guint d) /* 0 means don't show the popup */ if (!config_desktop_popup_time) return; - a = screen_physical_area_primary(); + a = screen_physical_area_primary(FALSE); pager_popup_position(desktop_popup, CenterGravity, a->x + a->width / 2, a->y + a->height / 2); pager_popup_icon_size_multiplier(desktop_popup, @@ -1285,19 +1285,6 @@ void screen_install_colormap(ObClient *client, gboolean install) } } -#define STRUT_LEFT_ON_MONITOR(s, i) \ - (RANGES_INTERSECT(s->left_start, s->left_end - s->left_start + 1, \ - monitor_area[i].y, monitor_area[i].height)) -#define STRUT_RIGHT_ON_MONITOR(s, i) \ - (RANGES_INTERSECT(s->right_start, s->right_end - s->right_start + 1, \ - monitor_area[i].y, monitor_area[i].height)) -#define STRUT_TOP_ON_MONITOR(s, i) \ - (RANGES_INTERSECT(s->top_start, s->top_end - s->top_start + 1, \ - monitor_area[i].x, monitor_area[i].width)) -#define STRUT_BOTTOM_ON_MONITOR(s, i) \ - (RANGES_INTERSECT(s->bottom_start, s->bottom_end - s->bottom_start + 1, \ - monitor_area[i].x, monitor_area[i].width)) - typedef struct { guint desktop; StrutPartial *strut; @@ -1325,7 +1312,7 @@ typedef struct { void screen_update_areas(void) { - guint i, j; + guint j; gulong *dims; GList *it; GSList *sit; @@ -1343,7 +1330,7 @@ void screen_update_areas(void) config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]); config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]); - dims = g_new(gulong, 4 * screen_num_desktops * screen_num_monitors); + dims = g_new(gulong, 4 * screen_num_desktops); RESET_STRUT_LIST(struts_left); RESET_STRUT_LIST(struts_top); @@ -1389,69 +1376,51 @@ void screen_update_areas(void) VALIDATE_STRUTS(struts_bottom, bottom, monitor_area[screen_num_monitors].height / 2); - /* set up the work areas to be full screen */ - for (i = 0; i < screen_num_monitors; ++i) - for (j = 0; j < screen_num_desktops; ++j) { - dims[(i * screen_num_desktops + j) * 4+0] = monitor_area[i].x; - dims[(i * screen_num_desktops + j) * 4+1] = monitor_area[i].y; - dims[(i * screen_num_desktops + j) * 4+2] = monitor_area[i].width; - dims[(i * screen_num_desktops + j) * 4+3] = monitor_area[i].height; + /* set up the work area to be full screen across all monitors */ + for (j = 0; j < screen_num_desktops; ++j) { + dims[j*4 + 0] = + monitor_area[screen_num_monitors].x; + dims[j*4 + 1] = + monitor_area[screen_num_monitors].y; + dims[j*4 + 2] = + monitor_area[screen_num_monitors].width; + dims[j*4 + 3] = + monitor_area[screen_num_monitors].height; + } + + /* calculate the work area from the struts */ + for (j = 0; j < screen_num_desktops; ++j) { + gint l = 0, r = 0, t = 0, b = 0; + + for (sit = struts_left; sit; sit = g_slist_next(sit)) { + ObScreenStrut *s = sit->data; + if (s->desktop == j || s->desktop == DESKTOP_ALL) + l = MAX(l, s->strut->left); + } + for (sit = struts_top; sit; sit = g_slist_next(sit)) { + ObScreenStrut *s = sit->data; + if (s->desktop == j || s->desktop == DESKTOP_ALL) + t = MAX(t, s->strut->top); + } + for (sit = struts_right; sit; sit = g_slist_next(sit)) { + ObScreenStrut *s = sit->data; + if (s->desktop == j || s->desktop == DESKTOP_ALL) + r = MAX(r, s->strut->right); + } + for (sit = struts_bottom; sit; sit = g_slist_next(sit)) { + ObScreenStrut *s = sit->data; + if (s->desktop == j || s->desktop == DESKTOP_ALL) + b = MAX(b, s->strut->bottom); } - /* calculate the work areas from the struts */ - for (i = 0; i < screen_num_monitors; ++i) - for (j = 0; j < screen_num_desktops; ++j) { - gint l = 0, r = 0, t = 0, b = 0; + /* based on these margins, set the work area for the desktop */ + dims[j*4 + 0] += l; + dims[j*4 + 1] += t; + dims[j*4 + 2] -= l + r; + dims[j*4 + 3] -= t + b; + } - /* only add the strut to the area if it touches the monitor */ - - for (sit = struts_left; sit; sit = g_slist_next(sit)) { - ObScreenStrut *s = sit->data; - if ((s->desktop == j || s->desktop == DESKTOP_ALL) && - STRUT_LEFT_ON_MONITOR(s->strut, i)) - l = MAX(l, s->strut->left); - } - for (sit = struts_top; sit; sit = g_slist_next(sit)) { - ObScreenStrut *s = sit->data; - if ((s->desktop == j || s->desktop == DESKTOP_ALL) && - STRUT_TOP_ON_MONITOR(s->strut, i)) - t = MAX(t, s->strut->top); - } - for (sit = struts_right; sit; sit = g_slist_next(sit)) { - ObScreenStrut *s = sit->data; - if ((s->desktop == j || s->desktop == DESKTOP_ALL) && - STRUT_RIGHT_ON_MONITOR(s->strut, i)) - r = MAX(r, s->strut->right); - } - for (sit = struts_bottom; sit; sit = g_slist_next(sit)) { - ObScreenStrut *s = sit->data; - if ((s->desktop == j || s->desktop == DESKTOP_ALL) && - STRUT_BOTTOM_ON_MONITOR(s->strut, i)) - b = MAX(b, s->strut->bottom); - } - - /* if the monitor is not against the edge of the root window, - the struts will include the distance from the root window's edge - to the monitor, so add that back into the monitor's work area */ - if (l) l += RECT_LEFT (monitor_area[screen_num_monitors]) - - RECT_LEFT (monitor_area[i]); - if (t) t += RECT_TOP (monitor_area[screen_num_monitors]) - - RECT_TOP (monitor_area[i]); - if (r) r -= RECT_RIGHT (monitor_area[screen_num_monitors]) - - RECT_RIGHT (monitor_area[i]); - if (b) b -= RECT_BOTTOM(monitor_area[screen_num_monitors]) - - RECT_BOTTOM(monitor_area[i]); - - /* based on these margins, set the work area for the - monitor/desktop */ - dims[(i * screen_num_desktops + j) * 4 + 0] += l; - dims[(i * screen_num_desktops + j) * 4 + 1] += t; - dims[(i * screen_num_desktops + j) * 4 + 2] -= l + r; - dims[(i * screen_num_desktops + j) * 4 + 3] -= t + b; - } - - /* all the work areas are not used here, only the ones for the first - monitor are */ + /* set the legacy workarea hint to the union of all the monitors */ PROP_SETA32(RootWindow(ob_display, ob_screen), net_workarea, cardinal, dims, 4 * screen_num_desktops); @@ -1690,7 +1659,7 @@ Rect* screen_physical_area_active(void) return screen_physical_area_monitor(screen_monitor_active()); } -guint screen_monitor_primary(void) +guint screen_monitor_primary(gboolean fixed) { if (config_primary_monitor_index > 0) { if (config_primary_monitor_index-1 < screen_num_monitors) @@ -1698,15 +1667,17 @@ guint screen_monitor_primary(void) else return 0; } + else if (fixed) + return 0; else if (config_primary_monitor == OB_PLACE_MONITOR_ACTIVE) return screen_monitor_active(); else /* config_primary_monitor == OB_PLACE_MONITOR_MOUSE */ return screen_monitor_pointer(); } -Rect *screen_physical_area_primary(void) +Rect *screen_physical_area_primary(gboolean fixed) { - return screen_physical_area_monitor(screen_monitor_primary()); + return screen_physical_area_monitor(screen_monitor_primary(fixed)); } void screen_set_root_cursor(void) diff --git a/openbox/screen.h b/openbox/screen.h index 4a8d8a1e..16903300 100644 --- a/openbox/screen.h +++ b/openbox/screen.h @@ -110,10 +110,18 @@ guint screen_monitor_active(void); Rect *screen_physical_area_active(void); -/*! Returns the primary monitor, as specified by the config */ -guint screen_monitor_primary(void); +/*! Returns the primary monitor, as specified by the config. + @fixed If TRUE, then this will always return a fixed monitor, otherwise + it may change based on where focus is, or other heuristics. + */ +guint screen_monitor_primary(gboolean fixed); -Rect *screen_physical_area_primary(void); +/*! Returns physical area for the primary monitor, as specified by the config. + @fixed If TRUE, then this will always use a fixed monitor as primary, + otherwise it may change based on where focus is, or other heuristics. + See screen_monitor_primary(). +*/ +Rect *screen_physical_area_primary(gboolean fixed); /* doesn't include struts which the search area is already outside of when 'search' is not NULL */ From 7c4753ac91796ab7677b55b79467c743ff7b5517 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Tue, 15 Dec 2009 16:03:35 -0500 Subject: [PATCH 5/7] Comment out some debug messages for the image cache --- render/image.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/render/image.c b/render/image.c index c8e839ce..48012d42 100644 --- a/render/image.c +++ b/render/image.c @@ -73,12 +73,14 @@ static void AddPicture(RrImage *self, RrImagePic ***list, gint *len, /* add the picture as a key to point to this image in the cache */ g_hash_table_insert(self->cache->table, (*list)[0], self); +/* #ifdef DEBUG g_message("Adding %s picture to the cache:\n " "Image 0x%x, w %d h %d Hash %u", (*list == self->original ? "ORIGINAL" : "RESIZED"), (guint)self, pic->width, pic->height, RrImagePicHash(pic)); #endif +*/ } /*! Remove a picture from an Image. This may remove it from the "originals" @@ -88,6 +90,7 @@ static void RemovePicture(RrImage *self, RrImagePic ***list, { gint j; +/* #ifdef DEBUG g_message("Removing %s picture from the cache:\n " "Image 0x%x, w %d h %d Hash %u", @@ -95,6 +98,7 @@ static void RemovePicture(RrImage *self, RrImagePic ***list, (guint)self, (*list)[i]->width, (*list)[i]->height, RrImagePicHash((*list)[i])); #endif +*/ /* remove the picture as a key in the cache */ g_hash_table_remove(self->cache->table, (*list)[i]); @@ -329,10 +333,12 @@ void RrImageRef(RrImage *self) void RrImageUnref(RrImage *self) { if (self && --self->ref == 0) { +/* #ifdef DEBUG g_message("Refcount to 0, removing ALL pictures from the cache:\n " "Image 0x%x", (guint)self); #endif +*/ while (self->n_original > 0) RemovePicture(self, &self->original, 0, &self->n_original); while (self->n_resized > 0) @@ -352,10 +358,12 @@ void RrImageAddPicture(RrImage *self, RrPixel32 *data, gint w, gint h) /* make sure we don't already have this size.. */ for (i = 0; i < self->n_original; ++i) if (self->original[i]->width == w && self->original[i]->height == h) { +/* #ifdef DEBUG g_message("Found duplicate ORIGINAL image:\n " "Image 0x%x, w %d h %d", (guint)self, w, h); #endif +*/ return; } From 811e3ddbc30d90283b532360ee8c39251fa54de8 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 16 Dec 2009 10:43:22 -0500 Subject: [PATCH 6/7] Ignore enter events caused by closing openbox menus --- openbox/menuframe.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openbox/menuframe.c b/openbox/menuframe.c index ad692d2e..3d2bf829 100644 --- a/openbox/menuframe.c +++ b/openbox/menuframe.c @@ -23,6 +23,7 @@ #include "screen.h" #include "prop.h" #include "actions.h" +#include "event.h" #include "grab.h" #include "openbox.h" #include "mainloop.h" @@ -949,7 +950,6 @@ gboolean menu_frame_show_topmenu(ObMenuFrame *self, gint x, gint y, gboolean mouse) { gint px, py; - guint i; if (menu_frame_is_visible(self)) return TRUE; @@ -1021,6 +1021,7 @@ gboolean menu_frame_show_submenu(ObMenuFrame *self, ObMenuFrame *parent, static void menu_frame_hide(ObMenuFrame *self) { GList *it = g_list_find(menu_frame_visible, self); + gulong ignore_start; if (!it) return; @@ -1046,7 +1047,9 @@ static void menu_frame_hide(ObMenuFrame *self) ungrab_keyboard(); } + ignore_start = event_start_ignore_all_enters(); XUnmapWindow(ob_display, self->window); + event_end_ignore_all_enters(ignore_start); menu_frame_free(self); } From 8ada991d829671ab5e6dd4c5526b6a0238ba6a16 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 16 Dec 2009 15:32:51 -0500 Subject: [PATCH 7/7] Fix the delayed hiding of submenus, so that they don't end up hidden while the entry is selected --- openbox/menuframe.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/openbox/menuframe.c b/openbox/menuframe.c index 3d2bf829..8177f513 100644 --- a/openbox/menuframe.c +++ b/openbox/menuframe.c @@ -1161,6 +1161,8 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry, } if (!entry && self->open_submenu) { + /* we moved out of the menu, so move the selection back to the open + submenu */ entry = self->open_submenu; oldchild = NULL; @@ -1178,12 +1180,16 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry, /* there is an open submenu */ if (config_submenu_show_delay && !immediate) { - if (old == self->open_submenu) { - /* close the open submenu after a delay if we don't have - it selected */ + if (entry == self->open_submenu) { + /* we moved onto the entry that has an open submenu, so stop + trying to close the submenu */ ob_main_loop_timeout_remove (ob_main_loop, menu_entry_frame_submenu_hide_timeout); + } + else if (old == self->open_submenu) { + /* we just moved off the entry with an open submenu, so + close the open submenu after a delay */ ob_main_loop_timeout_add(ob_main_loop, config_submenu_show_delay * 1000, menu_entry_frame_submenu_hide_timeout, @@ -1198,7 +1204,7 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry, if (self->selected) { menu_entry_frame_render(self->selected); - /* if we've selected a submenu and it wasn't always open, then + /* if we've selected a submenu and it wasn't already open, then show it */ if (self->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU && self->selected != self->open_submenu)