summaryrefslogtreecommitdiff
path: root/openbox
diff options
context:
space:
mode:
Diffstat (limited to 'openbox')
-rw-r--r--openbox/client.c62
-rw-r--r--openbox/client.h1
-rw-r--r--openbox/event.c17
-rw-r--r--openbox/focus.c28
-rw-r--r--openbox/focus.h4
-rw-r--r--openbox/focus_cycle.c85
-rw-r--r--openbox/focus_cycle.h6
-rw-r--r--openbox/focus_cycle_popup.c211
-rw-r--r--openbox/focus_cycle_popup.h10
-rw-r--r--openbox/menuframe.c36
-rw-r--r--openbox/menuframe.h2
-rw-r--r--openbox/misc.h2
-rw-r--r--openbox/openbox.c4
-rw-r--r--openbox/prop.c479
-rw-r--r--openbox/prop.h267
-rw-r--r--openbox/screen.c17
16 files changed, 1122 insertions, 109 deletions
diff --git a/openbox/client.c b/openbox/client.c
index 4b703768..fd2afed1 100644
--- a/openbox/client.c
+++ b/openbox/client.c
@@ -31,6 +31,7 @@
#include "grab.h"
#include "prompt.h"
#include "focus.h"
+#include "focus_cycle.h"
#include "stacking.h"
#include "openbox.h"
#include "group.h"
@@ -75,7 +76,7 @@ static RrImage *client_default_icon = NULL;
static void client_get_all(ObClient *self, gboolean real);
static void client_get_startup_id(ObClient *self);
static void client_get_session_ids(ObClient *self);
-static void client_save_session_ids(ObClient *self);
+static void client_save_app_rule_values(ObClient *self);
static void client_get_area(ObClient *self);
static void client_get_desktop(ObClient *self);
static void client_get_state(ObClient *self);
@@ -223,6 +224,7 @@ void client_manage(Window window, ObPrompt *prompt)
self->obwin.type = OB_WINDOW_CLASS_CLIENT;
self->window = window;
self->prompt = prompt;
+ self->managed = TRUE;
/* non-zero defaults */
self->wmstate = WithdrawnState; /* make sure it gets updated first time */
@@ -551,6 +553,8 @@ void client_unmanage(ObClient *self)
mouse_grab_for_client(self, FALSE);
+ self->managed = FALSE;
+
/* remove the window from our save set, unless we are managing an internal
ObPrompt window */
if (!self->prompt)
@@ -1078,7 +1082,9 @@ static void client_get_all(ObClient *self, gboolean real)
/* get the session related properties, these can change decorations
from per-app settings */
client_get_session_ids(self);
- client_save_session_ids(self);
+
+ /* save the values of the variables used for app rule matching */
+ client_save_app_rule_values(self);
/* now we got everything that can affect the decorations */
if (!real)
@@ -1912,6 +1918,8 @@ void client_update_wmhints(ObClient *self)
XFree(hints);
}
+
+ focus_cycle_addremove(self, TRUE);
}
void client_update_title(ObClient *self)
@@ -2292,13 +2300,36 @@ static void client_get_session_ids(ObClient *self)
}
}
-/*! Save the session IDs as seen by Openbox when the window mapped, so that
- users can still access them later if the app changes them */
-static void client_save_session_ids(ObClient *self)
+/*! Save the properties used for app matching rules, as seen by Openbox when
+ the window mapped, so that users can still access them later if the app
+ changes them */
+static void client_save_app_rule_values(ObClient *self)
{
- OBT_PROP_SETS(self->window, OB_ROLE, utf8, self->role);
- OBT_PROP_SETS(self->window, OB_NAME, utf8, self->name);
- OBT_PROP_SETS(self->window, OB_CLASS, utf8, self->class);
+ const gchar *type;
+
+ OBT_PROP_SETS(self->window, OB_APP_ROLE, utf8, self->role);
+ OBT_PROP_SETS(self->window, OB_APP_NAME, utf8, self->name);
+ OBT_PROP_SETS(self->window, OB_APP_CLASS, utf8, self->class);
+
+ switch (self->type) {
+ case OB_CLIENT_TYPE_NORMAL:
+ type = "normal"; break;
+ case OB_CLIENT_TYPE_DIALOG:
+ type = "dialog"; break;
+ case OB_CLIENT_TYPE_UTILITY:
+ type = "utility"; break;
+ case OB_CLIENT_TYPE_MENU:
+ type = "menu"; break;
+ case OB_CLIENT_TYPE_TOOLBAR:
+ type = "toolbar"; break;
+ case OB_CLIENT_TYPE_SPLASH:
+ type = "splash"; break;
+ case OB_CLIENT_TYPE_DESKTOP:
+ type = "desktop"; break;
+ case OB_CLIENT_TYPE_DOCK:
+ type = "dock"; break;
+ }
+ OBT_PROP_SETS(self->window, OB_APP_TYPE, utf8, type);
}
static void client_change_wm_state(ObClient *self)
@@ -3149,7 +3180,7 @@ static void client_iconify_recursive(ObClient *self,
self->iconic = iconic;
/* update the focus lists.. iconic windows go to the bottom of
- the list */
+ the list. this will also call focus_cycle_addremove(). */
focus_order_to_bottom(self);
changed = TRUE;
@@ -3161,9 +3192,10 @@ static void client_iconify_recursive(ObClient *self,
self->desktop != DESKTOP_ALL)
client_set_desktop(self, screen_desktop, FALSE, FALSE);
- /* this puts it after the current focused window */
- focus_order_remove(self);
- focus_order_add_new(self);
+ /* this puts it after the current focused window, this will
+ also cause focus_cycle_addremove() to be called for the
+ client */
+ focus_order_like_new(self);
changed = TRUE;
}
@@ -3496,6 +3528,8 @@ static void client_set_desktop_recursive(ObClient *self,
/* the new desktop's geometry may be different, so we may need to
resize, for example if we are maximized */
client_reconfigure(self, FALSE);
+
+ focus_cycle_addremove(self, FALSE);
}
/* move all transients */
@@ -3511,6 +3545,8 @@ void client_set_desktop(ObClient *self, guint target,
{
self = client_search_top_direct_parent(self);
client_set_desktop_recursive(self, target, donthide, dontraise);
+
+ focus_cycle_addremove(NULL, TRUE);
}
gboolean client_is_direct_child(ObClient *parent, ObClient *child)
@@ -3724,6 +3760,8 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
client_hilite(self, demands_attention);
client_change_state(self); /* change the hint to reflect these changes */
+
+ focus_cycle_addremove(self, TRUE);
}
ObClient *client_focus_target(ObClient *self)
diff --git a/openbox/client.h b/openbox/client.h
index a361e367..7370efcf 100644
--- a/openbox/client.h
+++ b/openbox/client.h
@@ -73,6 +73,7 @@ struct _ObClient
{
ObWindow obwin;
Window window;
+ gboolean managed;
/*! If this client is managing an ObPrompt window, then this is set to the
prompt */
diff --git a/openbox/event.c b/openbox/event.c
index e279c9db..45ae101e 100644
--- a/openbox/event.c
+++ b/openbox/event.c
@@ -1816,7 +1816,12 @@ static gboolean event_handle_menu_input(XEvent *ev)
else if (ob_keycode_match(keycode, OB_KEY_RIGHT)) {
/* Right goes to the selected submenu */
- if (frame->child) menu_frame_select_next(frame->child);
+ if (frame->selected->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ {
+ /* make sure it is visible */
+ menu_frame_select(frame, frame->selected, TRUE);
+ menu_frame_select_next(frame->child);
+ }
ret = TRUE;
}
@@ -1829,6 +1834,16 @@ static gboolean event_handle_menu_input(XEvent *ev)
menu_frame_select_next(frame);
ret = TRUE;
}
+
+ else if (ob_keycode_match(keycode, OB_KEY_HOME)) {
+ menu_frame_select_first(frame);
+ ret = TRUE;
+ }
+
+ else if (ob_keycode_match(keycode, OB_KEY_END)) {
+ menu_frame_select_last(frame);
+ ret = TRUE;
+ }
}
/* Use KeyRelease events for running things so that the key release
diff --git a/openbox/focus.c b/openbox/focus.c
index 8b4b66e4..20b799f6 100644
--- a/openbox/focus.c
+++ b/openbox/focus.c
@@ -90,6 +90,9 @@ void focus_set_client(ObClient *client)
push_to_top(client);
/* remove hiliting from the window when it gets focused */
client_hilite(client, FALSE);
+
+ /* make sure the focus cycle popup shows things in the right order */
+ focus_cycle_reorder();
}
/* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */
@@ -199,7 +202,7 @@ void focus_order_add_new(ObClient *c)
focus_order_to_top(c);
else {
g_assert(!g_list_find(focus_order, c));
- /* if there are any iconic windows, put this above them in the order,
+ /* if there are only iconic windows, put this above them in the order,
but if there are not, then put it under the currently focused one */
if (focus_order && ((ObClient*)focus_order->data)->iconic)
focus_order = g_list_insert(focus_order, c, 0);
@@ -207,16 +210,20 @@ void focus_order_add_new(ObClient *c)
focus_order = g_list_insert(focus_order, c, 1);
}
- /* in the middle of cycling..? kill it. */
- focus_cycle_stop(c);
+ focus_cycle_addremove(c, TRUE);
}
void focus_order_remove(ObClient *c)
{
focus_order = g_list_remove(focus_order, c);
- /* in the middle of cycling..? kill it. */
- focus_cycle_stop(c);
+ focus_cycle_addremove(c, TRUE);
+}
+
+void focus_order_like_new(struct _ObClient *c)
+{
+ focus_order = g_list_remove(focus_order, c);
+ focus_order_add_new(c);
}
void focus_order_to_top(ObClient *c)
@@ -232,6 +239,8 @@ void focus_order_to_top(ObClient *c)
it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
focus_order = g_list_insert_before(focus_order, it, c);
}
+
+ focus_cycle_reorder();
}
void focus_order_to_bottom(ObClient *c)
@@ -247,6 +256,8 @@ void focus_order_to_bottom(ObClient *c)
it && !((ObClient*)it->data)->iconic; it = g_list_next(it));
focus_order = g_list_insert_before(focus_order, it, c);
}
+
+ focus_cycle_reorder();
}
ObClient *focus_order_find_first(guint desktop)
@@ -294,8 +305,15 @@ gboolean focus_valid_target(ObClient *ft,
gboolean desktop_windows,
gboolean user_request)
{
+ /* NOTE: if any of these things change on a client, then they should call
+ focus_cycle_addremove() to make sure the client is not shown/hidden
+ when it should not be */
+
gboolean ok = FALSE;
+ /* see if the window is still managed or is going away */
+ if (!ft->managed) return FALSE;
+
/* it's on this desktop unless you want all desktops.
do this check first because it will usually filter out the most
diff --git a/openbox/focus.h b/openbox/focus.h
index f926d01e..47d86d85 100644
--- a/openbox/focus.h
+++ b/openbox/focus.h
@@ -58,6 +58,10 @@ void focus_order_remove(struct _ObClient *c);
/*! Move a client to the top of the focus order */
void focus_order_to_top(struct _ObClient *c);
+/*! Move a client to where it would be if it was newly added to the focus order
+ */
+void focus_order_like_new(struct _ObClient *c);
+
/*! Move a client to the bottom of the focus order (keeps iconic windows at the
very bottom always though). */
void focus_order_to_bottom(struct _ObClient *c);
diff --git a/openbox/focus_cycle.c b/openbox/focus_cycle.c
index 4e04477b..e4c370e7 100644
--- a/openbox/focus_cycle.c
+++ b/openbox/focus_cycle.c
@@ -29,7 +29,14 @@
#include <X11/Xlib.h>
#include <glib.h>
+typedef enum {
+ OB_CYCLE_NONE = 0,
+ OB_CYCLE_NORMAL,
+ OB_CYCLE_DIRECTIONAL
+} ObCycleType;
+
ObClient *focus_cycle_target = NULL;
+static ObCycleType focus_cycle_type = OB_CYCLE_NONE;
static gboolean focus_cycle_iconic_windows;
static gboolean focus_cycle_all_desktops;
static gboolean focus_cycle_dock_windows;
@@ -50,17 +57,40 @@ void focus_cycle_shutdown(gboolean reconfig)
if (reconfig) return;
}
-void focus_cycle_stop(ObClient *ifclient)
+void focus_cycle_addremove(ObClient *c, gboolean redraw)
{
- /* stop focus cycling if the given client is a valid focus target,
- and so the cycling is being disrupted */
- if (focus_cycle_target &&
- ((ifclient && (ifclient == focus_cycle_target ||
- focus_cycle_popup_is_showing(ifclient))) ||
- !ifclient))
- {
- focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,TRUE);
- focus_directional_cycle(0, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE);
+ if (!focus_cycle_type)
+ return;
+
+ if (focus_cycle_type == OB_CYCLE_DIRECTIONAL) {
+ if (c && focus_cycle_target == c) {
+ focus_directional_cycle(0, TRUE, TRUE, TRUE, TRUE,
+ TRUE, TRUE, TRUE);
+ }
+ }
+ else if (c && redraw) {
+ gboolean v, s;
+
+ v = focus_cycle_valid(c);
+ s = focus_cycle_popup_is_showing(c);
+
+ if (v != s)
+ focus_cycle_reorder();
+ }
+ else if (redraw) {
+ focus_cycle_reorder();
+ }
+}
+
+void focus_cycle_reorder()
+{
+ if (focus_cycle_type == OB_CYCLE_NORMAL) {
+ focus_cycle_target = focus_cycle_popup_refresh(focus_cycle_target,
+ TRUE);
+ focus_cycle_update_indicator(focus_cycle_target);
+ if (!focus_cycle_target)
+ focus_cycle(TRUE, TRUE, TRUE, TRUE, TRUE,
+ TRUE, TRUE, TRUE, TRUE, TRUE);
}
}
@@ -115,16 +145,11 @@ ObClient* focus_cycle(gboolean forward, gboolean all_desktops,
if (it == NULL) it = g_list_last(list);
}
ft = it->data;
- if (focus_valid_target(ft, screen_desktop, TRUE,
- focus_cycle_iconic_windows,
- focus_cycle_all_desktops,
- focus_cycle_dock_windows,
- focus_cycle_desktop_windows,
- FALSE))
- {
+ if (focus_cycle_valid(ft)) {
if (interactive) {
if (ft != focus_cycle_target) { /* prevents flicker */
focus_cycle_target = ft;
+ focus_cycle_type = OB_CYCLE_NORMAL;
focus_cycle_draw_indicator(showbar ? ft : NULL);
}
/* same arguments as focus_target_valid */
@@ -137,6 +162,7 @@ ObClient* focus_cycle(gboolean forward, gboolean all_desktops,
return focus_cycle_target;
} else if (ft != focus_cycle_target) {
focus_cycle_target = ft;
+ focus_cycle_type = OB_CYCLE_NORMAL;
done = TRUE;
break;
}
@@ -147,6 +173,7 @@ done_cycle:
if (done && !cancel) ret = focus_cycle_target;
focus_cycle_target = NULL;
+ focus_cycle_type = OB_CYCLE_NONE;
g_list_free(order);
order = NULL;
@@ -186,9 +213,7 @@ static ObClient *focus_find_directional(ObClient *c, ObDirection dir,
/* the currently selected window isn't interesting */
if (cur == c)
continue;
- if (!focus_valid_target(it->data, screen_desktop,
- TRUE, FALSE, FALSE, dock_windows,
- desktop_windows, FALSE))
+ if (!focus_cycle_valid(it->data))
continue;
/* find the centre coords of this window, from the
@@ -292,16 +317,15 @@ ObClient* focus_directional_cycle(ObDirection dir, gboolean dock_windows,
GList *it;
for (it = focus_order; it; it = g_list_next(it))
- if (focus_valid_target(it->data, screen_desktop, TRUE,
- focus_cycle_iconic_windows,
- focus_cycle_all_desktops,
- focus_cycle_dock_windows,
- focus_cycle_desktop_windows, FALSE))
+ if (focus_cycle_valid(it->data)) {
ft = it->data;
+ break;
+ }
}
if (ft && ft != focus_cycle_target) {/* prevents flicker */
focus_cycle_target = ft;
+ focus_cycle_type = OB_CYCLE_DIRECTIONAL;
if (!interactive)
goto done_cycle;
focus_cycle_draw_indicator(showbar ? ft : NULL);
@@ -320,9 +344,20 @@ done_cycle:
first = NULL;
focus_cycle_target = NULL;
+ focus_cycle_type = OB_CYCLE_NONE;
focus_cycle_draw_indicator(NULL);
focus_cycle_popup_single_hide();
return ret;
}
+
+gboolean focus_cycle_valid(struct _ObClient *client)
+{
+ return focus_valid_target(client, screen_desktop, TRUE,
+ focus_cycle_iconic_windows,
+ focus_cycle_all_desktops,
+ focus_cycle_dock_windows,
+ focus_cycle_desktop_windows,
+ FALSE);
+}
diff --git a/openbox/focus_cycle.h b/openbox/focus_cycle.h
index c31abc81..ab477d65 100644
--- a/openbox/focus_cycle.h
+++ b/openbox/focus_cycle.h
@@ -48,6 +48,10 @@ struct _ObClient* focus_directional_cycle(ObDirection dir,
gboolean dialog,
gboolean done, gboolean cancel);
-void focus_cycle_stop(struct _ObClient *ifclient);
+/*! Set @redraw to FALSE if there are more clients to be added/removed first */
+void focus_cycle_addremove(struct _ObClient *ifclient, gboolean redraw);
+void focus_cycle_reorder();
+
+gboolean focus_cycle_valid(struct _ObClient *client);
#endif
diff --git a/openbox/focus_cycle_popup.c b/openbox/focus_cycle_popup.c
index 42484418..08016fe3 100644
--- a/openbox/focus_cycle_popup.c
+++ b/openbox/focus_cycle_popup.c
@@ -18,6 +18,7 @@
*/
#include "focus_cycle_popup.h"
+#include "focus_cycle.h"
#include "popup.h"
#include "client.h"
#include "screen.h"
@@ -98,15 +99,12 @@ static ObFocusCyclePopup popup;
/*! This popup shows a single window */
static ObIconPopup *single_popup;
-static gchar *popup_get_name (ObClient *c);
-static void popup_setup (ObFocusCyclePopup *p,
- gboolean create_targets,
- gboolean iconic_windows,
- gboolean all_desktops,
- gboolean dock_windows,
- gboolean desktop_windows);
-static void popup_render (ObFocusCyclePopup *p,
- const ObClient *c);
+static gchar *popup_get_name (ObClient *c);
+static gboolean popup_setup (ObFocusCyclePopup *p,
+ gboolean create_targets,
+ gboolean refresh_targets);
+static void popup_render (ObFocusCyclePopup *p,
+ const ObClient *c);
static Window create_window(Window parent, guint bwidth, gulong mask,
XSetWindowAttributes *attr)
@@ -242,12 +240,35 @@ void focus_cycle_popup_shutdown(gboolean reconfig)
RrAppearanceFree(popup.a_bg);
}
-static void popup_setup(ObFocusCyclePopup *p, gboolean create_targets,
- gboolean iconic_windows, gboolean all_desktops,
- gboolean dock_windows, gboolean desktop_windows)
+static void popup_target_free(ObFocusCyclePopupTarget *t)
+{
+ RrImageUnref(t->icon);
+ g_free(t->text);
+ XDestroyWindow(obt_display, t->iconwin);
+ XDestroyWindow(obt_display, t->textwin);
+ g_free(t);
+}
+
+static gboolean popup_setup(ObFocusCyclePopup *p, gboolean create_targets,
+ gboolean refresh_targets)
{
gint maxwidth, n;
GList *it;
+ GList *rtargets; /* old targets for refresh */
+ GList *rtlast;
+ gboolean change;
+
+ if (refresh_targets) {
+ rtargets = p->targets;
+ rtlast = g_list_last(rtargets);
+ p->targets = NULL;
+ p->n_targets = 0;
+ change = FALSE;
+ }
+ else {
+ rtargets = rtlast = NULL;
+ change = TRUE;
+ }
g_assert(p->targets == NULL);
g_assert(p->n_targets == 0);
@@ -261,39 +282,82 @@ static void popup_setup(ObFocusCyclePopup *p, gboolean create_targets,
for (it = g_list_last(focus_order); it; it = g_list_previous(it)) {
ObClient *ft = it->data;
- if (focus_valid_target(ft, screen_desktop, TRUE,
- iconic_windows,
- all_desktops,
- dock_windows,
- desktop_windows,
- FALSE))
- {
- gchar *text = popup_get_name(ft);
+ if (focus_cycle_valid(ft)) {
+ GList *rit;
- /* measure */
- p->a_text->texture[0].data.text.string = text;
- maxwidth = MAX(maxwidth, RrMinWidth(p->a_text));
+ /* reuse the target if possible during refresh */
+ for (rit = rtlast; rit; rit = g_list_previous(rit)) {
+ ObFocusCyclePopupTarget *t = rit->data;
+ if (t->client == ft) {
+ if (rit == rtlast)
+ rtlast = g_list_previous(rit);
+ rtargets = g_list_remove_link(rtargets, rit);
- if (!create_targets) {
- g_free(text);
- } else {
- ObFocusCyclePopupTarget *t = g_new(ObFocusCyclePopupTarget, 1);
+ p->targets = g_list_concat(rit, p->targets);
+ ++n;
+
+ if (rit != rtlast)
+ change = TRUE; /* order changed */
+ break;
+ }
+ }
+
+ if (!rit) {
+ gchar *text = popup_get_name(ft);
+
+ /* measure */
+ p->a_text->texture[0].data.text.string = text;
+ maxwidth = MAX(maxwidth, RrMinWidth(p->a_text));
- t->client = ft;
- t->text = text;
- t->icon = client_icon(t->client);
- RrImageRef(t->icon); /* own the icon so it won't go away */
- t->iconwin = create_window(p->bg, 0, 0, NULL);
- t->textwin = create_window(p->bg, 0, 0, NULL);
+ if (!create_targets) {
+ g_free(text);
+ } else {
+ ObFocusCyclePopupTarget *t =
+ g_new(ObFocusCyclePopupTarget, 1);
+
+ t->client = ft;
+ t->text = text;
+ t->icon = client_icon(t->client);
+ RrImageRef(t->icon); /* own the icon so it won't go away */
+ t->iconwin = create_window(p->bg, 0, 0, NULL);
+ t->textwin = create_window(p->bg, 0, 0, NULL);
- p->targets = g_list_prepend(p->targets, t);
- ++n;
+ p->targets = g_list_prepend(p->targets, t);
+ ++n;
+
+ change = TRUE; /* added a window */
+ }
}
}
}
+ if (rtargets) {
+ change = TRUE; /* removed a window */
+
+ while (rtargets) {
+ popup_target_free(rtargets->data);
+ rtargets = g_list_delete_link(rtargets, rtargets);
+ }
+ }
+
p->n_targets = n;
- p->maxtextw = maxwidth;
+ if (refresh_targets)
+ /* don't shrink when refreshing */
+ p->maxtextw = MAX(p->maxtextw, maxwidth);
+ else
+ p->maxtextw = maxwidth;
+
+ return change;
+}
+
+static void popup_cleanup(void)
+{
+ while(popup.targets) {
+ popup_target_free(popup.targets->data);
+ popup.targets = g_list_delete_link(popup.targets, popup.targets);
+ }
+ popup.n_targets = 0;
+ popup.last_target = NULL;
}
static gchar *popup_get_name(ObClient *c)
@@ -652,8 +716,7 @@ void focus_cycle_popup_show(ObClient *c, gboolean iconic_windows,
/* do this stuff only when the dialog is first showing */
if (!popup.mapped) {
- popup_setup(&popup, TRUE, iconic_windows, all_desktops,
- dock_windows, desktop_windows);
+ popup_setup(&popup, TRUE, FALSE);
/* this is fixed once the dialog is shown */
popup.mode = mode;
}
@@ -683,19 +746,7 @@ void focus_cycle_popup_hide(void)
popup.mapped = FALSE;
- while(popup.targets) {
- ObFocusCyclePopupTarget *t = popup.targets->data;
-
- RrImageUnref(t->icon);
- g_free(t->text);
- XDestroyWindow(obt_display, t->iconwin);
- XDestroyWindow(obt_display, t->textwin);
- g_free(t);
-
- popup.targets = g_list_delete_link(popup.targets, popup.targets);
- }
- popup.n_targets = 0;
- popup.last_target = NULL;
+ popup_cleanup();
}
void focus_cycle_popup_single_show(struct _ObClient *c,
@@ -712,8 +763,7 @@ void focus_cycle_popup_single_show(struct _ObClient *c,
if (!popup.mapped) {
Rect *a;
- popup_setup(&popup, FALSE, iconic_windows, all_desktops,
- dock_windows, desktop_windows);
+ popup_setup(&popup, FALSE, FALSE);
g_assert(popup.targets == NULL);
/* position the popup */
@@ -738,16 +788,67 @@ void focus_cycle_popup_single_hide(void)
icon_popup_hide(single_popup);
}
-gboolean focus_cycle_popup_is_showing(ObClient *client)
+gboolean focus_cycle_popup_is_showing(ObClient *c)
{
if (popup.mapped) {
GList *it;
for (it = popup.targets; it; it = g_list_next(it)) {
ObFocusCyclePopupTarget *t = it->data;
- if (t->client == client)
+ if (t->client == c)
return TRUE;
}
}
return FALSE;
}
+
+static ObClient* popup_revert(ObClient *target)
+{
+ GList *it, *itt;
+
+ for (it = popup.targets; it; it = g_list_next(it)) {
+ ObFocusCyclePopupTarget *t = it->data;
+ if (t->client == target) {
+ /* move to a previous window if possible */
+ for (itt = it->prev; itt; itt = g_list_previous(itt)) {
+ ObFocusCyclePopupTarget *t2 = itt->data;
+ if (focus_cycle_valid(t2->client))
+ return t2->client;
+ }
+
+ /* otherwise move to a following window if possible */
+ for (itt = it->next; itt; itt = g_list_next(itt)) {
+ ObFocusCyclePopupTarget *t2 = itt->data;
+ if (focus_cycle_valid(t2->client))
+ return t2->client;
+ }
+
+ /* otherwise, we can't go anywhere there is nowhere valid to go */
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+ObClient* focus_cycle_popup_refresh(ObClient *target,
+ gboolean redraw)
+{
+ if (!popup.mapped) return NULL;
+
+ if (!focus_cycle_valid(target))
+ target = popup_revert(target);
+
+ redraw = popup_setup(&popup, TRUE, TRUE) && redraw;
+
+ if (!target && popup.targets)
+ target = ((ObFocusCyclePopupTarget*)popup.targets->data)->client;
+
+ if (target && redraw) {
+ popup.mapped = FALSE;
+ popup_render(&popup, target);
+ XFlush(obt_display);
+ popup.mapped = TRUE;
+ }
+
+ return target;
+}
diff --git a/openbox/focus_cycle_popup.h b/openbox/focus_cycle_popup.h
index ad76491d..b085f9a9 100644
--- a/openbox/focus_cycle_popup.h
+++ b/openbox/focus_cycle_popup.h
@@ -46,7 +46,13 @@ void focus_cycle_popup_single_show(struct _ObClient *c,
gboolean desktop_windows);
void focus_cycle_popup_single_hide(void);
-/*! Returns TRUE if the popup is showing the client, otherwise FALSE. */
-gboolean focus_cycle_popup_is_showing(struct _ObClient *client);
+gboolean focus_cycle_popup_is_showing(struct _ObClient *c);
+
+/*! Redraws the focus cycle popup, and returns the current target. If
+ the target given to the function is no longer valid, this will return
+ a different target that is valid, and which should be considered the
+ current focus cycling target. */
+struct _ObClient *focus_cycle_popup_refresh(struct _ObClient *target,
+ gboolean redraw);
#endif
diff --git a/openbox/menuframe.c b/openbox/menuframe.c
index 57f29438..b235f5eb 100644
--- a/openbox/menuframe.c
+++ b/openbox/menuframe.c
@@ -1300,7 +1300,7 @@ void menu_frame_select_previous(ObMenuFrame *self)
}
}
}
- menu_frame_select(self, it ? it->data : NULL, TRUE);
+ menu_frame_select(self, it ? it->data : NULL, FALSE);
}
void menu_frame_select_next(ObMenuFrame *self)
@@ -1325,5 +1325,37 @@ void menu_frame_select_next(ObMenuFrame *self)
}
}
}
- menu_frame_select(self, it ? it->data : NULL, TRUE);
+ menu_frame_select(self, it ? it->data : NULL, FALSE);
+}
+
+void menu_frame_select_first(ObMenuFrame *self)
+{
+ GList *it = NULL;
+
+ if (self->entries) {
+ for (it = self->entries; it; it = g_list_next(it)) {
+ ObMenuEntryFrame *e = it->data;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ break;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
+ break;
+ }
+ }
+ menu_frame_select(self, it ? it->data : NULL, FALSE);
+}
+
+void menu_frame_select_last(ObMenuFrame *self)
+{
+ GList *it = NULL;
+
+ if (self->entries) {
+ for (it = g_list_last(self->entries); it; it = g_list_previous(it)) {
+ ObMenuEntryFrame *e = it->data;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_SUBMENU)
+ break;
+ if (e->entry->type == OB_MENU_ENTRY_TYPE_NORMAL)
+ break;
+ }
+ }
+ menu_frame_select(self, it ? it->data : NULL, FALSE);
}
diff --git a/openbox/menuframe.h b/openbox/menuframe.h
index a57b0dcb..aa32b211 100644
--- a/openbox/menuframe.h
+++ b/openbox/menuframe.h
@@ -127,6 +127,8 @@ void menu_frame_select(ObMenuFrame *self, ObMenuEntryFrame *entry,
gboolean immediate);
void menu_frame_select_previous(ObMenuFrame *self);
void menu_frame_select_next(ObMenuFrame *self);
+void menu_frame_select_first(ObMenuFrame *self);
+void menu_frame_select_last(ObMenuFrame *self);
ObMenuFrame* menu_frame_under(gint x, gint y);
ObMenuEntryFrame* menu_entry_frame_under(gint x, gint y);
diff --git a/openbox/misc.h b/openbox/misc.h
index c1ec4075..68403f49 100644
--- a/openbox/misc.h
+++ b/openbox/misc.h
@@ -53,6 +53,8 @@ typedef enum
OB_KEY_DOWN,
OB_KEY_TAB,
OB_KEY_SPACE,
+ OB_KEY_HOME,
+ OB_KEY_END,
OB_NUM_KEYS
} ObKey;
diff --git a/openbox/openbox.c b/openbox/openbox.c
index 792dae2e..69889081 100644
--- a/openbox/openbox.c
+++ b/openbox/openbox.c
@@ -224,6 +224,8 @@ gint main(gint argc, gchar **argv)
keys[OB_KEY_DOWN] = obt_keyboard_keysym_to_keycode(XK_Down);
keys[OB_KEY_TAB] = obt_keyboard_keysym_to_keycode(XK_Tab);
keys[OB_KEY_SPACE] = obt_keyboard_keysym_to_keycode(XK_space);
+ keys[OB_KEY_HOME] = obt_keyboard_keysym_to_keycode(XK_Home);
+ keys[OB_KEY_END] = obt_keyboard_keysym_to_keycode(XK_End);
{
ObtXmlInst *i;
@@ -411,6 +413,8 @@ gint main(gint argc, gchar **argv)
g_free(keys[OB_KEY_DOWN]);
g_free(keys[OB_KEY_TAB]);
g_free(keys[OB_KEY_SPACE]);
+ g_free(keys[OB_KEY_HOME]);
+ g_free(keys[OB_KEY_END]);
} while (reconfigure);
}
diff --git a/openbox/prop.c b/openbox/prop.c
new file mode 100644
index 00000000..5184edae
--- /dev/null
+++ b/openbox/prop.c
@@ -0,0 +1,479 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ prop.c for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#include "prop.h"
+#include "openbox.h"
+
+#include <X11/Xatom.h>
+
+Atoms prop_atoms;
+
+#define CREATE(var, name) (prop_atoms.var = \
+ XInternAtom(ob_display, name, FALSE))
+
+void prop_startup(void)
+{
+ CREATE(cardinal, "CARDINAL");
+ CREATE(window, "WINDOW");
+ CREATE(pixmap, "PIXMAP");
+ CREATE(atom, "ATOM");
+ CREATE(string, "STRING");
+ CREATE(utf8, "UTF8_STRING");
+
+ CREATE(manager, "MANAGER");
+
+ CREATE(wm_colormap_windows, "WM_COLORMAP_WINDOWS");
+ CREATE(wm_protocols, "WM_PROTOCOLS");
+ CREATE(wm_state, "WM_STATE");
+ CREATE(wm_change_state, "WM_CHANGE_STATE");
+ CREATE(wm_delete_window, "WM_DELETE_WINDOW");
+ CREATE(wm_take_focus, "WM_TAKE_FOCUS");
+ CREATE(wm_name, "WM_NAME");
+ CREATE(wm_icon_name, "WM_ICON_NAME");
+ CREATE(wm_class, "WM_CLASS");
+ CREATE(wm_window_role, "WM_WINDOW_ROLE");
+ CREATE(wm_client_machine, "WM_CLIENT_MACHINE");
+ CREATE(wm_command, "WM_COMMAND");
+ CREATE(wm_client_leader, "WM_CLIENT_LEADER");
+ CREATE(wm_transient_for, "WM_TRANSIENT_FOR");
+ CREATE(motif_wm_hints, "_MOTIF_WM_HINTS");
+ CREATE(motif_wm_info, "_MOTIF_WM_INFO");
+
+ CREATE(sm_client_id, "SM_CLIENT_ID");
+
+ CREATE(net_wm_full_placement, "_NET_WM_FULL_PLACEMENT");
+
+ CREATE(net_supported, "_NET_SUPPORTED");
+ CREATE(net_client_list, "_NET_CLIENT_LIST");
+ CREATE(net_client_list_stacking, "_NET_CLIENT_LIST_STACKING");
+ CREATE(net_number_of_desktops, "_NET_NUMBER_OF_DESKTOPS");
+ CREATE(net_desktop_geometry, "_NET_DESKTOP_GEOMETRY");
+ CREATE(net_desktop_viewport, "_NET_DESKTOP_VIEWPORT");
+ CREATE(net_current_desktop, "_NET_CURRENT_DESKTOP");
+ CREATE(net_desktop_names, "_NET_DESKTOP_NAMES");
+ CREATE(net_active_window, "_NET_ACTIVE_WINDOW");
+/* CREATE(net_restack_window, "_NET_RESTACK_WINDOW");*/
+ CREATE(net_workarea, "_NET_WORKAREA");
+ CREATE(net_supporting_wm_check, "_NET_SUPPORTING_WM_CHECK");
+ CREATE(net_desktop_layout, "_NET_DESKTOP_LAYOUT");
+ CREATE(net_showing_desktop, "_NET_SHOWING_DESKTOP");
+
+ CREATE(net_close_window, "_NET_CLOSE_WINDOW");
+ CREATE(net_wm_moveresize, "_NET_WM_MOVERESIZE");
+ CREATE(net_moveresize_window, "_NET_MOVERESIZE_WINDOW");
+ CREATE(net_request_frame_extents, "_NET_REQUEST_FRAME_EXTENTS");
+ CREATE(net_restack_window, "_NET_RESTACK_WINDOW");
+
+ CREATE(net_startup_id, "_NET_STARTUP_ID");
+
+ CREATE(net_wm_name, "_NET_WM_NAME");
+ CREATE(net_wm_visible_name, "_NET_WM_VISIBLE_NAME");
+ CREATE(net_wm_icon_name, "_NET_WM_ICON_NAME");
+ CREATE(net_wm_visible_icon_name, "_NET_WM_VISIBLE_ICON_NAME");
+ CREATE(net_wm_desktop, "_NET_WM_DESKTOP");
+ CREATE(net_wm_window_type, "_NET_WM_WINDOW_TYPE");
+ CREATE(net_wm_state, "_NET_WM_STATE");
+ CREATE(net_wm_strut, "_NET_WM_STRUT");
+ CREATE(net_wm_strut_partial, "_NET_WM_STRUT_PARTIAL");
+ CREATE(net_wm_icon, "_NET_WM_ICON");
+ CREATE(net_wm_icon_geometry, "_NET_WM_ICON_GEOMETRY");
+ CREATE(net_wm_pid, "_NET_WM_PID");
+ CREATE(net_wm_allowed_actions, "_NET_WM_ALLOWED_ACTIONS");
+ CREATE(net_wm_user_time, "_NET_WM_USER_TIME");
+/* CREATE(net_wm_user_time_window, "_NET_WM_USER_TIME_WINDOW"); */
+ CREATE(kde_net_wm_frame_strut, "_KDE_NET_WM_FRAME_STRUT");
+ CREATE(net_frame_extents, "_NET_FRAME_EXTENTS");
+
+ CREATE(net_wm_ping, "_NET_WM_PING");
+#ifdef SYNC
+ CREATE(net_wm_sync_request, "_NET_WM_SYNC_REQUEST");
+ CREATE(net_wm_sync_request_counter, "_NET_WM_SYNC_REQUEST_COUNTER");
+#endif
+
+ CREATE(net_wm_window_type_desktop, "_NET_WM_WINDOW_TYPE_DESKTOP");
+ CREATE(net_wm_window_type_dock, "_NET_WM_WINDOW_TYPE_DOCK");
+ CREATE(net_wm_window_type_toolbar, "_NET_WM_WINDOW_TYPE_TOOLBAR");
+ CREATE(net_wm_window_type_menu, "_NET_WM_WINDOW_TYPE_MENU");
+ CREATE(net_wm_window_type_utility, "_NET_WM_WINDOW_TYPE_UTILITY");
+ CREATE(net_wm_window_type_splash, "_NET_WM_WINDOW_TYPE_SPLASH");
+ CREATE(net_wm_window_type_dialog, "_NET_WM_WINDOW_TYPE_DIALOG");
+ CREATE(net_wm_window_type_normal, "_NET_WM_WINDOW_TYPE_NORMAL");
+ CREATE(net_wm_window_type_popup_menu, "_NET_WM_WINDOW_TYPE_POPUP_MENU");
+
+ prop_atoms.net_wm_moveresize_size_topleft = 0;
+ prop_atoms.net_wm_moveresize_size_top = 1;
+ prop_atoms.net_wm_moveresize_size_topright = 2;
+ prop_atoms.net_wm_moveresize_size_right = 3;
+ prop_atoms.net_wm_moveresize_size_bottomright = 4;
+ prop_atoms.net_wm_moveresize_size_bottom = 5;
+ prop_atoms.net_wm_moveresize_size_bottomleft = 6;
+ prop_atoms.net_wm_moveresize_size_left = 7;
+ prop_atoms.net_wm_moveresize_move = 8;
+ prop_atoms.net_wm_moveresize_size_keyboard = 9;
+ prop_atoms.net_wm_moveresize_move_keyboard = 10;
+ prop_atoms.net_wm_moveresize_cancel = 11;
+
+ CREATE(net_wm_action_move, "_NET_WM_ACTION_MOVE");
+ CREATE(net_wm_action_resize, "_NET_WM_ACTION_RESIZE");
+ CREATE(net_wm_action_minimize, "_NET_WM_ACTION_MINIMIZE");
+ CREATE(net_wm_action_shade, "_NET_WM_ACTION_SHADE");
+ CREATE(net_wm_action_maximize_horz, "_NET_WM_ACTION_MAXIMIZE_HORZ");
+ CREATE(net_wm_action_maximize_vert, "_NET_WM_ACTION_MAXIMIZE_VERT");
+ CREATE(net_wm_action_fullscreen, "_NET_WM_ACTION_FULLSCREEN");
+ CREATE(net_wm_action_change_desktop, "_NET_WM_ACTION_CHANGE_DESKTOP");
+ CREATE(net_wm_action_close, "_NET_WM_ACTION_CLOSE");
+ CREATE(net_wm_action_above, "_NET_WM_ACTION_ABOVE");
+ CREATE(net_wm_action_below, "_NET_WM_ACTION_BELOW");
+
+ CREATE(net_wm_state_modal, "_NET_WM_STATE_MODAL");
+/* CREATE(net_wm_state_sticky, "_NET_WM_STATE_STICKY");*/
+ CREATE(net_wm_state_maximized_vert, "_NET_WM_STATE_MAXIMIZED_VERT");
+ CREATE(net_wm_state_maximized_horz, "_NET_WM_STATE_MAXIMIZED_HORZ");
+ CREATE(net_wm_state_shaded, "_NET_WM_STATE_SHADED");
+ CREATE(net_wm_state_skip_taskbar, "_NET_WM_STATE_SKIP_TASKBAR");
+ CREATE(net_wm_state_skip_pager, "_NET_WM_STATE_SKIP_PAGER");
+ CREATE(net_wm_state_hidden, "_NET_WM_STATE_HIDDEN");
+ CREATE(net_wm_state_fullscreen, "_NET_WM_STATE_FULLSCREEN");
+ CREATE(net_wm_state_above, "_NET_WM_STATE_ABOVE");
+ CREATE(net_wm_state_below, "_NET_WM_STATE_BELOW");
+ CREATE(net_wm_state_demands_attention, "_NET_WM_STATE_DEMANDS_ATTENTION");
+
+ prop_atoms.net_wm_state_add = 1;
+ prop_atoms.net_wm_state_remove = 0;
+ prop_atoms.net_wm_state_toggle = 2;
+
+ prop_atoms.net_wm_orientation_horz = 0;
+ prop_atoms.net_wm_orientation_vert = 1;
+ prop_atoms.net_wm_topleft = 0;
+ prop_atoms.net_wm_topright = 1;
+ prop_atoms.net_wm_bottomright = 2;
+ prop_atoms.net_wm_bottomleft = 3;
+
+ CREATE(kde_wm_change_state, "_KDE_WM_CHANGE_STATE");
+ CREATE(kde_net_wm_window_type_override,"_KDE_NET_WM_WINDOW_TYPE_OVERRIDE");
+
+/*
+ CREATE(rootpmapid, "_XROOTPMAP_ID");
+ CREATE(esetrootid, "ESETROOT_PMAP_ID");
+*/
+
+ CREATE(openbox_pid, "_OPENBOX_PID");
+ CREATE(ob_theme, "_OB_THEME");
+ CREATE(ob_config_file, "_OB_CONFIG_FILE");
+ CREATE(ob_wm_action_undecorate, "_OB_WM_ACTION_UNDECORATE");
+ CREATE(ob_wm_state_undecorated, "_OB_WM_STATE_UNDECORATED");
+ CREATE(ob_control, "_OB_CONTROL");
+ CREATE(ob_version, "_OB_VERSION");
+ CREATE(ob_app_role, "_OB_APP_ROLE");
+ CREATE(ob_app_name, "_OB_APP_NAME");
+ CREATE(ob_app_class, "_OB_APP_CLASS");
+ CREATE(ob_app_type, "_OB_APP_TYPE");
+}
+
+#include <X11/Xutil.h>
+#include <glib.h>
+#include <string.h>
+
+/* this just isn't used... and it also breaks on 64bit, watch out
+static gboolean get(Window win, Atom prop, Atom type, gint size,
+ guchar **data, gulong num)
+{
+ gboolean ret = FALSE;
+ gint res;
+ guchar *xdata = NULL;
+ Atom ret_type;
+ gint ret_size;
+ gulong ret_items, bytes_left;
+ glong num32 = 32 / size * num; /\* num in 32-bit elements *\/
+
+ res = XGetWindowProperty(display, win, prop, 0l, num32,
+ FALSE, type, &ret_type, &ret_size,
+ &ret_items, &bytes_left, &xdata);
+ if (res == Success && ret_items && xdata) {
+ if (ret_size == size && ret_items >= num) {
+ *data = g_memdup(xdata, num * (size / 8));
+ ret = TRUE;
+ }
+ XFree(xdata);
+ }
+ return ret;
+}
+*/
+
+static gboolean get_prealloc(Window win, Atom prop, Atom type, gint size,
+ guchar *data, gulong num)
+{
+ gboolean ret = FALSE;
+ gint res;
+ guchar *xdata = NULL;
+ Atom ret_type;
+ gint ret_size;
+ gulong ret_items, bytes_left;
+ glong num32 = 32 / size * num; /* num in 32-bit elements */
+
+ res = XGetWindowProperty(ob_display, win, prop, 0l, num32,
+ FALSE, type, &ret_type, &ret_size,
+ &ret_items, &bytes_left, &xdata);
+ if (res == Success && ret_items && xdata) {
+ if (ret_size == size && ret_items >= num) {
+ guint i;
+ for (i = 0; i < num; ++i)
+ switch (size) {
+ case 8:
+ data[i] = xdata[i];
+ break;
+ case 16:
+ ((guint16*)data)[i] = ((gushort*)xdata)[i];
+ break;
+ case 32:
+ ((guint32*)data)[i] = ((gulong*)xdata)[i];
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled size */
+ }
+ ret = TRUE;
+ }
+ XFree(xdata);
+ }
+ return ret;
+}
+
+static gboolean get_all(Window win, Atom prop, Atom type, gint size,
+ guchar **data, guint *num)
+{
+ gboolean ret = FALSE;
+ gint res;
+ guchar *xdata = NULL;
+ Atom ret_type;
+ gint ret_size;
+ gulong ret_items, bytes_left;
+
+ res = XGetWindowProperty(ob_display, win, prop, 0l, G_MAXLONG,
+ FALSE, type, &ret_type, &ret_size,
+ &ret_items, &bytes_left, &xdata);
+ if (res == Success) {
+ if (ret_size == size && ret_items > 0) {
+ guint i;
+
+ *data = g_malloc(ret_items * (size / 8));
+ for (i = 0; i < ret_items; ++i)
+ switch (size) {
+ case 8:
+ (*data)[i] = xdata[i];
+ break;
+ case 16:
+ ((guint16*)*data)[i] = ((gushort*)xdata)[i];
+ break;
+ case 32:
+ ((guint32*)*data)[i] = ((gulong*)xdata)[i];
+ break;
+ default:
+ g_assert_not_reached(); /* unhandled size */
+ }
+ *num = ret_items;
+ ret = TRUE;
+ }
+ XFree(xdata);
+ }
+ return ret;
+}
+
+static gboolean get_stringlist(Window win, Atom prop, gchar ***list, gint *nstr)
+{
+ XTextProperty tprop;
+ gboolean ret = FALSE;
+
+ if (XGetTextProperty(ob_display, win, &tprop, prop) && tprop.nitems) {
+ if (XTextPropertyToStringList(&tprop, list, nstr))
+ ret = TRUE;
+ XFree(tprop.value);
+ }
+ return ret;
+}
+
+gboolean prop_get32(Window win, Atom prop, Atom type, guint32 *ret)
+{
+ return get_prealloc(win, prop, type, 32, (guchar*)ret, 1);
+}
+
+gboolean prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret,
+ guint *nret)
+{
+ return get_all(win, prop, type, 32, (guchar**)ret, nret);
+}
+
+gboolean prop_get_string_locale(Window win, Atom prop, gchar **ret)
+{
+ gchar **list;
+ gint nstr;
+ gchar *s;
+
+ if (get_stringlist(win, prop, &list, &nstr) && nstr) {
+ s = g_locale_to_utf8(list[0], -1, NULL, NULL, NULL);
+ XFreeStringList(list);
+ if (s) {
+ *ret = s;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+gboolean prop_get_strings_locale(Window win, Atom prop, gchar ***ret)
+{
+ GSList *strs = NULL, *it;
+ gchar *raw, *p;
+ guint num, i, count = 0;
+
+ if (get_all(win, prop, prop_atoms.string, 8, (guchar**)&raw, &num)) {
+
+ p = raw;
+ while (p < raw + num) {
+ ++count;
+ strs = g_slist_append(strs, p);
+ p += strlen(p) + 1; /* next string */
+ }
+
+ *ret = g_new0(gchar*, count + 1);
+ (*ret)[count] = NULL; /* null terminated list */
+
+ for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) {
+ (*ret)[i] = g_locale_to_utf8(it->data, -1, NULL, NULL, NULL);
+ /* make sure translation did not fail */
+ if (!(*ret)[i])
+ (*ret)[i] = g_strdup("");
+ }
+ g_free(raw);
+ g_slist_free(strs);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+gboolean prop_get_string_utf8(Window win, Atom prop, gchar **ret)
+{
+ gchar *raw;
+ gchar *str;
+ guint num;
+
+ if (get_all(win, prop, prop_atoms.utf8, 8, (guchar**)&raw, &num)) {
+ str = g_strndup(raw, num); /* grab the first string from the list */
+ g_free(raw);
+ if (g_utf8_validate(str, -1, NULL)) {
+ *ret = str;
+ return TRUE;
+ }
+ g_free(str);
+ }
+ return FALSE;
+}
+
+gboolean prop_get_strings_utf8(Window win, Atom prop, gchar ***ret)
+{
+ GSList *strs = NULL, *it;
+ gchar *raw, *p;
+ guint num, i, count = 0;
+
+ if (get_all(win, prop, prop_atoms.utf8, 8, (guchar**)&raw, &num)) {
+
+ p = raw;
+ while (p < raw + num) {
+ ++count;
+ strs = g_slist_append(strs, p);
+ p += strlen(p) + 1; /* next string */
+ }
+
+ *ret = g_new0(gchar*, count + 1);
+
+ for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) {
+ if (g_utf8_validate(it->data, -1, NULL))
+ (*ret)[i] = g_strdup(it->data);
+ else
+ (*ret)[i] = g_strdup("");
+ }
+ g_free(raw);
+ g_slist_free(strs);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void prop_set32(Window win, Atom prop, Atom type, gulong val)
+{
+ XChangeProperty(ob_display, win, prop, type, 32, PropModeReplace,
+ (guchar*)&val, 1);
+}
+
+void prop_set_array32(Window win, Atom prop, Atom type, gulong *val,
+ guint num)
+{
+ XChangeProperty(ob_display, win, prop, type, 32, PropModeReplace,
+ (guchar*)val, num);
+}
+
+void prop_set_string_utf8(Window win, Atom prop, const gchar *val)
+{
+ XChangeProperty(ob_display, win, prop, prop_atoms.utf8, 8,
+ PropModeReplace, (const guchar*)val, strlen(val));
+}
+
+void prop_set_strings_utf8(Window win, Atom prop, gchar **strs)
+{
+ GString *str;
+ gchar **s;
+
+ str = g_string_sized_new(0);
+ for (s = strs; *s; ++s) {
+ str = g_string_append(str, *s);
+ str = g_string_append_c(str, '\0');
+ }
+ XChangeProperty(ob_display, win, prop, prop_atoms.utf8, 8,
+ PropModeReplace, (guchar*)str->str, str->len);
+ g_string_free(str, TRUE);
+}
+
+void prop_erase(Window win, Atom prop)
+{
+ XDeleteProperty(ob_display, win, prop);
+}
+
+void prop_message(Window about, Atom messagetype, glong data0, glong data1,
+ glong data2, glong data3, glong mask)
+{
+ prop_message_to(RootWindow(ob_display, ob_screen), about, messagetype,
+ data0, data1, data2, data3, 0, mask);
+}
+
+void prop_message_to(Window to, Window about, Atom messagetype,
+ glong data0, glong data1, glong data2,
+ glong data3, glong data4, glong mask)
+{
+ XEvent ce;
+ ce.xclient.type = ClientMessage;
+ ce.xclient.message_type = messagetype;
+ ce.xclient.display = ob_display;
+ ce.xclient.window = about;
+ ce.xclient.format = 32;
+ ce.xclient.data.l[0] = data0;
+ ce.xclient.data.l[1] = data1;
+ ce.xclient.data.l[2] = data2;
+ ce.xclient.data.l[3] = data3;
+ ce.xclient.data.l[4] = data4;
+ XSendEvent(ob_display, to, FALSE, mask, &ce);
+}
diff --git a/openbox/prop.h b/openbox/prop.h
new file mode 100644
index 00000000..71645673
--- /dev/null
+++ b/openbox/prop.h
@@ -0,0 +1,267 @@
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ prop.h for the Openbox window manager
+ Copyright (c) 2006 Mikael Magnusson
+ Copyright (c) 2003-2007 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __atoms_h
+#define __atoms_h
+
+#include <X11/Xlib.h>
+#include <glib.h>
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
+
+/*! The atoms on the X server which this class will cache */
+typedef struct Atoms {
+ /* types */
+ Atom cardinal; /*!< The atom which represents the Cardinal data type */
+ Atom window; /*!< The atom which represents window ids */
+ Atom pixmap; /*!< The atom which represents pixmap ids */
+ Atom atom; /*!< The atom which represents atom values */
+ Atom string; /*!< The atom which represents ascii strings */
+ Atom utf8; /*!< The atom which represents utf8-encoded strings */
+
+ /* selection stuff */
+ Atom manager;
+
+ /* window hints */
+ Atom wm_colormap_windows;
+ Atom wm_protocols;
+ Atom wm_state;
+ Atom wm_delete_window;
+ Atom wm_take_focus;
+ Atom wm_change_state;
+ Atom wm_name;
+ Atom wm_icon_name;
+ Atom wm_class;
+ Atom wm_window_role;
+ Atom wm_client_machine;
+ Atom wm_command;
+ Atom wm_client_leader;
+ Atom wm_transient_for;
+ Atom motif_wm_hints;
+ Atom motif_wm_info;
+
+ /* SM atoms */
+ Atom sm_client_id;
+
+ /* NETWM atoms */
+
+ /* Atoms that are used inside messages - these don't go in net_supported */
+
+ Atom net_wm_moveresize_size_topleft;
+ Atom net_wm_moveresize_size_top;
+ Atom net_wm_moveresize_size_topright;
+ Atom net_wm_moveresize_size_right;
+ Atom net_wm_moveresize_size_bottomright;
+ Atom net_wm_moveresize_size_bottom;
+ Atom net_wm_moveresize_size_bottomleft;
+ Atom net_wm_moveresize_size_left;
+ Atom net_wm_moveresize_move;
+ Atom net_wm_moveresize_size_keyboard;
+ Atom net_wm_moveresize_move_keyboard;
+ Atom net_wm_moveresize_cancel;
+
+ Atom net_wm_state_add;
+ Atom net_wm_state_remove;
+ Atom net_wm_state_toggle;
+
+ Atom net_wm_orientation_horz;
+ Atom net_wm_orientation_vert;
+ Atom net_wm_topleft;
+ Atom net_wm_topright;
+ Atom net_wm_bottomright;
+ Atom net_wm_bottomleft;
+
+ /* types that we use but don't support */
+
+ Atom net_wm_window_type_popup_menu;
+
+ /* Everything below here must go in net_supported on the root window */
+
+ /* root window properties */
+ Atom net_supported;
+ Atom net_client_list;
+ Atom net_client_list_stacking;
+ Atom net_number_of_desktops;
+ Atom net_desktop_geometry;
+ Atom net_desktop_viewport;
+ Atom net_current_desktop;
+ Atom net_desktop_names;
+ Atom net_active_window;
+/* Atom net_restack_window;*/
+ Atom net_workarea;
+ Atom net_supporting_wm_check;
+ Atom net_desktop_layout;
+ Atom net_showing_desktop;
+
+ /* root window messages */
+ Atom net_close_window;
+ Atom net_wm_moveresize;
+ Atom net_moveresize_window;
+ Atom net_request_frame_extents;
+ Atom net_restack_window;
+
+ /* helpful hints to apps that aren't used for anything */
+ Atom net_wm_full_placement;
+
+ /* startup-notification extension */
+ Atom net_startup_id;
+
+ /* application window properties */
+ Atom net_wm_name;
+ Atom net_wm_visible_name;
+ Atom net_wm_icon_name;
+ Atom net_wm_visible_icon_name;
+ Atom net_wm_desktop;
+ Atom net_wm_window_type;
+ Atom net_wm_state;
+ Atom net_wm_strut;
+ Atom net_wm_strut_partial;
+ Atom net_wm_icon;
+ Atom net_wm_icon_geometry;
+ Atom net_wm_pid;
+ Atom net_wm_allowed_actions;
+ Atom net_wm_user_time;
+/* Atom net_wm_user_time_window; */
+ Atom net_frame_extents;
+
+ /* application protocols */
+ Atom net_wm_ping;
+#ifdef SYNC
+ Atom net_wm_sync_request;
+ Atom net_wm_sync_request_counter;
+#endif
+
+ Atom net_wm_window_type_desktop;
+ Atom net_wm_window_type_dock;
+ Atom net_wm_window_type_toolbar;
+ Atom net_wm_window_type_menu;
+ Atom net_wm_window_type_utility;
+ Atom net_wm_window_type_splash;
+ Atom net_wm_window_type_dialog;
+ Atom net_wm_window_type_normal;
+
+ Atom net_wm_action_move;
+ Atom net_wm_action_resize;
+ Atom net_wm_action_minimize;
+ Atom net_wm_action_shade;
+/* Atom net_wm_action_stick;*/
+ Atom net_wm_action_maximize_horz;
+ Atom net_wm_action_maximize_vert;
+ Atom net_wm_action_fullscreen;
+ Atom net_wm_action_change_desktop;
+ Atom net_wm_action_close;
+ Atom net_wm_action_above;
+ Atom net_wm_action_below;
+
+ Atom net_wm_state_modal;
+/* Atom net_wm_state_sticky;*/
+ Atom net_wm_state_maximized_vert;
+ Atom net_wm_state_maximized_horz;
+ Atom net_wm_state_shaded;
+ Atom net_wm_state_skip_taskbar;
+ Atom net_wm_state_skip_pager;
+ Atom net_wm_state_hidden;
+ Atom net_wm_state_fullscreen;
+ Atom net_wm_state_above;
+ Atom net_wm_state_below;
+ Atom net_wm_state_demands_attention;
+
+ /* KDE atoms */
+
+ Atom kde_wm_change_state;
+ Atom kde_net_wm_frame_strut;
+ Atom kde_net_wm_window_type_override;
+
+/*
+ Atom rootpmapid;
+ Atom esetrootid;
+*/
+
+ /* Openbox specific atoms */
+
+ Atom ob_wm_action_undecorate;
+ Atom ob_wm_state_undecorated;
+ Atom openbox_pid; /* this is depreecated in favour of ob_control */
+ Atom ob_theme;
+ Atom ob_config_file;
+ Atom ob_control;
+ Atom ob_version;
+ Atom ob_app_role;
+ Atom ob_app_name;
+ Atom ob_app_class;
+ Atom ob_app_type;
+} Atoms;
+extern Atoms prop_atoms;
+
+void prop_startup();
+
+gboolean prop_get32(Window win, Atom prop, Atom type, guint32 *ret);
+gboolean prop_get_array32(Window win, Atom prop, Atom type, guint32 **ret,
+ guint *nret);
+gboolean prop_get_string_locale(Window win, Atom prop, gchar **ret);
+gboolean prop_get_string_utf8(Window win, Atom prop, gchar **ret);
+gboolean prop_get_strings_locale(Window win, Atom prop, gchar ***ret);
+gboolean prop_get_strings_utf8(Window win, Atom prop, gchar ***ret);
+
+void prop_set32(Window win, Atom prop, Atom type, gulong val);
+void prop_set_array32(Window win, Atom prop, Atom type, gulong *val,
+ guint num);
+void prop_set_string_utf8(Window win, Atom prop, const gchar *val);
+void prop_set_strings_utf8(Window win, Atom prop, gchar **strs);
+
+void prop_erase(Window win, Atom prop);
+
+void prop_message(Window about, Atom messagetype, glong data0, glong data1,
+ glong data2, glong data3, glong mask);
+void prop_message_to(Window to, Window about, Atom messagetype,
+ glong data0, glong data1, glong data2,
+ glong data3, glong data4, glong mask);
+
+#define PROP_GET32(win, prop, type, ret) \
+ (prop_get32(win, prop_atoms.prop, prop_atoms.type, ret))
+#define PROP_GETA32(win, prop, type, ret, nret) \
+ (prop_get_array32(win, prop_atoms.prop, prop_atoms.type, ret, \
+ nret))
+#define PROP_GETS(win, prop, type, ret) \
+ (prop_get_string_##type(win, prop_atoms.prop, ret))
+#define PROP_GETSS(win, prop, type, ret) \
+ (prop_get_strings_##type(win, prop_atoms.prop, ret))
+
+#define PROP_SET32(win, prop, type, val) \
+ prop_set32(win, prop_atoms.prop, prop_atoms.type, val)
+#define PROP_SETA32(win, prop, type, val, num) \
+ prop_set_array32(win, prop_atoms.prop, prop_atoms.type, val, num)
+#define PROP_SETS(win, prop, val) \
+ prop_set_string_utf8(win, prop_atoms.prop, val)
+#define PROP_SETSS(win, prop, strs) \
+ prop_set_strings_utf8(win, prop_atoms.prop, strs)
+
+#define PROP_ERASE(win, prop) prop_erase(win, prop_atoms.prop)
+
+#define PROP_MSG(about, msgtype, data0, data1, data2, data3) \
+ (prop_message(about, prop_atoms.msgtype, data0, data1, data2, data3, \
+ SubstructureNotifyMask | SubstructureRedirectMask))
+
+#define PROP_MSG_TO(to, about, msgtype, data0, data1, data2, data3, data4, \
+ mask) \
+ (prop_message_to(to, about, prop_atoms.msgtype, \
+ data0, data1, data2, data3, data4, mask))
+
+#endif
diff --git a/openbox/screen.c b/openbox/screen.c
index bce2faaf..2e69f11e 100644
--- a/openbox/screen.c
+++ b/openbox/screen.c
@@ -32,6 +32,7 @@
#include "focus.h"
#include "focus_cycle.h"
#include "popup.h"
+#include "version.h"
#include "obrender/render.h"
#include "gettext.h"
#include "obt/display.h"
@@ -294,15 +295,20 @@ gboolean screen_annex(void)
supported[i++] = OBT_PROP_ATOM(OB_THEME);
supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
- supported[i++] = OBT_PROP_ATOM(OB_ROLE);
- supported[i++] = OBT_PROP_ATOM(OB_NAME);
- supported[i++] = OBT_PROP_ATOM(OB_CLASS);
+ supported[i++] = OBT_PROP_ATOM(OB_VERSION);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_ROLE);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_NAME);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE);
g_assert(i == num_support);
OBT_PROP_SETA32(obt_root(ob_screen),
NET_SUPPORTED, ATOM, supported, num_support);
g_free(supported);
+ OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION, utf8,
+ OPENBOX_VERSION);
+
screen_tell_ksplash();
return TRUE;
@@ -705,9 +711,6 @@ void screen_set_desktop(guint num, gboolean dofocus)
if (WINDOW_IS_CLIENT(it->data)) {
ObClient *c = it->data;
if (client_hide(c)) {
- /* in the middle of cycling..? kill it. */
- focus_cycle_stop(c);
-
if (c == focus_client) {
/* c was focused and we didn't do fallback clearly so make
sure openbox doesnt still consider the window focused.
@@ -723,6 +726,8 @@ void screen_set_desktop(guint num, gboolean dofocus)
}
}
+ focus_cycle_addremove(NULL, TRUE);
+
event_end_ignore_all_enters(ignore_start);
if (event_curtime != CurrentTime)