From: Dana Jansens Date: Fri, 9 May 2003 16:57:17 +0000 (+0000) Subject: all my changes while i was offline. X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=f26f23de50cb7941a7702198e3b4d1b2f9de062e;p=chaz%2Fopenbox all my changes while i was offline. better alt-tabbing. better transient handling. i dont even know. lots of fucking cool shit so WATCH the FUCK OUT. --- diff --git a/HACKING b/HACKING index afd84869..45104201 100644 --- a/HACKING +++ b/HACKING @@ -13,6 +13,10 @@ which is not a valid pointer. You must ALWAYS check for TRAN_GROUP before following transient_for. When TRAN_GROUP is found, Client.group will always be !NULL. Some smart action should be taken using all members of the group in this case. + Smart action idea: + Skip over members of the group that are also transients of the group + (have Client.transient_for set to TRAN_GROUP). These windows are not + ancestors and using them will also end up causing infinite loops! When using coordinates/sizes of windows, make sure you use the right area. The Client.area rect is the reference point and size of the *CLIENT* window. This diff --git a/Makefile.am b/Makefile.am index 060cf7dc..8e701c9c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ #SUBDIRS = po themes doc render cwmcc obcl kernel plugins -SUBDIRS = po themes data render kernel plugins +SUBDIRS = po themes data render kernel plugins tools MAINTAINERCLEANFILES = aclocal.m4 config.h.in configure Makefile.in stamp-h.in doc: diff --git a/configure.ac b/configure.ac index e8ba1b38..9b914efa 100644 --- a/configure.ac +++ b/configure.ac @@ -57,6 +57,8 @@ AC_SUBST(XFT_LIBS) # Check for X11 extensions X11_EXT_XKB +X11_EXT_XRANDR +X11_EXT_VIDMODE X11_EXT_SHAPE X11_EXT_XINERAMA @@ -69,7 +71,9 @@ AC_CONFIG_FILES([Makefile plugins/Makefile plugins/placement/Makefile plugins/mouse/Makefile - plugins/keyboard/Makefile]) + plugins/keyboard/Makefile + tools/Makefile + tools/slit/Makefile]) AC_OUTPUT AC_MSG_RESULT diff --git a/data/rc3 b/data/rc3 index 67c87da3..fd16dc93 100644 --- a/data/rc3 +++ b/data/rc3 @@ -27,6 +27,9 @@ # set, the previously focused window on the desktop is focused when switching #focusLastOnDesktop = yes +# shows a helpful dialog while cycling focus +#cyclingDialog = yes + [desktops] # The number of virtual desktops to use diff --git a/m4/x11.m4 b/m4/x11.m4 index 5a4a35dd..65e2ab7f 100644 --- a/m4/x11.m4 +++ b/m4/x11.m4 @@ -211,6 +211,11 @@ AC_DEFUN([X11_EXT_XKB], AC_MSG_RESULT([yes]) XKB="yes" AC_DEFINE([XKB], [1], [Found the XKB extension]) + + XKB_CFLAGS="" + XKB_LIBS="" + AC_SUBST(XKB_CFLAGS) + AC_SUBST(XKB_LIBS) ], [ AC_MSG_RESULT([no]) @@ -229,6 +234,63 @@ AC_DEFUN([X11_EXT_XKB], fi ]) +# X11_EXT_XRANDR() +# +# Check for the presence of the "XRandR" X Window System extension. +# Defines "XRANDR" and sets the $(XRANDR) variable to "yes" if the extension is +# present. +AC_DEFUN([X11_EXT_XRANDR], +[ + AC_REQUIRE([X11_DEVEL]) + + # Store these + OLDLIBS=$LIBS + OLDCPPFLAGS=$CPPFLAGS + + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + LIBS="$LIBS $X_PRE_LIBS $X_LIBS $X_EXTRA_LIBS -lXext -lXrender -lXrandr" + + AC_CHECK_LIB([Xrandr], [XRRSelectInput], + AC_MSG_CHECKING([for X11/extensions/Xrandr.h]) + AC_TRY_LINK( + [ + #include + #include + ], + [ + Display *d; + Drawable r; + int i; + XRRQueryExtension(d, &i, &i); + XRRGetScreenInfo(d, r); + ], + [ + AC_MSG_RESULT([yes]) + XRANDR="yes" + AC_DEFINE([XRANDR], [1], [Found the XRandR extension]) + + XRANDR_CFLAGS="" + XRANDR_LIBS="-lXext -lXrender -lXrandr" + AC_SUBST(XRANDR_CFLAGS) + AC_SUBST(XRANDR_LIBS) + ], + [ + AC_MSG_RESULT([no]) + XRANDR="no" + ]) + ) + + LIBS=$OLDLIBS + CPPFLAGS=$OLDCPPFLAGS + + AC_MSG_CHECKING([for the XRandR extension]) + if test "$XRANDR" = "yes"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi +]) + # X11_EXT_SHAPE() # # Check for the presence of the "Shape" X Window System extension. @@ -260,7 +322,11 @@ AC_DEFUN([X11_EXT_SHAPE], AC_MSG_RESULT([yes]) SHAPE="yes" AC_DEFINE([SHAPE], [1], [Found the XShape extension]) - LIBS="$LIBS -lXext" + + XSHAPE_CFLAGS="" + XSHAPE_LIBS="-lXext" + AC_SUBST(XSHAPE_CFLAGS) + AC_SUBST(XSHAPE_LIBS) ], [ AC_MSG_RESULT([no]) @@ -271,7 +337,7 @@ AC_DEFUN([X11_EXT_SHAPE], LIBS=$OLDLIBS CPPFLAGS=$OLDCPPFLAGS - AC_MSG_CHECKING([for the Shape extension]) + AC_MSG_CHECKING([for the Shape extension]) if test "$SHAPE" = "yes"; then AC_MSG_RESULT([yes]) else @@ -330,3 +396,59 @@ AC_DEFUN([X11_EXT_XINERAMA], AC_MSG_RESULT([no]) fi ]) + +# VIDMODE() +# +# Check for the presence of the "VidMode" X Window System extension. +# Defines "VIDMODE" and sets the $(VIDMODE) variable to "yes" if the extension +# is present. +AC_DEFUN([X11_EXT_VIDMODE], +[ + AC_REQUIRE([X11_DEVEL]) + + # Store these + OLDLIBS=$LIBS + OLDCPPFLAGS=$CPPFLAGS + + CPPFLAGS="$CPPFLAGS $X_CFLAGS" + LIBS="$LIBS $X_PRE_LIBS $X_LIBS $X_EXTRA_LIBS -lXext -lXxf86vm" + + AC_CHECK_LIB([Xxf86vm], [XF86VidModeGetViewPort], + AC_MSG_CHECKING([for X11/extensions/xf86vmode.h]) + AC_TRY_LINK( + [ + #include + #include + ], + [ + Display *d; + int i; + XF86VidModeQueryExtension(d, &i, &i); + XF86VidModeGetViewPort(d, i, &i, &i); + ], + [ + AC_MSG_RESULT([yes]) + VIDMODE="yes" + AC_DEFINE([VIDMODE], [1], [Found the VidMode extension]) + + VIDMODE_CFLAGS="" + VIDMODE_LIBS="-lXext -lXxf86vm" + AC_SUBST(VIDMODE_CFLAGS) + AC_SUBST(VIDMODE_LIBS) + ], + [ + AC_MSG_RESULT([no]) + VIDMODE="no" + ]) + ) + + LIBS=$OLDLIBS + CPPFLAGS=$OLDCPPFLAGS + + AC_MSG_CHECKING([for the VidMode extension]) + if test "$VIDMODE" = "yes"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi +]) diff --git a/openbox/Makefile.am b/openbox/Makefile.am index a82473b5..8e027ddb 100644 --- a/openbox/Makefile.am +++ b/openbox/Makefile.am @@ -14,22 +14,24 @@ CPPFLAGS=$(X_CFLAGS) $(XFT_CFLAGS) $(GLIB_CFLAGS) $(GMODULE_CFLAGS) \ -DBINARY=\"$(binary)\" INCLUDES=-I.. -LIBS=$(X_LIBS) $(XFT_LIBS) $(XINERAMA_LIBS) $(GLIB_LIBS) $(GMODULE_LIBS) @LIBS@ +LIBS=$(X_LIBS) $(XFT_LIBS) $(XINERAMA_LIBS) $(XKB_LIBS) $(XRANDR_LIBS) \ + $(VIDMODE_LIBS) $(XSHAPE_LIBS) $(GLIB_LIBS) $(GMODULE_LIBS) @LIBS@ \ + @LIBINTL@ bin_PROGRAMS=$(binary) -openbox3_LDADD=@LIBINTL@ -lobrender -L../render +openbox3_LDADD=-lobrender -L../render openbox3_LDFLAGS=-export-dynamic openbox3_SOURCES=parse.tab.c parse.lex.c action.c client.c config.c \ extensions.c focus.c frame.c grab.c menu.c menu_render.c \ openbox.c framerender.c parse.c plugin.c prop.c screen.c \ stacking.c dispatch.c event.c group.c timer.c xerror.c \ - moveresize.c startup.c + moveresize.c startup.c popup.c noinst_HEADERS=action.h client.h config.h dispatch.h event.h extensions.h \ focus.h frame.h framerender.h geom.h gettext.h grab.h group.h \ menu.h openbox.h parse.h parse.tab.h plugin.h prop.h screen.h \ - stacking.h timer.h xerror.h moveresize.h startup.h + stacking.h timer.h xerror.h moveresize.h startup.h popup.h # kill the implicit .c.y rule %.c: %.y diff --git a/openbox/client.c b/openbox/client.c index 47be1bd9..2cd92125 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -38,10 +38,7 @@ static void client_get_gravity(Client *self); static void client_showhide(Client *self); static void client_change_allowed_actions(Client *self); static void client_change_state(Client *self); -static void client_move_onscreen(Client *self); -static Client *search_focus_tree(Client *node, Client *skip); static void client_apply_startup_state(Client *self); -static Client *search_modal_tree(Client *node, Client *skip); static guint map_hash(Window *w) { return *w; } static gboolean map_key_comp(Window *w1, Window *w2) { return *w1 == *w2; } @@ -82,6 +79,37 @@ void client_set_list() stacking_set_list(); } +/* +void client_foreach_transient(Client *self, ClientForeachFunc func, void *data) +{ + GSList *it; + + for (it = self->transients; it; it = it->next) { + if (!func(it->data, data)) return; + client_foreach_transient(it->data, func, data); + } +} + +void client_foreach_ancestor(Client *self, ClientForeachFunc func, void *data) +{ + if (self->transient_for) { + if (self->transient_for != TRAN_GROUP) { + if (!func(self->transient_for, data)) return; + client_foreach_ancestor(self->transient_for, func, data); + } else { + GSList *it; + + for (it = self->group->members; it; it = it->next) + if (it->data != self && + ((Client*)it->data)->transient_for != TRAN_GROUP) { + if (!func(it->data, data)) return; + client_foreach_ancestor(it->data, func, data); + } + } + } +} +*/ + void client_manage_all() { unsigned int i, j, nchild; @@ -137,7 +165,7 @@ void client_manage_all() if (config_focus_new) { active = g_hash_table_lookup(client_map, &startup_active); - if (!active || !client_focus(active)) + if (!(active && client_focus(active))) focus_fallback(Fallback_NoFocus); } } @@ -266,7 +294,7 @@ void client_manage(Window window) (!parent && (!self->group || !self->group->members->next)))))) || (parent && (client_focused(parent) || - search_focus_tree(parent, parent)))) { + client_search_focus_tree(parent)))) { client_focus(self); } } @@ -399,7 +427,7 @@ void client_unmanage(Client *self) client_set_list(); } -static void client_move_onscreen(Client *self) +void client_move_onscreen(Client *self) { Rect *a; int x = self->frame->area.x, y = self->frame->area.y; @@ -415,7 +443,7 @@ static void client_move_onscreen(Client *self) y = a->y; frame_frame_gravity(self->frame, &x, &y); /* get where the client - should be */ + should be */ client_configure(self , Corner_TopLeft, x, y, self->area.width, self->area.height, TRUE, TRUE); @@ -496,6 +524,7 @@ static void client_get_all(Client *self) /* defaults */ self->frame = NULL; self->title = self->icon_title = NULL; + self->title_count = 1; self->name = self->class = self->role = NULL; self->wmstate = NormalState; self->transient = FALSE; @@ -529,7 +558,6 @@ static void client_get_all(Client *self) client_setup_decor_and_functions(self); client_update_title(self); - client_update_icon_title(self); client_update_class(self); client_update_strut(self); client_update_icons(self); @@ -782,10 +810,6 @@ void client_get_type(Client *self) else self->type = Type_Normal; } - - /* this makes sure that these windows appear on all desktops */ - if (self->type == Type_Desktop) - self->desktop = DESKTOP_ALL; } void client_update_protocols(Client *self) @@ -991,11 +1015,19 @@ void client_setup_decor_and_functions(Client *self) client_change_allowed_actions(self); if (self->frame) { + /* this makes sure that these windows appear on all desktops */ + if (self->type == Type_Desktop && self->desktop != DESKTOP_ALL) + client_set_desktop(self, DESKTOP_ALL, FALSE); + /* change the decors on the frame, and with more/less decorations, we may also need to be repositioned */ frame_adjust_area(self->frame, TRUE, TRUE); /* with new decor, the window's maximized size may change */ client_remaximize(self); + } else { + /* this makes sure that these windows appear on all desktops */ + if (self->type == Type_Desktop && self->desktop != DESKTOP_ALL) + self->desktop = DESKTOP_ALL; } } @@ -1004,7 +1036,9 @@ static void client_change_allowed_actions(Client *self) guint32 actions[9]; int num = 0; - actions[num++] = prop_atoms.net_wm_action_change_desktop; + /* desktop windows are kept on all desktops */ + if (self->type != Type_Desktop) + actions[num++] = prop_atoms.net_wm_action_change_desktop; if (self->functions & Func_Shade) actions[num++] = prop_atoms.net_wm_action_shade; @@ -1154,6 +1188,9 @@ void client_update_wmhints(Client *self) void client_update_title(Client *self) { + GList *it; + guint32 nums; + guint i; char *data = NULL; g_free(self->title); @@ -1165,6 +1202,29 @@ void client_update_title(Client *self) data = g_strdup("Unnamed Window"); /* look for duplicates and append a number */ + nums = 0; + for (it = client_list; it; it = it->next) + if (it->data != self) { + Client *c = it->data; + if (0 == strncmp(c->title, data, strlen(data))) + nums |= 1 << c->title_count; + } + /* find first free number */ + for (i = 1; i <= 32; ++i) + if (!(nums & (1 << i))) { + if (self->title_count == 1 || i == 1) + self->title_count = i; + break; + } + /* dont display the number for the first window */ + if (self->title_count > 1) { + char *vdata, *ndata; + ndata = g_strdup_printf(" - [%u]", self->title_count); + vdata = g_strconcat(data, ndata, NULL); + g_free(ndata); + g_free(data); + data = vdata; + } PROP_SETS(self->window, net_wm_visible_name, data); @@ -1172,12 +1232,9 @@ void client_update_title(Client *self) if (self->frame) frame_adjust_title(self->frame); -} - -void client_update_icon_title(Client *self) -{ - char *data = NULL; + /* update the icon title */ + data = NULL; g_free(self->icon_title); /* try netwm */ @@ -1186,6 +1243,16 @@ void client_update_icon_title(Client *self) if (!PROP_GETS(self->window, wm_icon_name, locale, &data)) data = g_strdup("Unnamed Window"); + /* append the title count, dont display the number for the first window */ + if (self->title_count > 1) { + char *vdata, *ndata; + ndata = g_strdup_printf(" - [%u]", self->title_count); + vdata = g_strconcat(data, ndata, NULL); + g_free(ndata); + g_free(data); + data = vdata; + } + PROP_SETS(self->window, net_wm_visible_icon_name, data); self->icon_title = data; @@ -1342,28 +1409,70 @@ static void client_change_state(Client *self) frame_adjust_state(self->frame); } -static Client *search_focus_tree(Client *node, Client *skip) +Client *client_search_focus_tree(Client *self) { GSList *it; Client *ret; - for (it = node->transients; it != NULL; it = it->next) { - Client *c = it->data; - if (c == skip) continue; /* circular? */ - if ((ret = search_focus_tree(c, skip))) return ret; - if (client_focused(c)) return c; + for (it = self->transients; it != NULL; it = it->next) { + if (client_focused(it->data)) return it->data; + if ((ret = client_search_focus_tree(it->data))) return ret; } return NULL; } +Client *client_search_focus_tree_full(Client *self) +{ + if (self->transient_for) { + if (self->transient_for != TRAN_GROUP) { + return client_search_focus_tree_full(self->transient_for); + } else { + GSList *it; + + for (it = self->group->members; it; it = it->next) + if (((Client*)it->data)->transient_for != TRAN_GROUP) { + Client *c; + if ((c = client_search_focus_tree_full(it->data))) + return c; + } + return NULL; + } + } else { + /* this function checks the whole tree, the client_search_focus_tree + does not, so we need to check this window */ + if (client_focused(self)) + return self; + return client_search_focus_tree(self); + } +} + +static StackLayer calc_layer(Client *self) +{ + StackLayer l; + + if (self->iconic) l = Layer_Icon; + else if (self->fullscreen) l = Layer_Fullscreen; + else if (self->type == Type_Desktop) l = Layer_Desktop; + else if (self->type == Type_Dock) { + if (!self->below) l = Layer_Top; + else l = Layer_Normal; + } + else if (self->above) l = Layer_Above; + else if (self->below) l = Layer_Below; + else l = Layer_Normal; + + return l; +} + static void calc_recursive(Client *self, Client *orig, StackLayer l, gboolean raised) { - StackLayer old; + StackLayer old, own; GSList *it; old = self->layer; - self->layer = l; + own = calc_layer(self); + self->layer = l > own ? l : own; for (it = self->transients; it; it = it->next) calc_recursive(it->data, orig, l, raised ? raised : l != old); @@ -1376,7 +1485,6 @@ static void calc_recursive(Client *self, Client *orig, StackLayer l, void client_calc_layer(Client *self) { StackLayer l; - gboolean f; Client *orig; orig = self; @@ -1397,25 +1505,7 @@ void client_calc_layer(Client *self) } } - /* is us or one of our transients focused? */ - if (client_focused(self)) - f = TRUE; - else if (search_focus_tree(self, self)) - f = TRUE; - else - f = FALSE; - - if (self->iconic) l = Layer_Icon; - /* fullscreen windows are only in the fullscreen layer while focused */ - else if (self->fullscreen && f) l = Layer_Fullscreen; - else if (self->type == Type_Desktop) l = Layer_Desktop; - else if (self->type == Type_Dock) { - if (!self->below) l = Layer_Top; - else l = Layer_Normal; - } - else if (self->above) l = Layer_Above; - else if (self->below) l = Layer_Below; - else l = Layer_Normal; + l = calc_layer(self); calc_recursive(self, orig, l, FALSE); } @@ -1698,31 +1788,30 @@ void client_iconify(Client *self, gboolean iconic, gboolean curdesk) { GSList *it; - /* move up the transient chain as far as possible first if deiconifying */ - if (!iconic) - while (self->transient_for) { - if (self->transient_for != TRAN_GROUP) { - if (self->transient_for->iconic == iconic) - break; - self = self->transient_for; - } else { - GSList *it; - - /* the check for TRAN_GROUP is to prevent an infinate loop with - 2 transients of the same group at the head of the group's - members list */ - for (it = self->group->members; it; it = it->next) { - Client *c = it->data; + /* move up the transient chain as far as possible first */ + if (self->transient_for) { + if (self->transient_for != TRAN_GROUP) { + if (self->transient_for->iconic != iconic) { + client_iconify(self->transient_for, iconic, curdesk); + return; + } + } else { + GSList *it; - if (c != self && c->transient_for->iconic != iconic && - c->transient_for != TRAN_GROUP) { - self = it->data; - break; - } + /* the check for TRAN_GROUP is to prevent an infinate loop with + 2 transients of the same group at the head of the group's + members list */ + for (it = self->group->members; it; it = it->next) { + Client *c = it->data; + if (c != self && c->iconic != iconic && + c->transient_for != TRAN_GROUP) { + client_iconify(it->data, iconic, curdesk); + break; } - if (it == NULL) break; } + if (it != NULL) return; } + } if (self->iconic == iconic) return; /* nothing to do */ @@ -1738,13 +1827,18 @@ void client_iconify(Client *self, gboolean iconic, gboolean curdesk) and because the ICCCM tells us to! */ XUnmapWindow(ob_display, self->window); - /* update the focus lists.. iconic windows go to the bottom */ - focus_order_to_bottom(self); + /* update the focus lists.. iconic windows go to the bottom of the + list, put the new iconic window at the 'top of the bottom'. */ + focus_order_to_top(self); } else { if (curdesk) client_set_desktop(self, screen_desktop, FALSE); self->wmstate = self->shaded ? IconicState : NormalState; XMapWindow(ob_display, self->window); + + /* this puts it after the current focused window */ + focus_order_remove(self); + focus_order_add_new(self); } client_change_state(self); client_showhide(self); @@ -1941,25 +2035,19 @@ void client_set_desktop(Client *self, guint target, gboolean donthide) dispatch_client(Event_Client_Desktop, self, target, old); } -static Client *search_modal_tree(Client *node, Client *skip) +Client *client_search_modal_child(Client *self) { GSList *it; Client *ret; - for (it = node->transients; it != NULL; it = it->next) { + for (it = self->transients; it != NULL; it = it->next) { Client *c = it->data; - if (c == skip) continue; /* circular? */ - if ((ret = search_modal_tree(c, skip))) return ret; + if ((ret = client_search_modal_child(c))) return ret; if (c->modal) return c; } return NULL; } -Client *client_find_modal_child(Client *self) -{ - return search_modal_tree(self, self); -} - gboolean client_validate(Client *self) { XEvent e; @@ -2118,7 +2206,7 @@ Client *client_focus_target(Client *self) Client *child; /* if we have a modal child, then focus it, not us */ - child = client_find_modal_child(self); + child = client_search_modal_child(self); if (child) return child; return self; } diff --git a/openbox/client.h b/openbox/client.h index e7ee3085..faa04e63 100644 --- a/openbox/client.h +++ b/openbox/client.h @@ -112,6 +112,7 @@ typedef enum { typedef struct Client { Window window; + /*! The window's decorations. NULL while the window is being managed! */ struct Frame *frame; /*! The number of unmap events to ignore on the window */ @@ -133,10 +134,13 @@ typedef struct Client { GSList *transients; /*! The desktop on which the window resides (0xffffffff for all desktops) */ - unsigned int desktop; + guint desktop; /*! Normal window title */ gchar *title; + /*! The count for the title. When another window with the same title + exists, a count will be appended to it. */ + guint title_count; /*! Window title when iconified */ gchar *icon_title; @@ -341,6 +345,11 @@ gboolean client_focused(Client *self); void client_configure(Client *self, Corner anchor, int x, int y, int w, int h, gboolean user, gboolean final); +/*! Moves a client so that it is on screen if it is entirely out of the + viewable screen. +*/ +void client_move_onscreen(Client *self); + /*! Fullscreen's or unfullscreen's the client window @param fs true if the window should be made fullscreen; false if it should be returned to normal state. @@ -389,11 +398,6 @@ void client_kill(Client *self); desktop has been changed. Generally this should be FALSE. */ void client_set_desktop(Client *self, guint target, gboolean donthide); -/*! Return a modal child of the client window - @return A modal child of the client window, or 0 if none was found. -*/ -Client *client_find_modal_child(Client *self); - /*! Validate client, by making sure no Destroy or Unmap events exist in the event queue for the window. @return true if the client is valid; false if the client has already @@ -440,10 +444,8 @@ void client_update_normal_hints(Client *self); process. */ void client_update_wmhints(Client *self); -/*! Updates the window's title */ +/*! Updates the window's title and icon title */ void client_update_title(Client *self); -/*! Updates the window's icon title */ -void client_update_icon_title(Client *self); /*! Updates the window's application name and class */ void client_update_class(Client *self); /*! Updates the strut for the client */ @@ -464,4 +466,21 @@ void client_get_type(Client *self); Icon *client_icon(Client *self, int w, int h); +/*! Searches a client's transients for a focused window. The function does not + check for the passed client, only for its transients. + If no focused transient is found, NULL is returned. +*/ +Client *client_search_focus_tree(Client *self); + +/*! Searches a client's transient tree for a focused window. The function + searches up the tree and down other branches as well as the passed client's. + If no focused client is found, NULL is returned. +*/ +Client *client_search_focus_tree_full(Client *self); + +/*! Return a modal child of the client window + @return A modal child of the client window, or 0 if none was found. +*/ +Client *client_search_modal_child(Client *self); + #endif diff --git a/openbox/config.c b/openbox/config.c index db8856e6..959f6c2c 100644 --- a/openbox/config.c +++ b/openbox/config.c @@ -5,6 +5,7 @@ gboolean config_focus_new; gboolean config_focus_follow; gboolean config_focus_last; gboolean config_focus_last_on_desktop; +gboolean config_focus_popup; char *config_theme; @@ -37,6 +38,12 @@ static void parse_focus(char *name, ParseToken *value) else { config_focus_last_on_desktop = value->data.bool; } + } else if (!g_ascii_strcasecmp(name, "cyclingdialog")) { + if (value->type != TOKEN_BOOL) + yyerror("invalid value"); + else { + config_focus_popup = value->data.bool; + } } else yyerror("invalid option"); parse_free_token(value); @@ -95,6 +102,7 @@ void config_startup() config_focus_follow = FALSE; config_focus_last = TRUE; config_focus_last_on_desktop = TRUE; + config_focus_popup = TRUE; parse_reg_section("focus", NULL, parse_focus); diff --git a/openbox/config.h b/openbox/config.h index 27b00dd7..081561cb 100644 --- a/openbox/config.h +++ b/openbox/config.h @@ -11,6 +11,8 @@ extern gboolean config_focus_follow; extern gboolean config_focus_last; /*! Focus the last focused window as a fallback when switching desktops */ extern gboolean config_focus_last_on_desktop; +/*! Show a popup dialog while cycling focus */ +extern gboolean config_focus_popup; /* The name of the theme */ char *config_theme; diff --git a/openbox/event.c b/openbox/event.c index eea6583b..39dfca5c 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -144,6 +144,9 @@ static Window event_get_window(XEvent *e) case ConfigureRequest: window = e->xconfigurerequest.window; break; + case ConfigureNotify: + window = e->xconfigure.window; + break; default: #ifdef XKB if (extensions_xkb && e->type == extensions_xkb_event_basep) { @@ -308,6 +311,16 @@ static gboolean event_ignore(XEvent *e, Client *client) #ifdef DEBUG_FOCUS g_message("found pending FocusIn"); #endif + /* is the focused window getting a FocusOut/In back to + itself? */ + if (fe.xfocus.window == e->xfocus.window) { +#ifdef DEBUG_FOCUS + g_message("focused window got an Out/In back to " + "itself IGNORED both"); +#endif + return TRUE; + } + /* once all the FocusOut's have been dealt with, if there is a FocusIn still left and it is valid, then use it */ event_process(&fe); @@ -451,6 +464,21 @@ static void event_handle_root(XEvent *e) else if (e->xproperty.atom == prop_atoms.net_desktop_layout) screen_update_layout(); break; + case ConfigureNotify: +#ifdef XRANDR + XRRUpdateConfiguration(e); +#endif + if (e->xconfigure.width != screen_physical_size.width || + e->xconfigure.height != screen_physical_size.height) + screen_resize(e->xconfigure.width, e->xconfigure.height); + break; + default: + ; +#ifdef VIDMODE + if (extensions_vidmode && e->type == extensions_vidmode_event_basep) { + g_message("VIDMODE EVENT"); + } +#endif } } @@ -490,15 +518,25 @@ static void event_handle_client(Client *client, XEvent *e) } break; case FocusIn: +#ifdef DEBUG_FOCUS + g_message("FocusIn on client for %lx", client->window); +#endif focus_set_client(client); + frame_adjust_focus(client->frame, TRUE); + break; case FocusOut: #ifdef DEBUG_FOCUS - g_message("Focus%s on client for %lx", (e->type==FocusIn?"In":"Out"), - client->window); + g_message("FocusOut on client for %lx", client->window); #endif - /* focus state can affect the stacking layer */ - client_calc_layer(client); - frame_adjust_focus(client->frame, e->type == FocusIn); + /* are we a fullscreen window or a transient of one? (checks layer) + if we are then we need to be iconified since we are losing focus + */ + if (client->layer == Layer_Fullscreen && !client->iconic && + !client_search_focus_tree_full(client)) + /* iconify fullscreen windows when they and their transients + aren't focused */ + client_iconify(client, TRUE, TRUE); + frame_adjust_focus(client->frame, FALSE); break; case EnterNotify: if (client_normal(client)) { @@ -797,11 +835,10 @@ static void event_handle_client(Client *client, XEvent *e) client_setup_decor_and_functions(client); } else if (msgtype == prop_atoms.net_wm_name || - msgtype == prop_atoms.wm_name) - client_update_title(client); - else if (msgtype == prop_atoms.net_wm_icon_name || + msgtype == prop_atoms.wm_name || + msgtype == prop_atoms.net_wm_icon_name || msgtype == prop_atoms.wm_icon_name) - client_update_icon_title(client); + client_update_title(client); else if (msgtype == prop_atoms.wm_class) client_update_class(client); else if (msgtype == prop_atoms.wm_protocols) { diff --git a/openbox/extensions.c b/openbox/extensions.c index 3fe43194..be1ac547 100644 --- a/openbox/extensions.c +++ b/openbox/extensions.c @@ -7,7 +7,10 @@ gboolean extensions_shape = FALSE; int extensions_shape_event_basep; gboolean extensions_xinerama = FALSE; int extensions_xinerama_event_basep; - +gboolean extensions_randr = FALSE; +int extensions_randr_event_basep; +gboolean extensions_vidmode = FALSE; +int extensions_vidmode_event_basep; void extensions_query_all() { @@ -31,4 +34,16 @@ void extensions_query_all() XineramaQueryExtension(ob_display, &extensions_xinerama_event_basep, &junk); #endif + +#ifdef XRANDR + extensions_randr = + XRRQueryExtension(ob_display, &extensions_randr_event_basep, + &junk); +#endif + +#ifdef VIDMODE + extensions_vidmode = + XF86VidModeQueryExtension(ob_display, &extensions_vidmode_event_basep, + &junk); +#endif } diff --git a/openbox/extensions.h b/openbox/extensions.h index 3c11076a..cda1bf7e 100644 --- a/openbox/extensions.h +++ b/openbox/extensions.h @@ -11,6 +11,12 @@ #ifdef XINERAMA #include #endif +#ifdef XRANDR +#include +#endif +#ifdef VIDMODE +#include +#endif #include /*! Does the display have the XKB extension? */ @@ -28,6 +34,16 @@ extern gboolean extensions_xinerama; /*! Base for events for the Xinerama extension */ extern int extensions_xinerama_event_basep; +/*! Does the display have the RandR extension? */ +extern gboolean extensions_randr; +/*! Base for events for the Randr extension */ +extern int extensions_randr_event_basep; + +/*! Does the display have the VidMode extension? */ +extern gboolean extensions_vidmode; +/*! Base for events for the VidMode extension */ +extern int extensions_vidmode_event_basep; + void extensions_query_all(); #endif diff --git a/openbox/focus.c b/openbox/focus.c index 5e47dd28..7a2d079e 100644 --- a/openbox/focus.c +++ b/openbox/focus.c @@ -1,6 +1,5 @@ #include "event.h" #include "openbox.h" -#include "grab.h" #include "framerender.h" #include "client.h" #include "config.h" @@ -12,6 +11,7 @@ #include "focus.h" #include "parse.h" #include "stacking.h" +#include "popup.h" #include #include @@ -23,6 +23,7 @@ GList **focus_order = NULL; /* these lists are created when screen_startup Window focus_backup = None; static Client *focus_cycle_target = NULL; +static Popup *focus_cycle_popup = NULL; void focus_startup() { @@ -32,6 +33,7 @@ void focus_startup() XSetWindowAttributes attrib; focus_client = NULL; + focus_cycle_popup = popup_new(TRUE); attrib.override_redirect = TRUE; focus_backup = XCreateWindow(ob_display, ob_root, @@ -54,6 +56,9 @@ void focus_shutdown() g_free(focus_order); focus_order = NULL; + popup_free(focus_cycle_popup); + focus_cycle_popup = NULL; + XDestroyWindow(ob_display, focus_backup); /* reset focus to root */ @@ -98,9 +103,11 @@ void focus_set_client(Client *client) if (client != NULL) push_to_top(client); - /* set the NET_ACTIVE_WINDOW hint */ - active = client ? client->window : None; - PROP_SET32(ob_root, net_active_window, window, active); + /* set the NET_ACTIVE_WINDOW hint, but preserve it on shutdown */ + if (ob_state != State_Exiting) { + active = client ? client->window : None; + PROP_SET32(ob_root, net_active_window, window, active); + } if (focus_client != NULL) dispatch_client(Event_Client_Focus, focus_client, 0, 0); @@ -206,7 +213,11 @@ void focus_fallback(FallbackType type) for (it = focus_order[screen_desktop]; it != NULL; it = it->next) if (type != Fallback_Unfocusing || it->data != old) - if (client_normal(it->data) && client_focus(it->data)) + if (client_normal(it->data) && + /* dont fall back to 'anonymous' fullscreen windows. theres no + checks for this is in transient/group fallbacks. */ + !((Client*)it->data)->fullscreen && + client_focus(it->data)) return; /* nothing to focus */ @@ -215,38 +226,28 @@ void focus_fallback(FallbackType type) static void popup_cycle(Client *c, gboolean show) { - XSetWindowAttributes attrib; - static Window coords = None; - - if (coords == None) { - attrib.override_redirect = TRUE; - coords = XCreateWindow(ob_display, ob_root, - 0, 0, 1, 1, 0, render_depth, InputOutput, - render_visual, CWOverrideRedirect, &attrib); - g_assert(coords != None); - - grab_pointer(TRUE, None); - - XMapWindow(ob_display, coords); - } - if (!show) { - XDestroyWindow(ob_display, coords); - coords = None; - - grab_pointer(FALSE, None); + popup_hide(focus_cycle_popup); } else { Rect *a; - Size s; a = screen_area(c->desktop); - - framerender_size_popup_label(c->title, &s); - XMoveResizeWindow(ob_display, coords, - a->x + (a->width - s.width) / 2, - a->y + (a->height - s.height) / 2, - s.width, s.height); - framerender_popup_label(coords, &s, c->title); + popup_position(focus_cycle_popup, CenterGravity, + a->x + a->width / 2, a->y + a->height / 2); +/* popup_size(focus_cycle_popup, a->height/2, a->height/16); + popup_show(focus_cycle_popup, c->title, + client_icon(c, a->height/16, a->height/16)); +*/ + /* XXX the size and the font extents need to be related on some level + */ + popup_size(focus_cycle_popup, 320, 48); + + /* use the transient's parent's title/icon */ + while (c->transient_for && c->transient_for != TRAN_GROUP) + c = c->transient_for; + + popup_show(focus_cycle_popup, (c->iconic ? c->icon_title : c->title), + client_icon(c, 48, 48)); } } @@ -294,15 +295,18 @@ Client *focus_cycle(gboolean forward, gboolean linear, gboolean done, it = it->prev; if (it == NULL) it = g_list_last(list); } - ft = client_focus_target(it->data); - if (ft == it->data && client_normal(ft) && + /*ft = client_focus_target(it->data);*/ + ft = it->data; + if (ft->transients == NULL && /*ft == it->data &&*/client_normal(ft) && (ft->can_focus || ft->focus_notify) && (ft->desktop == screen_desktop || ft->desktop == DESKTOP_ALL)) { - if (focus_cycle_target) - frame_adjust_focus(focus_cycle_target->frame, FALSE); - focus_cycle_target = ft; - frame_adjust_focus(focus_cycle_target->frame, TRUE); - popup_cycle(ft, TRUE); + if (ft != focus_cycle_target) { /* prevents flicker */ + if (focus_cycle_target) + frame_adjust_focus(focus_cycle_target->frame, FALSE); + focus_cycle_target = ft; + frame_adjust_focus(focus_cycle_target->frame, TRUE); + } + popup_cycle(ft, config_focus_popup); return ft; } } while (it != start); diff --git a/openbox/framerender.c b/openbox/framerender.c index 0f4c6659..737dfe95 100644 --- a/openbox/framerender.c +++ b/openbox/framerender.c @@ -218,34 +218,3 @@ static void framerender_close(Frame *self, Appearance *a) theme_button_size, theme_button_size); paint(self->close, a); } - -void framerender_popup_label(Window win, Size *sz, char *text) -{ - Appearance *a; - - a = theme_app_hilite_label; - a->texture[0].data.text.string = text; - RECT_SET(a->area, 0, 0, sz->width, sz->height); - a->texture[0].position = a->area; - a->texture[0].position.x += theme_bevel; - a->texture[0].position.y += theme_bevel; - a->texture[0].position.width -= theme_bevel * 2; - a->texture[0].position.height -= theme_bevel * 2; - - XSetWindowBorderWidth(ob_display, win, theme_bwidth); - XSetWindowBorder(ob_display, win, theme_b_color->pixel); - - paint(win, a); -} - -void framerender_size_popup_label(char *text, Size *sz) -{ - Appearance *a; - - a = theme_app_hilite_label; - a->texture[0].data.text.string = text; - - appearance_minsize(a, &sz->width, &sz->height); - sz->width += theme_bevel * 2; - sz->height += theme_bevel * 2; -} diff --git a/openbox/framerender.h b/openbox/framerender.h index c02c9e7f..9655a932 100644 --- a/openbox/framerender.h +++ b/openbox/framerender.h @@ -5,7 +5,4 @@ void framerender_frame(Frame *self); -void framerender_popup_label(Window win, Size *sz, char *text); -void framerender_size_popup_label(char *text, Size *sz); - #endif diff --git a/openbox/menu.c b/openbox/menu.c index 42a183ab..88eef9c5 100644 --- a/openbox/menu.c +++ b/openbox/menu.c @@ -76,6 +76,16 @@ void menu_startup() menu_add_entry(m, menu_entry_new("--", NULL)); a = action_from_string("exit"); menu_add_entry(m, menu_entry_new("exit", a)); + + m = menu_new("client menu", "client", NULL); + a = action_from_string("iconify"); + menu_add_entry(m, menu_entry_new("iconify", a)); + a = action_from_string("toggleshade"); + menu_add_entry(m, menu_entry_new("(un)shade", a)); + a = action_from_string("togglemaximizefull"); + menu_add_entry(m, menu_entry_new("(un)maximize", a)); + a = action_from_string("close"); + menu_add_entry(m, menu_entry_new("close", a)); } void menu_shutdown() diff --git a/openbox/moveresize.c b/openbox/moveresize.c index 08d96ad6..7878b9f2 100644 --- a/openbox/moveresize.c +++ b/openbox/moveresize.c @@ -4,6 +4,7 @@ #include "client.h" #include "dispatch.h" #include "openbox.h" +#include "popup.h" #include #include @@ -13,7 +14,6 @@ Client *moveresize_client = NULL; static gboolean moving = FALSE; /* TRUE - moving, FALSE - resizing */ -static Window coords = None; static int start_x, start_y, start_cx, start_cy, start_cw, start_ch; static int cur_x, cur_y; static guint button; @@ -23,6 +23,8 @@ static Corner lockcorner; static guint button_return, button_escape, button_left, button_right, button_up, button_down; +static Popup *popup = NULL; + #define POPUP_X (10) #define POPUP_Y (10) @@ -34,29 +36,24 @@ void moveresize_startup() button_right = XKeysymToKeycode(ob_display, XStringToKeysym("Right")); button_up = XKeysymToKeycode(ob_display, XStringToKeysym("Up")); button_down = XKeysymToKeycode(ob_display, XStringToKeysym("Down")); + + popup = popup_new(FALSE); + popup_size_to_string(popup, "W: 0000 W: 0000"); + popup_position(popup, NorthWestGravity, POPUP_X, POPUP_Y); +} + +void moveresize_shutdown() +{ + popup_free(popup); + popup = NULL; } static void popup_coords(char *format, int a, int b) { - XSetWindowAttributes attrib; - Size s; char *text; - if (coords == None) { - attrib.override_redirect = TRUE; - coords = XCreateWindow(ob_display, ob_root, - 0, 0, 1, 1, 0, render_depth, InputOutput, - render_visual, CWOverrideRedirect, &attrib); - g_assert(coords != None); - - XMapWindow(ob_display, coords); - } - text = g_strdup_printf(format, a, b); - framerender_size_popup_label(text, &s); - XMoveResizeWindow(ob_display, coords, - POPUP_X, POPUP_Y, s.width, s.height); - framerender_popup_label(coords, &s, text); + popup_show(popup, text, NULL); g_free(text); } @@ -127,8 +124,7 @@ void moveresize_end(gboolean cancel) grab_keyboard(FALSE); grab_pointer(FALSE, None); - XDestroyWindow(ob_display, coords); - coords = None; + popup_hide(popup); if (moving) { client_configure(moveresize_client, Corner_TopLeft, @@ -156,7 +152,9 @@ static void do_move() client_configure(moveresize_client, Corner_TopLeft, cur_x, cur_y, start_cw, start_ch, TRUE, FALSE); - popup_coords("X: %d Y: %d", moveresize_client->frame->area.x, + /* this would be better with a fixed width font ... XXX can do it better + if there are 2 text boxes */ + popup_coords("X: %4d Y: %4d", moveresize_client->frame->area.x, moveresize_client->frame->area.y); } @@ -178,7 +176,9 @@ static void do_resize() client_configure(moveresize_client, lockcorner, moveresize_client->area.x, moveresize_client->area.y, cur_x, cur_y, TRUE, FALSE); - popup_coords("W: %d H: %d", moveresize_client->logical_size.width, + /* this would be better with a fixed width font ... XXX can do it better + if there are 2 text boxes */ + popup_coords("W: %4d H: %4d", moveresize_client->logical_size.width, moveresize_client->logical_size.height); } diff --git a/openbox/moveresize.h b/openbox/moveresize.h index ba717cd8..4b9cdac4 100644 --- a/openbox/moveresize.h +++ b/openbox/moveresize.h @@ -9,6 +9,7 @@ extern gboolean moveresize_in_progress; extern Client *moveresize_client; void moveresize_startup(); +void moveresize_shutdown(); void moveresize_start(Client *c, int x, int y, guint button, guint32 corner); void moveresize_end(gboolean cancel); diff --git a/openbox/openbox.c b/openbox/openbox.c index ea9b895f..304d52c6 100644 --- a/openbox/openbox.c +++ b/openbox/openbox.c @@ -166,7 +166,6 @@ int main(int argc, char **argv) font_startup(); theme_startup(); event_startup(); - moveresize_startup(); grab_startup(); plugin_startup(); /* load the plugins specified in the pluginrc */ @@ -187,6 +186,7 @@ int main(int argc, char **argv) menu_startup(); frame_startup(); stacking_startup(); + moveresize_startup(); focus_startup(); screen_startup(); group_startup(); @@ -210,6 +210,7 @@ int main(int argc, char **argv) group_shutdown(); screen_shutdown(); focus_shutdown(); + moveresize_shutdown(); stacking_shutdown(); frame_shutdown(); menu_shutdown(); diff --git a/openbox/parse.h b/openbox/parse.h index 3ce5ebbd..abd5bb2d 100644 --- a/openbox/parse.h +++ b/openbox/parse.h @@ -11,7 +11,7 @@ typedef enum { TOKEN_INTEGER = INTEGER, TOKEN_STRING = STRING, TOKEN_IDENTIFIER = IDENTIFIER, - TOKEN_BOOL = BOOL, + TOKEN_BOOL = BOOLEAN, TOKEN_LIST, TOKEN_LBRACE = '{', TOKEN_RBRACE = '}', diff --git a/openbox/parse.l b/openbox/parse.l index 3762f335..a7cd6c63 100644 --- a/openbox/parse.l +++ b/openbox/parse.l @@ -31,7 +31,7 @@ bool ([tT][rR][uU][eE]|[fF][aA][lL][sS][eE]|[yY][eE][sS]|[nN][oO]|[oO][nN]|[oO][ {bool} { yylval.bool = (!g_ascii_strcasecmp("true", yytext) || !g_ascii_strcasecmp("yes", yytext) || !g_ascii_strcasecmp("on", yytext)); - return BOOL; + return BOOLEAN; } {identifier} { yylval.identifier = g_strdup(yytext); return IDENTIFIER; } [{}()\[\]=,] { yylval.character = *yytext; return *yytext; } diff --git a/openbox/parse.y b/openbox/parse.y index aa59fe4c..7e92efd6 100644 --- a/openbox/parse.y +++ b/openbox/parse.y @@ -41,7 +41,7 @@ void parse_set_section(char *section); %token INTEGER %token STRING %token IDENTIFIER -%token BOOL +%token BOOLEAN %token '(' %token ')' %token '{' @@ -78,7 +78,7 @@ token: | INTEGER { t.type = TOKEN_INTEGER; t.data.integer = $1; } | STRING { t.type = TOKEN_STRING; t.data.string = $1; } | IDENTIFIER { t.type = TOKEN_IDENTIFIER; t.data.identifier = $1; } - | BOOL { t.type = TOKEN_BOOL; t.data.bool = $1; } + | BOOLEAN { t.type = TOKEN_BOOL; t.data.bool = $1; } | list { t.type = TOKEN_LIST; t.data.list = $1; } | '{' { t.type = $1; t.data.character = $1; } | '}' { t.type = $1; t.data.character = $1; } @@ -107,7 +107,7 @@ listtoken: | INTEGER { t.type = TOKEN_INTEGER; t.data.integer = $1; } | STRING { t.type = TOKEN_STRING; t.data.string = $1; } | IDENTIFIER { t.type = TOKEN_IDENTIFIER; t.data.identifier = $1; } - | BOOL { t.type = TOKEN_BOOL; t.data.bool = $1; } + | BOOLEAN { t.type = TOKEN_BOOL; t.data.bool = $1; } | list { t.type = TOKEN_LIST; t.data.list = $1; } | '{' { t.type = $1; t.data.character = $1; } | '}' { t.type = $1; t.data.character = $1; } diff --git a/openbox/screen.c b/openbox/screen.c index 7f9b4397..04bc21c5 100644 --- a/openbox/screen.c +++ b/openbox/screen.c @@ -7,6 +7,7 @@ #include "frame.h" #include "focus.h" #include "dispatch.h" +#include "extensions.h" #include "../render/render.h" #include @@ -16,7 +17,7 @@ #endif /*! The event mask to grab on the root window */ -#define ROOT_EVENTMASK (/*ColormapChangeMask |*/ PropertyChangeMask | \ +#define ROOT_EVENTMASK (StructureNotifyMask | PropertyChangeMask | \ EnterWindowMask | LeaveWindowMask | \ SubstructureNotifyMask | SubstructureRedirectMask | \ ButtonPressMask | ButtonReleaseMask | ButtonMotionMask) @@ -160,7 +161,8 @@ void screen_startup() guint i; /* get the initial size */ - screen_resize(); + screen_resize(WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen)), + HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen))); /* set the names */ screen_desktop_names = g_new(char*, @@ -201,14 +203,14 @@ void screen_shutdown() g_free(area); } -void screen_resize() +void screen_resize(int w, int h) { - /* XXX RandR support here? */ + GList *it; guint32 geometry[2]; /* Set the _NET_DESKTOP_GEOMETRY hint */ - geometry[0] = WidthOfScreen(ScreenOfDisplay(ob_display, ob_screen)); - geometry[1] = HeightOfScreen(ScreenOfDisplay(ob_display, ob_screen)); + geometry[0] = w; + geometry[1] = h; PROP_SETA32(ob_root, net_desktop_geometry, cardinal, geometry, 2); screen_physical_size.width = geometry[0]; screen_physical_size.height = geometry[1]; @@ -218,7 +220,8 @@ void screen_resize() screen_update_struts(); - /* XXX adjust more stuff ? */ + for (it = client_list; it; it = it->next) + client_move_onscreen(it->data); } void screen_set_num_desktops(guint num) diff --git a/openbox/screen.h b/openbox/screen.h index f68b3beb..f493bd82 100644 --- a/openbox/screen.h +++ b/openbox/screen.h @@ -43,7 +43,7 @@ void screen_startup(); void screen_shutdown(); /*! Figure out the new size of the screen and adjust stuff for it */ -void screen_resize(); +void screen_resize(int w, int h); /*! Change the number of available desktops */ void screen_set_num_desktops(guint num); diff --git a/openbox/stacking.c b/openbox/stacking.c index 6ff01721..ab1e1955 100644 --- a/openbox/stacking.c +++ b/openbox/stacking.c @@ -70,7 +70,7 @@ static GList *find_lowest_transient(Client *c) static void raise_recursive(Client *client) { Window wins[2]; /* only ever restack 2 windows. */ - GList *it; + GList *it, *low; GSList *sit; g_assert(stacking_list != NULL); /* this would be bad */ @@ -86,15 +86,15 @@ static void raise_recursive(Client *client) /* find 'it' where it is the positiion in the stacking order where 'client' will be inserted *before* */ - it = find_lowest_transient(client); - if (it) - it = it->next; - else { - /* the stacking list is from highest to lowest */ - for (it = stacking_list; it; it = it->next) { - if (client->layer >= ((Client*)it->data)->layer) - break; + low = find_lowest_transient(client); + /* the stacking list is from highest to lowest */ + for (it = g_list_last(stacking_list); it; it = it->prev) { + if (it == low || client->layer < ((Client*)it->data)->layer) { + it = it->next; + break; } + if (it == stacking_list) + break; } /* diff --git a/plugins/keyboard/keyboard.c b/plugins/keyboard/keyboard.c index bf670c49..acda951a 100644 --- a/plugins/keyboard/keyboard.c +++ b/plugins/keyboard/keyboard.c @@ -56,12 +56,15 @@ gboolean kbind(GList *keylist, Action *action) if (!(tree = tree_build(keylist))) return FALSE; + if ((t = tree_find(tree, &conflict)) != NULL) { - /* already bound to something */ - g_message("keychain is already bound"); + /* already bound to something, use the existing tree */ tree_destroy(tree); - return FALSE; - } + tree = NULL; + } else + t = tree; + while (t->first_child) t = t->first_child; + if (conflict) { g_message("conflict with binding"); tree_destroy(tree); @@ -73,12 +76,10 @@ gboolean kbind(GList *keylist, Action *action) grab_keys(FALSE); /* set the action */ - t = tree; - while (t->first_child) t = t->first_child; - t->action = action; + t->actions = g_slist_append(t->actions, action); /* assimilate this built tree into the main tree. assimilation destroys/uses the tree */ - tree_assimilate(tree); + if (tree) tree_assimilate(tree); grab_keys(TRUE); grab_server(FALSE); @@ -100,17 +101,25 @@ static void event(ObEvent *e, void *foo) if (e->data.x.e->xkey.keycode == button_return) done = TRUE; else if (e->data.x.e->xkey.keycode == button_escape) { - grabbed_key->action->data.cycle.cancel = TRUE; + GSList *it; + for (it = grabbed_key->actions; it; it = it->next) { + Action *act = it->data; + act->data.cycle.cancel = TRUE; + } done = TRUE; } } - if (done) { - grabbed_key->action->data.cycle.final = TRUE; - grabbed_key->action->func(&grabbed_key->action->data); - grab_keyboard(FALSE); - grabbed_key = NULL; - reset_chains(); - return; + if (done) { + GSList *it; + for (it = grabbed_key->actions; it; it = it->next) { + Action *act = it->data; + act->data.cycle.final = TRUE; + act->func(&act->data); + grab_keyboard(FALSE); + grabbed_key = NULL; + reset_chains(); + return; + } } } if (e->type == Event_X_KeyRelease) @@ -140,20 +149,24 @@ static void event(ObEvent *e, void *foo) } curpos = p; } else { - if (p->action->func != NULL) { - p->action->data.any.c = focus_client; - - if (p->action->func == action_cycle_windows) { - p->action->data.cycle.final = FALSE; - p->action->data.cycle.cancel = FALSE; - } - - p->action->func(&p->action->data); - - if (p->action->func == action_cycle_windows && - !grabbed_key) { - grab_keyboard(TRUE); - grabbed_key = p; + GSList *it; + for (it = p->actions; it; it = it->next) { + Action *act = it->data; + if (act->func != NULL) { + act->data.any.c = focus_client; + + if (act->func == action_cycle_windows) { + act->data.cycle.final = FALSE; + act->data.cycle.cancel = FALSE; + } + + act->func(&act->data); + + if (act->func == action_cycle_windows && + !grabbed_key) { + grab_keyboard(TRUE); + grabbed_key = p; + } } } diff --git a/plugins/keyboard/tree.c b/plugins/keyboard/tree.c index c0ea2f49..896f77bb 100644 --- a/plugins/keyboard/tree.c +++ b/plugins/keyboard/tree.c @@ -11,10 +11,13 @@ void tree_destroy(KeyBindingTree *tree) c = tree->first_child; if (c == NULL) { GList *it; + GSList *sit; for (it = tree->keylist; it != NULL; it = it->next) g_free(it->data); g_list_free(tree->keylist); - action_free(tree->action); + for (it = tree->actions; it != NULL; it = it->next) + action_free(it->data); + g_slist_free(tree->actions); } g_free(tree); tree = c; diff --git a/plugins/keyboard/tree.h b/plugins/keyboard/tree.h index 8750c94e..aefb2565 100644 --- a/plugins/keyboard/tree.h +++ b/plugins/keyboard/tree.h @@ -8,7 +8,7 @@ typedef struct KeyBindingTree { guint state; guint key; GList *keylist; - Action *action; + GSList *actions; /* list of Action pointers */ /* the next binding in the tree at the same level */ struct KeyBindingTree *next_sibling; diff --git a/plugins/mouse/mouse.c b/plugins/mouse/mouse.c index caf4e554..8df8e2a0 100644 --- a/plugins/mouse/mouse.c +++ b/plugins/mouse/mouse.c @@ -96,9 +96,13 @@ static void clearall() int j; MouseBinding *b = it->data; - for (j = 0; j < NUM_MOUSEACTION; ++j) - if (b->action[j] != NULL) - action_free(b->action[j]); + for (j = 0; j < NUM_MOUSEACTION; ++j) { + GSList *it; + for (it = b->actions[j]; it; it = it->next) { + action_free(it->data); + } + g_slist_free(b->actions[j]); + } g_free(b); } g_slist_free(bound_contexts[i]); @@ -119,17 +123,20 @@ static void fire_button(MouseAction a, Context context, Client *c, guint state, /* if not bound, then nothing to do! */ if (it == NULL) return; - if (b->action[a] != NULL && b->action[a]->func != NULL) { - b->action[a]->data.any.c = c; + for (it = b->actions[a]; it; it = it->next) { + Action *act = it->data; + if (act->func != NULL) { + act->data.any.c = c; - g_assert(b->action[a]->func != action_moveresize); + g_assert(act->func != action_moveresize); - if (b->action[a]->func == action_showmenu) { - b->action[a]->data.showmenu.x = x; - b->action[a]->data.showmenu.y = y; - } + if (act->func == action_showmenu) { + act->data.showmenu.x = x; + act->data.showmenu.y = y; + } - b->action[a]->func(&b->action[a]->data); + act->func(&act->data); + } } } @@ -148,24 +155,27 @@ static void fire_motion(MouseAction a, Context context, Client *c, /* if not bound, then nothing to do! */ if (it == NULL) return; - if (b->action[a] != NULL && b->action[a]->func != NULL) { - b->action[a]->data.any.c = c; - - if (b->action[a]->func == action_moveresize) { - b->action[a]->data.moveresize.x = x_root; - b->action[a]->data.moveresize.y = y_root; - b->action[a]->data.moveresize.button = button; - if (!(b->action[a]->data.moveresize.corner == - prop_atoms.net_wm_moveresize_move || - b->action[a]->data.moveresize.corner == - prop_atoms.net_wm_moveresize_move_keyboard || - b->action[a]->data.moveresize.corner == - prop_atoms.net_wm_moveresize_size_keyboard)) - b->action[a]->data.moveresize.corner = corner; - } else - g_assert_not_reached(); + for (it = b->actions[a]; it; it = it->next) { + Action *act = it->data; + if (act->func != NULL) { + act->data.any.c = c; + + if (act->func == action_moveresize) { + act->data.moveresize.x = x_root; + act->data.moveresize.y = y_root; + act->data.moveresize.button = button; + if (!(act->data.moveresize.corner == + prop_atoms.net_wm_moveresize_move || + act->data.moveresize.corner == + prop_atoms.net_wm_moveresize_move_keyboard || + act->data.moveresize.corner == + prop_atoms.net_wm_moveresize_size_keyboard)) + act->data.moveresize.corner = corner; + } else + g_assert_not_reached(); - b->action[a]->func(&b->action[a]->data); + act->func(&act->data); + } } } @@ -338,12 +348,7 @@ gboolean mbind(char *buttonstr, char *contextstr, MouseAction mact, for (it = bound_contexts[context]; it != NULL; it = it->next){ b = it->data; if (b->state == state && b->button == button) { - /* already bound */ - if (b->action[mact] != NULL) { - g_warning("duplicate binding"); - return FALSE; - } - b->action[mact] = action; + b->actions[mact] = g_slist_append(b->actions[mact], action); return TRUE; } } @@ -354,7 +359,7 @@ gboolean mbind(char *buttonstr, char *contextstr, MouseAction mact, b = g_new0(MouseBinding, 1); b->state = state; b->button = button; - b->action[mact] = action; + b->actions[mact] = g_slist_append(NULL, action); bound_contexts[context] = g_slist_append(bound_contexts[context], b); grab_all_clients(TRUE); diff --git a/plugins/mouse/mouse.h b/plugins/mouse/mouse.h index 832ada1f..408ae964 100644 --- a/plugins/mouse/mouse.h +++ b/plugins/mouse/mouse.h @@ -15,7 +15,7 @@ typedef enum { typedef struct { guint state; guint button; - Action *action[NUM_MOUSEACTION]; + GSList *actions[NUM_MOUSEACTION]; /* lists of Action pointers */ } MouseBinding; gboolean mbind(char *buttonstr, char *contextstr, MouseAction mact, diff --git a/render/font.c b/render/font.c index 7ffcb53b..504063b9 100644 --- a/render/font.c +++ b/render/font.c @@ -8,6 +8,10 @@ #include #include "../kernel/geom.h" +#define ELIPSES "..." +#define ELIPSES_LENGTH(font, shadow, offset) \ + (font->elipses_length + (shadow ? offset : 0)) + void font_startup(void) { #ifdef DEBUG @@ -38,6 +42,11 @@ static void measure_height(ObFont *f) XftTextExtentsUtf8(ob_display, f->xftfont, (FcChar8*)str, strlen(str), &info); f->height = (signed) info.height; + + /* measure an elipses */ + XftTextExtentsUtf8(ob_display, f->xftfont, + (FcChar8*)ELIPSES, strlen(ELIPSES), &info); + f->elipses_length = (signed) info.xOff; } ObFont *font_open(char *fontstring) @@ -98,9 +107,10 @@ void font_draw(XftDraw *d, TextureText *t, Rect *position) { int x,y,w,h; XftColor c; - char *text; - int m; + GString *text; + int m, em; size_t l; + gboolean shortened = FALSE; y = position->y; w = position->width; @@ -110,12 +120,22 @@ void font_draw(XftDraw *d, TextureText *t, Rect *position) y -= (2 * (t->font->xftfont->ascent + t->font->xftfont->descent) - (t->font->height + h) - 1) / 2; - text = g_strdup(t->string); - l = strlen(text); - m = font_measure_string(t->font, text, t->shadow, t->offset); + text = g_string_new(t->string); + l = g_utf8_strlen(text->str, -1); + m = font_measure_string(t->font, text->str, t->shadow, t->offset); while (l && m > position->width) { - text[--l] = '\0'; - m = font_measure_string(t->font, text, t->shadow, t->offset); + shortened = TRUE; + /* remove a character from the middle */ + text = g_string_erase(text, l-- / 2, 1); + em = ELIPSES_LENGTH(t->font, t->shadow, t->offset); + /* if the elipses are too large, don't show them at all */ + if (em > position->width) + shortened = FALSE; + m = font_measure_string(t->font, text->str, t->shadow, t->offset) + em; + } + if (shortened) { + text = g_string_insert(text, (l + 1) / 2, ELIPSES); + l += 3; } if (!l) return; @@ -147,7 +167,7 @@ void font_draw(XftDraw *d, TextureText *t, Rect *position) } XftDrawStringUtf8(d, &c, t->font->xftfont, x + t->offset, t->font->xftfont->ascent + y + t->offset, - (FcChar8*)text, l); + (FcChar8*)text->str, l); } c.color.red = t->color->r | t->color->r << 8; c.color.green = t->color->g | t->color->g << 8; @@ -157,6 +177,6 @@ void font_draw(XftDraw *d, TextureText *t, Rect *position) XftDrawStringUtf8(d, &c, t->font->xftfont, x, t->font->xftfont->ascent + y, - (FcChar8*)text, l); + (FcChar8*)text->str, l); return; } diff --git a/render/image.c b/render/image.c index bdc51eed..e0c5540f 100644 --- a/render/image.c +++ b/render/image.c @@ -5,66 +5,72 @@ void image_draw(pixel32 *target, TextureRGBA *rgba, Rect *position, Rect *surarea) { - unsigned long *draw = rgba->data; - int c, sfw, sfh; - unsigned int i, e; - sfw = position->width; - sfh = position->height; + gulong *draw = rgba->data; + guint c, i, e, t, sfw, sfh; + sfw = position->width; + sfh = position->height; - /* it would be nice if this worked, but this function is well broken in these - cercumstances. */ - g_assert(position->width == surarea->width && - position->height == surarea->height); + /* it would be nice if this worked, but this function is well broken in + these circumstances. */ + g_assert(position->width == surarea->width && + position->height == surarea->height); - g_assert(rgba->data != NULL); + g_assert(rgba->data != NULL); - if ((rgba->width != sfw || rgba->height != sfh) && - (rgba->width != rgba->cwidth || rgba->height != rgba->cheight)) { - double dx = rgba->width / (double)sfw; - double dy = rgba->height / (double)sfh; - double px = 0.0; - double py = 0.0; - int iy = 0; + if ((rgba->width != sfw || rgba->height != sfh) && + (rgba->width != rgba->cwidth || rgba->height != rgba->cheight)) { + double dx = rgba->width / (double)sfw; + double dy = rgba->height / (double)sfh; + double px = 0.0; + double py = 0.0; + int iy = 0; - /* scale it and cache it */ - if (rgba->cache != NULL) - g_free(rgba->cache); - rgba->cache = g_new(unsigned long, sfw * sfh); - rgba->cwidth = sfw; - rgba->cheight = sfh; - for (i = 0, c = 0, e = sfw*sfh; i < e; ++i) { - rgba->cache[i] = rgba->data[(int)px + iy]; - if (++c >= sfw) { - c = 0; - px = 0; - py += dy; - iy = (int)py * rgba->width; - } else - px += dx; - } + /* scale it and cache it */ + if (rgba->cache != NULL) + g_free(rgba->cache); + rgba->cache = g_new(unsigned long, sfw * sfh); + rgba->cwidth = sfw; + rgba->cheight = sfh; + for (i = 0, c = 0, e = sfw*sfh; i < e; ++i) { + rgba->cache[i] = rgba->data[(int)px + iy]; + if (++c >= sfw) { + c = 0; + px = 0; + py += dy; + iy = (int)py * rgba->width; + } else + px += dx; + } + + /* do we use the cache we may have just created, or the original? */ + if (rgba->width != sfw || rgba->height != sfh) + draw = rgba->cache; + + /* apply the alpha channel */ + for (i = 0, c = 0, t = position->x, e = sfw*sfh; i < e; ++i, ++t) { + guchar alpha, r, g, b, bgr, bgg, bgb; -/* do we use the cache we may have just created, or the original? */ - if (rgba->width != sfw || rgba->height != sfh) - draw = rgba->cache; + alpha = draw[i] >> 24; + r = draw[i] >> 16; + g = draw[i] >> 8; + b = draw[i]; - /* apply the alpha channel */ - for (i = 0, c = 0, e = sfw*sfh; i < e; ++i) { - unsigned char alpha = draw[i] >> 24; - unsigned char r = draw[i] >> 16; - unsigned char g = draw[i] >> 8; - unsigned char b = draw[i]; + if (c >= sfw) { + c = 0; + t += surarea->width - sfw; + } - /* background color */ - unsigned char bgr = target[i] >> default_red_shift; - unsigned char bgg = target[i] >> default_green_shift; - unsigned char bgb = target[i] >> default_blue_shift; + /* background color */ + bgr = target[t] >> default_red_shift; + bgg = target[t] >> default_green_shift; + bgb = target[t] >> default_blue_shift; - r = bgr + (((r - bgr) * alpha) >> 8); - g = bgg + (((g - bgg) * alpha) >> 8); - b = bgb + (((b - bgb) * alpha) >> 8); + r = bgr + (((r - bgr) * alpha) >> 8); + g = bgg + (((g - bgg) * alpha) >> 8); + b = bgb + (((b - bgb) * alpha) >> 8); - target[i] = (r << default_red_shift) | (g << default_green_shift) | - (b << default_blue_shift); + target[t] = (r << default_red_shift) | (g << default_green_shift) | + (b << default_blue_shift); + } } - } } diff --git a/render/render.c b/render/render.c index a1412741..804e6c36 100644 --- a/render/render.c +++ b/render/render.c @@ -7,6 +7,7 @@ #include "mask.h" #include "color.h" #include "image.h" +#include "theme.h" #include "kernel/openbox.h" #ifdef HAVE_STDLIB_H @@ -421,6 +422,7 @@ void pixel32_to_pixmap(pixel32 *in, Pixmap out, int x, int y, int w, int h) void appearance_minsize(Appearance *l, int *w, int *h) { int i; + int m; *w = *h = 1; switch (l->surface.type) { @@ -437,20 +439,22 @@ void appearance_minsize(Appearance *l, int *w, int *h) } else if (l->surface.data.planar.border) *w = *h = 2; - for (i = 0; i < l->textures; ++i) + for (i = 0; i < l->textures; ++i) { switch (l->texture[i].type) { case Bitmask: *w += l->texture[i].data.mask.mask->w; *h += l->texture[i].data.mask.mask->h; break; case Text: - *w +=font_measure_string(l->texture[i].data.text.font, - l->texture[i].data.text.string, - l->texture[i].data.text.shadow, - l->texture[i].data.text.offset); - *h += font_height(l->texture[i].data.text.font, - l->texture[i].data.text.shadow, - l->texture[i].data.text.offset); + m = font_measure_string(l->texture[i].data.text.font, + l->texture[i].data.text.string, + l->texture[i].data.text.shadow, + l->texture[i].data.text.offset); + *w += m; + m = font_height(l->texture[i].data.text.font, + l->texture[i].data.text.shadow, + l->texture[i].data.text.offset); + *h += m; break; case RGBA: *w += l->texture[i].data.rgba.width; @@ -458,7 +462,8 @@ void appearance_minsize(Appearance *l, int *w, int *h) break; case NoTexture: break; - } + } + } break; } } diff --git a/render/render.h b/render/render.h index d98cebde..006eaf4c 100644 --- a/render/render.h +++ b/render/render.h @@ -79,6 +79,7 @@ typedef struct Surface { typedef struct { XftFont *xftfont; int height; + int elipses_length; } ObFont; typedef enum { @@ -109,12 +110,12 @@ typedef struct TextureMask { } TextureMask; typedef struct TextureRGBA { - int width; - int height; + guint width; + guint height; unsigned long *data; /* cached scaled so we don't have to scale often */ - int cwidth; - int cheight; + guint cwidth; + guint cheight; unsigned long *cache; } TextureRGBA; diff --git a/render/theme.c b/render/theme.c index eccc2c8a..39079bd8 100644 --- a/render/theme.c +++ b/render/theme.c @@ -93,8 +93,11 @@ Appearance *theme_a_menu_item; Appearance *theme_a_menu_disabled; Appearance *theme_a_menu_hilite; +Appearance *theme_app_hilite_bg; +Appearance *theme_app_unhilite_bg; Appearance *theme_app_hilite_label; Appearance *theme_app_unhilite_label; +Appearance *theme_app_icon; void theme_startup() { @@ -151,8 +154,11 @@ void theme_startup() theme_a_menu_disabled = appearance_new(Surface_Planar, 1); theme_a_menu_hilite = appearance_new(Surface_Planar, 1); + theme_app_hilite_bg = appearance_new(Surface_Planar, 0); + theme_app_unhilite_bg = appearance_new(Surface_Planar, 0); theme_app_hilite_label = appearance_new(Surface_Planar, 1); theme_app_unhilite_label = appearance_new(Surface_Planar, 1); + theme_app_icon = appearance_new(Surface_Planar, 1); } @@ -221,8 +227,11 @@ void theme_shutdown() appearance_free(theme_a_menu_item); appearance_free(theme_a_menu_disabled); appearance_free(theme_a_menu_hilite); + appearance_free(theme_app_hilite_bg); + appearance_free(theme_app_unhilite_bg); appearance_free(theme_app_hilite_label); appearance_free(theme_app_unhilite_label); + appearance_free(theme_app_icon); } static XrmDatabase loaddb(char *theme) @@ -330,7 +339,7 @@ static gboolean read_mask(XrmDatabase db, char *rname, char *theme, if (XrmGetResource(db, rname, rclass, &rettype, &retvalue) && retvalue.addr != NULL) { - button_dir = g_strdup_printf("%s_buttons", theme); + button_dir = g_strdup_printf("%s_data", theme); s = g_build_filename(g_get_home_dir(), ".openbox", "themes", button_dir, retvalue.addr, NULL); @@ -348,7 +357,7 @@ static gboolean read_mask(XrmDatabase db, char *rname, char *theme, g_free(s); themename = g_path_get_basename(theme); - s = g_strdup_printf("%s/%s_buttons/%s", theme, + s = g_strdup_printf("%s/%s_data/%s", theme, themename, retvalue.addr); g_free(themename); if (XReadBitmapFileData(s, &w, &h, &b, &hx, &hy) == @@ -728,26 +737,15 @@ char *theme_load(char *theme) if (!read_appearance(db, "menu.hilite", theme_a_menu_hilite)) set_default_appearance(theme_a_menu_hilite); - /* read the appearances for rendering non-decorations. these cannot be - parent-relative */ - if (theme_a_focused_label->surface.data.planar.grad != - Background_ParentRelative) { - if (!read_appearance(db, "window.label.focus", theme_app_hilite_label)) - set_default_appearance(theme_app_hilite_label); - } else { - if (!read_appearance(db, "window.title.focus", theme_app_hilite_label)) - set_default_appearance(theme_app_hilite_label); - } - if (theme_a_unfocused_label->surface.data.planar.grad != - Background_ParentRelative) { - if (!read_appearance(db, "window.label.unfocus", - theme_app_unhilite_label)) - set_default_appearance(theme_app_unhilite_label); - } else { - if (!read_appearance(db, "window.title.unfocus", - theme_app_unhilite_label)) - set_default_appearance(theme_app_unhilite_label); - } + /* read the appearances for rendering non-decorations */ + if (!read_appearance(db, "window.title.focus", theme_app_hilite_bg)) + set_default_appearance(theme_app_hilite_bg); + if (!read_appearance(db, "window.label.focus", theme_app_hilite_label)) + set_default_appearance(theme_app_hilite_label); + if (!read_appearance(db, "window.title.unfocus", theme_app_unhilite_bg)) + set_default_appearance(theme_app_unhilite_bg); + if (!read_appearance(db, "window.label.unfocus", theme_app_unhilite_label)) + set_default_appearance(theme_app_unhilite_label); /* read buttons textures */ if (!read_appearance(db, "window.button.pressed.focus", @@ -817,8 +815,8 @@ char *theme_load(char *theme) /* set up the textures */ theme_a_focused_label->texture[0].type = theme_app_hilite_label->texture[0].type = Text; - theme_a_focused_label->texture[0].data.text.justify = - theme_app_hilite_label->texture[0].data.text.justify = winjust; + theme_a_focused_label->texture[0].data.text.justify = winjust; + theme_app_hilite_label->texture[0].data.text.justify = Justify_Left; theme_a_focused_label->texture[0].data.text.font = theme_app_hilite_label->texture[0].data.text.font = theme_winfont; theme_a_focused_label->texture[0].data.text.shadow = @@ -836,8 +834,8 @@ char *theme_load(char *theme) theme_a_unfocused_label->texture[0].type = theme_app_unhilite_label->texture[0].type = Text; - theme_a_unfocused_label->texture[0].data.text.justify = - theme_app_unhilite_label->texture[0].data.text.justify = winjust; + theme_a_unfocused_label->texture[0].data.text.justify = winjust; + theme_app_unhilite_label->texture[0].data.text.justify = Justify_Left; theme_a_unfocused_label->texture[0].data.text.font = theme_app_unhilite_label->texture[0].data.text.font = theme_winfont; theme_a_unfocused_label->texture[0].data.text.shadow = @@ -865,6 +863,7 @@ char *theme_load(char *theme) theme_a_menu_item->surface.data.planar.grad = theme_a_menu_disabled->surface.data.planar.grad = + theme_app_icon->surface.data.planar.grad = Background_ParentRelative; theme_a_menu_item->texture[0].type = diff --git a/render/theme.h b/render/theme.h index 97a58f9f..2a93c8df 100644 --- a/render/theme.h +++ b/render/theme.h @@ -78,8 +78,11 @@ extern Appearance *theme_a_menu_item; extern Appearance *theme_a_menu_disabled; extern Appearance *theme_a_menu_hilite; +extern Appearance *theme_app_hilite_bg; +extern Appearance *theme_app_unhilite_bg; extern Appearance *theme_app_hilite_label; extern Appearance *theme_app_unhilite_label; +extern Appearance *theme_app_icon; void theme_startup(); void theme_shutdown();