From 6df2edd845317dc910f386ac20757c0a9bc6c9b0 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Wed, 9 May 2007 23:44:03 +0000 Subject: [PATCH] merge r6227-6246 from trunk --- COMPLIANCE | 9 +- openbox/action.c | 24 ++++-- openbox/client.c | 211 +++++++++++++++++++++++++--------------------- openbox/client.h | 19 +++-- openbox/event.c | 86 +++++++++++++++++-- openbox/frame.c | 112 +++++++++++++++--------- openbox/frame.h | 13 ++- openbox/menu.c | 1 + openbox/modkeys.c | 34 ++++++-- openbox/modkeys.h | 5 ++ openbox/mouse.c | 14 ++- openbox/openbox.c | 29 +++---- openbox/prop.c | 3 +- openbox/prop.h | 1 + openbox/screen.c | 1 + openbox/session.c | 3 +- render/font.c | 51 ++++++----- render/theme.c | 2 + 18 files changed, 406 insertions(+), 212 deletions(-) diff --git a/COMPLIANCE b/COMPLIANCE index ea9247f4..02358466 100644 --- a/COMPLIANCE +++ b/COMPLIANCE @@ -46,15 +46,14 @@ the version of the spec which Openbox is compliant up to for the hint. + _NET_WM_STRUT (1.3) + _NET_WM_STRUT_PARTIAL (1.3) Openbox uses these to create per-monitor struts in Xinerama setups. -- _NET_WM_ICON_GEOMETRY (1.3) - Openbox does not display icons for iconic windows. ++ _NET_WM_ICON_GEOMETRY (1.3) + _NET_WM_ICON (1.3) - _NET_WM_PID (1.3) - Openbox does not make use of available PIDs as I don't believe there is - a reliable way to tell what client the window is running on. + Openbox does not currently kill processes. - _NET_WM_HANDLED_ICONS (1.3) Openbox does not display icons for iconic windows. + _NET_WM_USER_TIME (1.3) ++ _NET_WM_USER_TIME_WINDOW (1.4) - _NET_WM_PING (1.3) Openbox doesn't look for hung processes at this time. + _NET_FRAME_EXTENTS (1.3) @@ -64,4 +63,4 @@ the version of the spec which Openbox is compliant up to for the hint. + _NET_WM_SYNC_REQUEST (1.3) + _NET_WM_FULL_PLACEMENT (1.4) + _NET_WM_MOVERESIZE_CANCEL (1.4) -- _NET_REQUEST_FRAME_EXTENTS (1.3) ++ _NET_REQUEST_FRAME_EXTENTS (1.3) diff --git a/openbox/action.c b/openbox/action.c index 24388dca..bcd1b60f 100644 --- a/openbox/action.c +++ b/openbox/action.c @@ -1589,7 +1589,8 @@ void action_toggle_decorations(union ActionData *data) client_action_end(data); } -static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch) +static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch, + gboolean shaded) { /* let's make x and y client relative instead of screen relative */ x = x - cx; @@ -1629,11 +1630,11 @@ static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch) |CCCCCCC | A B | DDDDDDD| | CCCCCCCC | A | | B | DDDDDDDD | | CCCCCCC A B DDDDDDD | - - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - - | | b c | | - | west | b move c | east | - | | b c | | - - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - - + - - - - - - - - - - - +CCCCCCC+aaaaaaa+DDDDDDD+ - - - - - - - - - - - - + | | b c | | sh + | west | b move c | east | ad + | | b c | | ed + - - - - - - - - - - - +EEEEEEE+ddddddd+FFFFFFF+- - - - - - - - - - - - | EEEEEEE G H FFFFFFF | | EEEEEEEE | G | | H | FFFFFFFF | |EEEEEEE | G H | FFFFFFF| @@ -1650,6 +1651,15 @@ static guint32 pick_corner(gint x, gint y, gint cx, gint cy, gint cw, gint ch) +---------------------G-------|-------|-------H---------------------+ */ + if (shaded) { + /* for shaded windows, you can only resize west/east and move */ + if (b) + return prop_atoms.net_wm_moveresize_size_left; + if (c) + return prop_atoms.net_wm_moveresize_size_right; + return prop_atoms.net_wm_moveresize_move; + } + if (y < A && y >= C) return prop_atoms.net_wm_moveresize_size_topleft; else if (y >= A && y >= B && a) @@ -1705,7 +1715,7 @@ void action_moveresize(union ActionData *data) c->area.width + c->frame->size.left + c->frame->size.right, c->area.height + c->frame->size.top + - c->frame->size.bottom)); + c->frame->size.bottom, c->shaded)); } moveresize_start(c, data->any.x, data->any.y, data->any.button, corner); diff --git a/openbox/client.c b/openbox/client.c index 3f3f161d..0c4cce1f 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -66,7 +66,7 @@ GList *client_list = NULL; static GSList *client_destructors = NULL; -static void client_get_all(ObClient *self); +static void client_get_all(ObClient *self, gboolean real); static void client_toggle_border(ObClient *self, gboolean show); static void client_get_startup_id(ObClient *self); static void client_get_session_ids(ObClient *self); @@ -76,9 +76,7 @@ static void client_get_state(ObClient *self); static void client_get_layer(ObClient *self); static void client_get_shaped(ObClient *self); static void client_get_mwm_hints(ObClient *self); -static void client_get_gravity(ObClient *self); static void client_get_colormap(ObClient *self); -static void client_get_transientness(ObClient *self); static void client_change_allowed_actions(ObClient *self); static void client_change_state(ObClient *self); static void client_change_wm_state(ObClient *self); @@ -236,8 +234,8 @@ void client_manage(Window window) grab_server(TRUE); - /* check if it has already been unmapped by the time we started mapping. - the grab does a sync so we don't have to here */ + /* check if it has already been unmapped by the time we started + mapping. the grab does a sync so we don't have to here */ if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) || XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) { @@ -277,7 +275,6 @@ void client_manage(Window window) XChangeWindowAttributes(ob_display, window, CWEventMask|CWDontPropagate, &attrib_set); - /* create the ObClient struct, and populate it from the hints on the window */ self = g_new0(ObClient, 1); @@ -290,7 +287,7 @@ void client_manage(Window window) self->desktop = screen_num_desktops; /* always an invalid value */ self->user_time = focus_client ? focus_client->user_time : CurrentTime; - client_get_all(self); + client_get_all(self, TRUE); /* per-app settings override stuff, and return the settings for other uses too */ settings = client_get_settings_state(self); @@ -311,14 +308,14 @@ void client_manage(Window window) /* remove the client's border (and adjust re gravity) */ client_toggle_border(self, FALSE); - /* specify that if we exit, the window should not be destroyed and should - be reparented back to root automatically */ + /* specify that if we exit, the window should not be destroyed and + should be reparented back to root automatically */ XChangeSaveSet(ob_display, window, SetModeInsert); /* create the decoration frame for the client window */ self->frame = frame_new(self); - frame_grab_client(self->frame, self); + frame_grab_client(self->frame); /* do this after we have a frame.. it uses the frame to help determine the WM_STATE to apply. */ @@ -484,6 +481,31 @@ void client_manage(Window window) client_set_list(); ob_debug("Managed window 0x%lx (%s)\n", window, self->class); + + return; +} + + +ObClient *client_fake_manage(Window window) +{ + ObClient *self; + ObAppSettings *settings; + + ob_debug("Pretend-managing window: %lx\n", window); + + /* do this minimal stuff to figure out the client's decorations */ + + self = g_new0(ObClient, 1); + self->window = window; + + client_get_all(self, FALSE); + /* per-app settings override stuff, and return the settings for other + uses too */ + settings = client_get_settings_state(self); + + /* create the decoration frame for the client window */ + self->frame = frame_new(self); + return self; } void client_unmanage_all() @@ -497,8 +519,8 @@ void client_unmanage(ObClient *self) guint j; GSList *it; - ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window, self->class, - self->title ? self->title : ""); + ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window, + self->class, self->title ? self->title : ""); g_assert(self != NULL); @@ -510,7 +532,8 @@ void client_unmanage(ObClient *self) /* flush to send the hide to the server quickly */ XFlush(ob_display); - /* ignore enter events from the unmap so it doesnt mess with the focus */ + /* ignore enter events from the unmap so it doesnt mess with the + focus */ event_ignore_queued_enters(); mouse_grab_for_client(self, FALSE); @@ -547,7 +570,7 @@ void client_unmanage(ObClient *self) for (it = self->group->members; it; it = g_slist_next(it)) if (it->data != self) ((ObClient*)it->data)->transients = - g_slist_remove(((ObClient*)it->data)->transients, self); + g_slist_remove(((ObClient*)it->data)->transients,self); } else if (self->transient_for) { /* transient of window */ self->transient_for->transients = g_slist_remove(self->transient_for->transients, self); @@ -594,7 +617,8 @@ void client_unmanage(ObClient *self) } /* reparent the window out of the frame, and free the frame */ - frame_release_client(self->frame, self); + frame_release_client(self->frame); + frame_free(self->frame); self->frame = NULL; if (ob_state() != OB_STATE_EXITING) { @@ -604,11 +628,15 @@ void client_unmanage(ObClient *self) PROP_ERASE(self->window, net_wm_state); PROP_ERASE(self->window, wm_state); } else { - /* if we're left in an unmapped state, the client wont be mapped. this - is bad, since we will no longer be managing the window on restart */ + /* if we're left in an unmapped state, the client wont be mapped. + this is bad, since we will no longer be managing the window on + restart */ XMapWindow(ob_display, self->window); } + /* update the list hints */ + client_set_list(); + ob_debug("Unmanaged window 0x%lx\n", self->window); /* free all data allocated in the client struct */ @@ -626,9 +654,14 @@ void client_unmanage(ObClient *self) g_free(self->client_machine); g_free(self->sm_client_id); g_free(self); - - /* update the list hints */ - client_set_list(); +} + +void client_fake_unmanage(ObClient *self) +{ + /* this is all that got allocated to get the decorations */ + + frame_free(self->frame); + g_free(self); } static ObAppSettings *client_get_settings_state(ObClient *self) @@ -949,68 +982,62 @@ static void client_toggle_border(ObClient *self, gboolean show) } -static void client_get_all(ObClient *self) +static void client_get_all(ObClient *self, gboolean real) { + /* this is needed for the frame to set itself up */ client_get_area(self); - client_get_mwm_hints(self); - - /* The transient-ness of a window is used to pick a type, but the type can - also affect transiency. - Dialogs are always made transients for their group if they have one. + /* these things can change the decor and functions of the window */ - I also have made non-application type windows be transients for their - group (eg utility windows). - */ - client_get_transientness(self); - client_get_type(self);/* this can change the mwmhints for special cases */ + client_get_mwm_hints(self); + /* this can change the mwmhints for special cases */ + client_get_type_and_transientness(self); client_get_state(self); + client_update_protocols(self); + client_update_normal_hints(self); - client_update_wmhints(self); - /* this may have already been called from client_update_wmhints */ - if (self->transient_for == NULL) - client_update_transient_for(self); - client_get_startup_id(self); - client_get_desktop(self);/* uses transient data/group/startup id if a - desktop is not specified */ - client_get_shaped(self); - - client_get_layer(self); /* if layer hasn't been specified, get it from - other sources if possible */ + /* got the type, the mwmhints, the protocols, and the normal hints + (min/max sizes), so we're ready to set up the decorations/functions */ + client_setup_decor_and_functions(self); - { - /* a couple type-based defaults for new windows */ + if (real) { + client_update_wmhints(self); + /* this may have already been called from client_update_wmhints */ + if (self->transient_for == NULL) + client_update_transient_for(self); - /* this makes sure that these windows appear on all desktops */ - if (self->type == OB_CLIENT_TYPE_DESKTOP) - self->desktop = DESKTOP_ALL; - } + client_get_startup_id(self); + client_get_desktop(self);/* uses transient data/group/startup id if a + desktop is not specified */ + client_get_shaped(self); - client_update_protocols(self); + client_get_layer(self); /* if layer hasn't been specified, get it from + other sources if possible */ - client_get_gravity(self); /* get the attribute gravity */ - client_update_normal_hints(self); /* this may override the attribute - gravity */ + { + /* a couple type-based defaults for new windows */ - /* got the type, the mwmhints, the protocols, and the normal hints - (min/max sizes), so we're ready to set up the decorations/functions */ - client_setup_decor_and_functions(self); + /* this makes sure that these windows appear on all desktops */ + if (self->type == OB_CLIENT_TYPE_DESKTOP) + self->desktop = DESKTOP_ALL; + } #ifdef SYNC - client_update_sync_request_counter(self); + client_update_sync_request_counter(self); #endif - /* get the session related properties */ - client_get_session_ids(self); + /* get the session related properties */ + client_get_session_ids(self); - client_get_colormap(self); - client_update_title(self); - client_update_strut(self); - client_update_icons(self); - client_update_user_time_window(self); - if (!self->user_time_window) /* check if this would have been called */ - client_update_user_time(self); - client_update_icon_geometry(self); + client_get_colormap(self); + client_update_title(self); + client_update_strut(self); + client_update_icons(self); + client_update_user_time_window(self); + if (!self->user_time_window) /* check if this would have been called */ + client_update_user_time(self); + client_update_icon_geometry(self); + } } static void client_get_startup_id(ObClient *self) @@ -1170,20 +1197,12 @@ static void client_get_shaped(ObClient *self) #endif } -void client_get_transientness(ObClient *self) -{ - Window t; - if (XGetTransientForHint(ob_display, self->window, &t)) - self->transient = TRUE; -} - void client_update_transient_for(ObClient *self) { Window t = None; ObClient *target = NULL; if (XGetTransientForHint(ob_display, self->window, &t)) { - self->transient = TRUE; if (t != self->window) { /* cant be transient to itself! */ target = g_hash_table_lookup(window_map, &t); /* if this happens then we need to check for it*/ @@ -1220,16 +1239,8 @@ void client_update_transient_for(ObClient *self) } } } - } else if (self->type == OB_CLIENT_TYPE_DIALOG || - self->type == OB_CLIENT_TYPE_TOOLBAR || - self->type == OB_CLIENT_TYPE_MENU || - self->type == OB_CLIENT_TYPE_UTILITY) - { - self->transient = TRUE; - if (self->group) - target = OB_TRAN_GROUP; - } else - self->transient = FALSE; + } else if (self->transient && self->group) + target = OB_TRAN_GROUP; client_update_transient_tree(self, self->group, self->group, self->transient_for, target); @@ -1364,12 +1375,14 @@ static void client_get_mwm_hints(ObClient *self) } } -void client_get_type(ObClient *self) +void client_get_type_and_transientness(ObClient *self) { guint num, i; guint32 *val; + Window t; self->type = -1; + self->transient = FALSE; if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) { /* use the first value that we know about in the array */ @@ -1403,7 +1416,10 @@ void client_get_type(ObClient *self) } g_free(val); } - + + if (XGetTransientForHint(ob_display, self->window, &t)) + self->transient = TRUE; + if (self->type == (ObClientType) -1) { /*the window type hint was not set, which means we either classify ourself as a normal window or a dialog, depending on if we are a @@ -1413,6 +1429,15 @@ void client_get_type(ObClient *self) else self->type = OB_CLIENT_TYPE_NORMAL; } + + /* then, based on our type, we can update our transientness.. */ + if (self->type == OB_CLIENT_TYPE_DIALOG || + self->type == OB_CLIENT_TYPE_TOOLBAR || + self->type == OB_CLIENT_TYPE_MENU || + self->type == OB_CLIENT_TYPE_UTILITY) + { + self->transient = TRUE; + } } void client_update_protocols(ObClient *self) @@ -1456,16 +1481,6 @@ void client_update_sync_request_counter(ObClient *self) } #endif -static void client_get_gravity(ObClient *self) -{ - XWindowAttributes wattrib; - Status ret; - - ret = XGetWindowAttributes(ob_display, self->window, &wattrib); - g_assert(ret != BadWindow); - self->gravity = wattrib.win_gravity; -} - void client_get_colormap(ObClient *self) { XWindowAttributes wa; @@ -1649,7 +1664,7 @@ void client_setup_decor_and_functions(ObClient *self) /* kill the handle on fully maxed windows */ if (self->max_vert && self->max_horz) - self->decorations &= ~OB_FRAME_DECOR_HANDLE; + self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS); /* finally, the user can have requested no decorations, which overrides everything (but doesnt give it a border if it doesnt have one) */ @@ -2184,6 +2199,8 @@ static void client_get_session_ids(ObClient *self) localhost[127] = '\0'; if (strcmp(localhost, s) != 0) self->client_machine = s; + else + g_free(s); } } diff --git a/openbox/client.h b/openbox/client.h index f9a19483..30d40510 100644 --- a/openbox/client.h +++ b/openbox/client.h @@ -314,13 +314,22 @@ void client_remove_destructor(ObClientCallback func); /*! Manages all existing windows */ void client_manage_all(); -/*! Manages a given window */ +/*! Manages a given window +*/ void client_manage(Window win); /*! Unmanages all managed windows */ void client_unmanage_all(); /*! Unmanages a given client */ void client_unmanage(ObClient *client); +/*! This manages a window only so far as is needed to get it's decorations. + This is used when you want to determine a window's decorations before it + is mapped. Call client_fake_unmanage() with the returned client when you + are done with it. */ +ObClient *client_fake_manage(Window win); +/*! Free the stuff created by client_fake_manage() */ +void client_fake_unmanage(ObClient *self); + /*! Sets the client list on the root window from the client_list */ void client_set_list(); @@ -354,7 +363,7 @@ gboolean client_focused(ObClient *self); /*! Convery a position/size from a given gravity to the client's true gravity */ -void client_convert_gravity(ObClient *client, gint gravity, gint *x, gint *y, +void client_convert_gravity(ObClient *self, gint gravity, gint *x, gint *y, gint w, gint h); #define client_move(self, x, y) \ @@ -536,7 +545,7 @@ void client_activate(ObClient *self, gboolean here, gboolean user); /*! Bring all of its helper windows to its desktop. These are the utility and stuff windows. */ -void client_bring_helper_windows(ObClient *client); +void client_bring_helper_windows(ObClient *self); /*! Calculates the stacking layer for the client window */ void client_calc_layer(ObClient *self); @@ -600,8 +609,8 @@ void client_update_icon_geometry(ObClient *self); */ void client_setup_decor_and_functions(ObClient *self); -/*! Retrieves the window's type and sets ObClient->type */ -void client_get_type(ObClient *self); +/*! Sets the window's type and transient flag */ +void client_get_type_and_transientness(ObClient *self); const ObClientIcon *client_icon(ObClient *self, gint w, gint h); diff --git a/openbox/event.c b/openbox/event.c index babb5197..8523f637 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -568,8 +568,32 @@ static void event_process(const XEvent *ec, gpointer data) event_handle_root(e); else if (e->type == MapRequest) client_manage(window); + else if (e->type == ClientMessage) { + /* This is for _NET_WM_REQUEST_FRAME_EXTENTS messages. They come for + windows that are not managed yet. */ + if (e->xclient.message_type == prop_atoms.net_request_frame_extents) { + /* Pretend to manage the client, getting information used to + determine its decorations */ + ObClient *c = client_fake_manage(e->xclient.window); + gulong vals[4]; + + /* adjust the decorations so we know the sizes */ + frame_adjust_area(c->frame, FALSE, TRUE, TRUE); + + /* set the frame extents on the window */ + vals[0] = c->frame->size.left; + vals[1] = c->frame->size.right; + vals[2] = c->frame->size.top; + vals[3] = c->frame->size.bottom; + PROP_SETA32(e->xclient.window, net_frame_extents, + cardinal, vals, 4); + + /* Free the pretend client */ + client_fake_unmanage(c); + } + } else if (e->type == ConfigureRequest) { - /* unhandled configure requests must be used to configure the + /* unhandled config5Aure requests must be used to configure the window directly */ XWindowChanges xwc; @@ -705,9 +729,17 @@ static void event_handle_client(ObClient *client, XEvent *e) XEvent ce; Atom msgtype; ObFrameContext con; + static gint px = -1, py = -1; + static guint pb = 0; switch (e->type) { case ButtonPress: + /* save where the press occured for the first button pressed */ + if (!pb) { + pb = e->xbutton.button; + px = e->xbutton.x; + py = e->xbutton.y; + } case ButtonRelease: /* Wheel buttons don't draw because they are an instant click, so it is a waste of resources to go drawing it. @@ -719,8 +751,13 @@ static void event_handle_client(ObClient *client, XEvent *e) !keyboard_interactively_grabbed() && !menu_frame_visible) { - con = frame_context(client, e->xbutton.window); + /* use where the press occured */ + con = frame_context(client, e->xbutton.window, px, py); con = mouse_button_frame_context(con, e->xbutton.button); + + if (e->type == ButtonRelease && e->xbutton.button == pb) + pb = 0, px = py = -1; + switch (con) { case OB_FRAME_CONTEXT_MAXIMIZE: client->frame->max_press = (e->type == ButtonPress); @@ -748,8 +785,46 @@ static void event_handle_client(ObClient *client, XEvent *e) } } break; + case MotionNotify: + con = frame_context(client, e->xmotion.window, + e->xmotion.x, e->xmotion.y); + switch (con) { + case OB_FRAME_CONTEXT_TITLEBAR: + /* we've left the button area inside the titlebar */ + client->frame->max_hover = FALSE; + client->frame->desk_hover = FALSE; + client->frame->shade_hover = FALSE; + client->frame->iconify_hover = FALSE; + client->frame->close_hover = FALSE; + frame_adjust_state(client->frame); + break; + case OB_FRAME_CONTEXT_MAXIMIZE: + client->frame->max_hover = TRUE; + frame_adjust_state(client->frame); + break; + case OB_FRAME_CONTEXT_ALLDESKTOPS: + client->frame->desk_hover = TRUE; + frame_adjust_state(client->frame); + break; + case OB_FRAME_CONTEXT_SHADE: + client->frame->shade_hover = TRUE; + frame_adjust_state(client->frame); + break; + case OB_FRAME_CONTEXT_ICONIFY: + client->frame->iconify_hover = TRUE; + frame_adjust_state(client->frame); + break; + case OB_FRAME_CONTEXT_CLOSE: + client->frame->close_hover = TRUE; + frame_adjust_state(client->frame); + break; + default: + break; + } + break; case LeaveNotify: - con = frame_context(client, e->xcrossing.window); + con = frame_context(client, e->xcrossing.window, + e->xcrossing.x, e->xcrossing.y); switch (con) { case OB_FRAME_CONTEXT_MAXIMIZE: client->frame->max_hover = FALSE; @@ -809,7 +884,8 @@ static void event_handle_client(ObClient *client, XEvent *e) nofocus = TRUE; } - con = frame_context(client, e->xcrossing.window); + con = frame_context(client, e->xcrossing.window, + e->xcrossing.x, e->xcrossing.y); switch (con) { case OB_FRAME_CONTEXT_MAXIMIZE: client->frame->max_hover = TRUE; @@ -1167,7 +1243,7 @@ static void event_handle_client(ObClient *client, XEvent *e) client_update_wmhints(client); } else if (msgtype == XA_WM_TRANSIENT_FOR) { client_update_transient_for(client); - client_get_type(client); + client_get_type_and_transientness(client); /* type may have changed, so update the layer */ client_calc_layer(client); client_setup_decor_and_functions(client); diff --git a/openbox/frame.c b/openbox/frame.c index 2c3fb58f..1f45ea8b 100644 --- a/openbox/frame.c +++ b/openbox/frame.c @@ -34,7 +34,7 @@ #define FRAME_EVENTMASK (EnterWindowMask | LeaveWindowMask | \ ButtonPressMask | ButtonReleaseMask) #define ELEMENT_EVENTMASK (ButtonPressMask | ButtonReleaseMask | \ - ButtonMotionMask | \ + ButtonMotionMask | PointerMotionMask | \ EnterWindowMask | LeaveWindowMask) /* The inner window does not need enter/leave events. If it does get them, then it needs its own context for enter events @@ -99,6 +99,7 @@ ObFrame *frame_new(ObClient *client) Visual *visual; self = g_new0(ObFrame, 1); + self->client = client; visual = check_32bit_client(client); @@ -245,7 +246,7 @@ static void free_theme_statics(ObFrame *self) RrAppearanceFree(self->a_icon); } -static void frame_free(ObFrame *self) +void frame_free(ObFrame *self) { free_theme_statics(self); @@ -553,12 +554,10 @@ void frame_adjust_icon(ObFrame *self) framerender_frame(self); } -void frame_grab_client(ObFrame *self, ObClient *client) +void frame_grab_client(ObFrame *self) { - self->client = client; - /* reparent the client to the frame */ - XReparentWindow(ob_display, client->window, self->plate, 0, 0); + XReparentWindow(ob_display, self->client->window, self->plate, 0, 0); /* When reparenting the client window, it is usually not mapped yet, since this occurs from a MapRequest. However, in the case where Openbox is @@ -568,52 +567,50 @@ void frame_grab_client(ObFrame *self, ObClient *client) handled and need to be ignored. */ if (ob_state() == OB_STATE_STARTING) - client->ignore_unmaps += 2; + self->client->ignore_unmaps += 2; /* select the event mask on the client's parent (to receive config/map req's) the ButtonPress is to catch clicks on the client border */ XSelectInput(ob_display, self->plate, PLATE_EVENTMASK); /* map the client so it maps when the frame does */ - XMapWindow(ob_display, client->window); + XMapWindow(ob_display, self->client->window); /* adjust the frame to the client's size */ frame_adjust_area(self, FALSE, TRUE, FALSE); /* set all the windows for the frame in the window_map */ - g_hash_table_insert(window_map, &self->window, client); - g_hash_table_insert(window_map, &self->plate, client); - g_hash_table_insert(window_map, &self->inner, client); - g_hash_table_insert(window_map, &self->title, client); - g_hash_table_insert(window_map, &self->label, client); - g_hash_table_insert(window_map, &self->max, client); - g_hash_table_insert(window_map, &self->close, client); - g_hash_table_insert(window_map, &self->desk, client); - g_hash_table_insert(window_map, &self->shade, client); - g_hash_table_insert(window_map, &self->icon, client); - g_hash_table_insert(window_map, &self->iconify, client); - g_hash_table_insert(window_map, &self->handle, client); - g_hash_table_insert(window_map, &self->lgrip, client); - g_hash_table_insert(window_map, &self->rgrip, client); - g_hash_table_insert(window_map, &self->tltresize, client); - g_hash_table_insert(window_map, &self->tllresize, client); - g_hash_table_insert(window_map, &self->trtresize, client); - g_hash_table_insert(window_map, &self->trrresize, client); + g_hash_table_insert(window_map, &self->window, self->client); + g_hash_table_insert(window_map, &self->plate, self->client); + g_hash_table_insert(window_map, &self->inner, self->client); + g_hash_table_insert(window_map, &self->title, self->client); + g_hash_table_insert(window_map, &self->label, self->client); + g_hash_table_insert(window_map, &self->max, self->client); + g_hash_table_insert(window_map, &self->close, self->client); + g_hash_table_insert(window_map, &self->desk, self->client); + g_hash_table_insert(window_map, &self->shade, self->client); + g_hash_table_insert(window_map, &self->icon, self->client); + g_hash_table_insert(window_map, &self->iconify, self->client); + g_hash_table_insert(window_map, &self->handle, self->client); + g_hash_table_insert(window_map, &self->lgrip, self->client); + g_hash_table_insert(window_map, &self->rgrip, self->client); + g_hash_table_insert(window_map, &self->tltresize, self->client); + g_hash_table_insert(window_map, &self->tllresize, self->client); + g_hash_table_insert(window_map, &self->trtresize, self->client); + g_hash_table_insert(window_map, &self->trrresize, self->client); } -void frame_release_client(ObFrame *self, ObClient *client) +void frame_release_client(ObFrame *self) { XEvent ev; gboolean reparent = TRUE; - g_assert(self->client == client); - /* if there was any animation going on, kill it */ ob_main_loop_timeout_remove_data(ob_main_loop, frame_animate_iconify, self, FALSE); /* check if the app has already reparented its window away */ - while (XCheckTypedWindowEvent(ob_display, client->window, + while (XCheckTypedWindowEvent(ob_display, self->client->window, ReparentNotify, &ev)) { /* This check makes sure we don't catch our own reparent action to @@ -633,10 +630,10 @@ void frame_release_client(ObFrame *self, ObClient *client) if (reparent) { /* according to the ICCCM - if the client doesn't reparent itself, then we will reparent the window to root for them */ - XReparentWindow(ob_display, client->window, + XReparentWindow(ob_display, self->client->window, RootWindow(ob_display, ob_screen), - client->area.x, - client->area.y); + self->client->area.x, + self->client->area.y); } /* remove all the windows for the frame from the window_map */ @@ -660,8 +657,6 @@ void frame_release_client(ObFrame *self, ObClient *client) g_hash_table_remove(window_map, &self->trrresize); ob_main_loop_timeout_remove_data(ob_main_loop, flash_timeout, self, TRUE); - - frame_free(self); } /* is there anything present between us and the label? */ @@ -688,7 +683,7 @@ static gboolean is_button_present(ObFrame *self, const gchar *lc, gint dir) { static void layout_title(ObFrame *self) { gchar *lc; - gint i, x; + gint i; const gint bwidth = ob_rr_theme->button_size + ob_rr_theme->paddingx + 1; /* position of the left most button */ @@ -700,6 +695,7 @@ static void layout_title(ObFrame *self) self->icon_on = self->desk_on = self->shade_on = self->iconify_on = self->max_on = self->close_on = self->label_on = FALSE; self->label_width = self->width - (ob_rr_theme->paddingx + 1) * 2; + self->leftmost = self->rightmost = OB_FRAME_CONTEXT_NONE; /* figure out what's being show, find each element's position, and the width of the label @@ -708,16 +704,21 @@ static void layout_title(ObFrame *self) i will be +1 the first time through when working to the left, and -1 the second time through when working to the right */ for (i = 1; i >= -1; i-=2) { + gint x; + ObFrameContext *firstcon; + if (i > 0) { x = left; lc = config_title_layout; + firstcon = &self->leftmost; } else { x = right; lc = config_title_layout + strlen(config_title_layout)-1; + firstcon = &self->rightmost; } /* stop at the end of the string (or the label, which calls break) */ - for (; *lc != '\0' && lc >= config_title_layout; lc+=i) + for (; *lc != '\0' && lc >= config_title_layout; lc+=i) { if (*lc == 'L') { if (i > 0) { self->label_on = TRUE; @@ -725,6 +726,7 @@ static void layout_title(ObFrame *self) } break; /* break the for loop, do other side of label */ } else if (*lc == 'N') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICON; if ((self->icon_on = is_button_present(self, lc, i))) { /* icon gets extra padding */ self->label_width -= bwidth + 2; @@ -732,36 +734,44 @@ static void layout_title(ObFrame *self) x += i * (bwidth + 2); } } else if (*lc == 'D') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_ALLDESKTOPS; if ((self->desk_on = is_button_present(self, lc, i))) { self->label_width -= bwidth; self->desk_x = x; x += i * bwidth; } } else if (*lc == 'S') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_SHADE; if ((self->shade_on = is_button_present(self, lc, i))) { self->label_width -= bwidth; self->shade_x = x; x += i * bwidth; } } else if (*lc == 'I') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_ICONIFY; if ((self->iconify_on = is_button_present(self, lc, i))) { self->label_width -= bwidth; self->iconify_x = x; x += i * bwidth; } } else if (*lc == 'M') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_MAXIMIZE; if ((self->max_on = is_button_present(self, lc, i))) { self->label_width -= bwidth; self->max_x = x; x += i * bwidth; } } else if (*lc == 'C') { + if (firstcon) *firstcon = OB_FRAME_CONTEXT_CLOSE; if ((self->close_on = is_button_present(self, lc, i))) { self->label_width -= bwidth; self->close_x = x; x += i * bwidth; } - } + } else + continue; /* don't set firstcon */ + firstcon = NULL; + } } /* position and map the elements */ @@ -853,7 +863,7 @@ ObFrameContext frame_context_from_string(const gchar *name) return OB_FRAME_CONTEXT_NONE; } -ObFrameContext frame_context(ObClient *client, Window win) +ObFrameContext frame_context(ObClient *client, Window win, gint x, gint y) { ObFrame *self; @@ -880,8 +890,30 @@ ObFrameContext frame_context(ObClient *client, Window win) return OB_FRAME_CONTEXT_CLIENT; } + if (win == self->title) { + /* when the user clicks in the corners of the titlebar and the client + is fully maximized, then treat it like they clicked in the + button that is there */ + if (self->client->max_horz && self->client->max_vert && + y < ob_rr_theme->paddingy + 1 + ob_rr_theme->button_size) + { + if (x < ((ob_rr_theme->paddingx + 1) * 2 + + ob_rr_theme->button_size)) { + if (self->leftmost != OB_FRAME_CONTEXT_NONE) + return self->leftmost; + } + else if (x > (self->width - + (ob_rr_theme->paddingx + 1 + + ob_rr_theme->button_size))) + { + if (self->rightmost != OB_FRAME_CONTEXT_NONE) + return self->rightmost; + } + } + return OB_FRAME_CONTEXT_TITLEBAR; + } + if (win == self->window) return OB_FRAME_CONTEXT_FRAME; - if (win == self->title) return OB_FRAME_CONTEXT_TITLEBAR; if (win == self->label) return OB_FRAME_CONTEXT_TITLEBAR; if (win == self->handle) return OB_FRAME_CONTEXT_HANDLE; if (win == self->lgrip) return OB_FRAME_CONTEXT_BLCORNER; diff --git a/openbox/frame.h b/openbox/frame.h index 3a5dfdb2..ebd8de98 100644 --- a/openbox/frame.h +++ b/openbox/frame.h @@ -136,6 +136,10 @@ struct _ObFrame gint cbwidth_x; /* client border width */ gint cbwidth_y; /* client border width */ + /* the leftmost and rightmost elements in the titlebar */ + ObFrameContext leftmost; + ObFrameContext rightmost; + gboolean max_press; gboolean close_press; gboolean desk_press; @@ -162,6 +166,8 @@ struct _ObFrame }; ObFrame *frame_new(struct _ObClient *c); +void frame_free(ObFrame *self); + void frame_show(ObFrame *self); void frame_hide(ObFrame *self); void frame_adjust_theme(ObFrame *self); @@ -173,12 +179,13 @@ void frame_adjust_state(ObFrame *self); void frame_adjust_focus(ObFrame *self, gboolean hilite); void frame_adjust_title(ObFrame *self); void frame_adjust_icon(ObFrame *self); -void frame_grab_client(ObFrame *self, struct _ObClient *client); -void frame_release_client(ObFrame *self, struct _ObClient *client); +void frame_grab_client(ObFrame *self); +void frame_release_client(ObFrame *self); ObFrameContext frame_context_from_string(const gchar *name); -ObFrameContext frame_context(struct _ObClient *self, Window win); +ObFrameContext frame_context(struct _ObClient *self, Window win, + gint x, gint y); /*! Applies gravity to the client's position to find where the frame should be positioned. diff --git a/openbox/menu.c b/openbox/menu.c index 0e203739..c6e986b9 100644 --- a/openbox/menu.c +++ b/openbox/menu.c @@ -357,6 +357,7 @@ static void menu_destroy_hash_value(ObMenu *self) g_free(self->name); g_free(self->title); g_free(self->execute); + g_free(self->more_menu); g_free(self); } diff --git a/openbox/modkeys.c b/openbox/modkeys.c index 4eb836e5..3346a88d 100644 --- a/openbox/modkeys.c +++ b/openbox/modkeys.c @@ -36,14 +36,14 @@ static void set_modkey_mask(guchar mask, KeySym sym); static XModifierKeymap *modmap; +static KeySym *keymap; +static gint min_keycode, max_keycode, keysyms_per_keycode; /* This is a bitmask of the different masks for each modifier key */ static guchar modkeys_keys[OB_MODKEY_NUM_KEYS]; void modkeys_startup(gboolean reconfigure) { - KeySym *keymap; gint i, j, k; - gint min_keycode, max_keycode, keysyms_per_keycode; /* reset the keys to not be bound to any masks */ for (i = 0; i < OB_MODKEY_NUM_KEYS; ++i) @@ -64,22 +64,25 @@ void modkeys_startup(gboolean reconfigure) KeySym sym; /* get a keycode that is bound to the mask (i) */ KeyCode keycode = modmap->modifiermap[i*modmap->max_keypermod + j]; - /* go through each keysym bound to the given keycode */ - for (k = 0; k < keysyms_per_keycode; ++k) { - sym = keymap[(keycode-min_keycode) * keysyms_per_keycode + k]; - if (sym != NoSymbol) { - /* bind the key to the mask (e.g. Alt_L => Mod1Mask) */ - set_modkey_mask(nth_mask(i), sym); + if (keycode) { + /* go through each keysym bound to the given keycode */ + for (k = 0; k < keysyms_per_keycode; ++k) { + sym = keymap[(keycode-min_keycode) * keysyms_per_keycode + + k]; + if (sym != NoSymbol) { + /* bind the key to the mask (e.g. Alt_L => Mod1Mask) */ + set_modkey_mask(nth_mask(i), sym); + } } } } } - XFree(keymap); } void modkeys_shutdown(gboolean reconfigure) { XFreeModifiermap(modmap); + XFree(keymap); } guint modkeys_keycode_to_mask(guint keycode) @@ -139,3 +142,16 @@ static void set_modkey_mask(guchar mask, KeySym sym) else if (sym == XK_Meta_L || sym == XK_Meta_R) modkeys_keys[OB_MODKEY_KEY_META] |= mask; } + +KeyCode modkeys_sym_to_code(KeySym sym) +{ + gint i, j; + + /* go through each keycode and look for the keysym */ + for (i = min_keycode; i <= max_keycode; ++i) + for (j = 0; j < keysyms_per_keycode; ++j) + if (sym == keymap[(i-min_keycode) * keysyms_per_keycode + j]) + return i; + return 0; +} + diff --git a/openbox/modkeys.h b/openbox/modkeys.h index cfa95b04..8f222369 100644 --- a/openbox/modkeys.h +++ b/openbox/modkeys.h @@ -20,6 +20,7 @@ #define ob__modkeys_h #include +#include /*! These keys are bound to the modifier masks in any fashion */ typedef enum { @@ -51,4 +52,8 @@ guint modkeys_only_modifier_masks(guint mask); right keys when there are both. */ guint modkeys_key_to_mask(ObModkeysKey key); +/*! Convert a KeySym to a KeyCode, because the X function is terrible - says + valgrind. */ +KeyCode modkeys_sym_to_code(KeySym sym); + #endif diff --git a/openbox/mouse.c b/openbox/mouse.c index 522eba8c..9edda8eb 100644 --- a/openbox/mouse.c +++ b/openbox/mouse.c @@ -179,7 +179,7 @@ void mouse_event(ObClient *client, XEvent *e) static Time ltime; static guint button = 0, state = 0, lbutton = 0; static Window lwindow = None; - static gint px, py; + static gint px, py, pwx = -1, pwy = -1; ObFrameContext context; gboolean click = FALSE; @@ -187,11 +187,14 @@ void mouse_event(ObClient *client, XEvent *e) switch (e->type) { case ButtonPress: - context = frame_context(client, e->xany.window); + context = frame_context(client, e->xbutton.window, + e->xbutton.x, e->xbutton.y); context = mouse_button_frame_context(context, e->xbutton.button); px = e->xbutton.x_root; py = e->xbutton.y_root; + if (pwx == -1) pwx = e->xbutton.x; + if (pwy == -1) pwy = e->xbutton.y; button = e->xbutton.button; state = e->xbutton.state; @@ -209,9 +212,12 @@ void mouse_event(ObClient *client, XEvent *e) break; case ButtonRelease: - context = frame_context(client, e->xany.window); + /* use where the press occured in the window */ + context = frame_context(client, e->xbutton.window, pwx, pwy); context = mouse_button_frame_context(context, e->xbutton.button); + pwx = pwy = -1; + if (e->xbutton.button == button) { /* clicks are only valid if its released over the window */ gint junk1, junk2; @@ -272,7 +278,7 @@ void mouse_event(ObClient *client, XEvent *e) case MotionNotify: if (button) { - context = frame_context(client, e->xany.window); + context = frame_context(client, e->xmotion.window, pwx, pwy); context = mouse_button_frame_context(context, button); if (ABS(e->xmotion.x_root - px) >= diff --git a/openbox/openbox.c b/openbox/openbox.c index db26f0d2..d3c80549 100644 --- a/openbox/openbox.c +++ b/openbox/openbox.c @@ -76,6 +76,10 @@ #include #endif +#include +#include + + RrInstance *ob_rr_inst; RrTheme *ob_rr_theme; ObMainLoop *ob_main_loop; @@ -201,32 +205,27 @@ gint main(gint argc, gchar **argv) cursors[OB_CURSOR_NORTHWEST] = load_cursor("top_left_corner", XC_top_left_corner); - /* create available keycodes */ - keys[OB_KEY_RETURN] = - XKeysymToKeycode(ob_display, XStringToKeysym("Return")); - keys[OB_KEY_ESCAPE] = - XKeysymToKeycode(ob_display, XStringToKeysym("Escape")); - keys[OB_KEY_LEFT] = - XKeysymToKeycode(ob_display, XStringToKeysym("Left")); - keys[OB_KEY_RIGHT] = - XKeysymToKeycode(ob_display, XStringToKeysym("Right")); - keys[OB_KEY_UP] = - XKeysymToKeycode(ob_display, XStringToKeysym("Up")); - keys[OB_KEY_DOWN] = - XKeysymToKeycode(ob_display, XStringToKeysym("Down")); prop_startup(); /* get atoms values for the display */ extensions_query_all(); /* find which extensions are present */ if (screen_annex(program_name)) { /* it will be ours! */ do { + modkeys_startup(reconfigure); + + /* get the keycodes for keys we use */ + keys[OB_KEY_RETURN] = modkeys_sym_to_code(XK_Return); + keys[OB_KEY_ESCAPE] = modkeys_sym_to_code(XK_Escape); + keys[OB_KEY_LEFT] = modkeys_sym_to_code(XK_Left); + keys[OB_KEY_RIGHT] = modkeys_sym_to_code(XK_Right); + keys[OB_KEY_UP] = modkeys_sym_to_code(XK_Up); + keys[OB_KEY_DOWN] = modkeys_sym_to_code(XK_Down); + { ObParseInst *i; xmlDocPtr doc; xmlNodePtr node; - modkeys_startup(reconfigure); - /* startup the parsing so everything can register sections of the rc */ i = parse_startup(); diff --git a/openbox/prop.c b/openbox/prop.c index e35c93aa..3861f7c1 100644 --- a/openbox/prop.c +++ b/openbox/prop.c @@ -95,6 +95,7 @@ void prop_startup() 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_request_frame_extents, "_NET_REQUEST_FRAME_EXTENTS"); /* CREATE(net_wm_ping, "_NET_WM_PING"); */ #ifdef SYNC @@ -167,7 +168,7 @@ void prop_startup() CREATE(openbox_pid, "_OPENBOX_PID"); CREATE(openbox_config, "_OPENBOX_CONFIG"); - CREATE(openbox_wm_state_undecorated, "_OPENBOX_WM_STATE_UNDECORATED"); + CREATE(openbox_wm_state_undecorated, "_OB_WM_STATE_UNDECORATED"); CREATE(openbox_control, "_OPENBOX_CONTROL"); } diff --git a/openbox/prop.h b/openbox/prop.h index f8b44a73..4f45b6d4 100644 --- a/openbox/prop.h +++ b/openbox/prop.h @@ -132,6 +132,7 @@ typedef struct Atoms { Atom net_wm_user_time; Atom net_wm_user_time_window; Atom net_frame_extents; + Atom net_request_frame_extents; /* application protocols */ /* Atom net_wm_ping; */ diff --git a/openbox/screen.c b/openbox/screen.c index 9265c73b..190468aa 100644 --- a/openbox/screen.c +++ b/openbox/screen.c @@ -275,6 +275,7 @@ gboolean screen_annex(const gchar *program_name) supported[i++] = prop_atoms.net_wm_user_time; supported[i++] = prop_atoms.net_wm_user_time_window; supported[i++] = prop_atoms.net_frame_extents; + supported[i++] = prop_atoms.net_request_frame_extents; supported[i++] = prop_atoms.net_startup_id; #ifdef SYNC supported[i++] = prop_atoms.net_wm_sync_request; diff --git a/openbox/session.c b/openbox/session.c index bcf80ad0..2c189ed0 100644 --- a/openbox/session.c +++ b/openbox/session.c @@ -175,7 +175,7 @@ static gboolean session_connect() SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask, &cb, oldid, &ob_sm_id, - SM_ERR_LEN, sm_err); + SM_ERR_LEN-1, sm_err); g_free(oldid); if (sm_conn == NULL) ob_debug("Failed to connect to session manager: %s\n", sm_err); @@ -394,6 +394,7 @@ static void sm_save_yourself(SmcConn conn, SmPointer data, gint save_type, if (!SmcRequestSaveYourselfPhase2(conn, sm_save_yourself_2, savedata)) { ob_debug_type(OB_DEBUG_SM, "Requst for phase 2 failed\n"); + g_free(savedata); SmcSaveYourselfDone(conn, FALSE); } } diff --git a/render/font.c b/render/font.c index 1ca1b768..166b9abb 100644 --- a/render/font.c +++ b/render/font.c @@ -34,22 +34,29 @@ static void measure_font(const RrInstance *inst, RrFont *f) { PangoFontMetrics *metrics; - gchar *locale, *p; - - /* get the default language from the locale - (based on gtk_get_default_language in gtkmain.c) */ - locale = g_strdup(setlocale(LC_CTYPE, NULL)); - if ((p = strchr(locale, '.'))) *p = '\0'; /* strip off the . */ - if ((p = strchr(locale, '@'))) *p = '\0'; /* strip off the @ */ + static PangoLanguage *lang = NULL; + + if (lang == NULL) { +#if PANGO_VERSION_CHECK(1,16,0) + lang = pango_language_get_default(); +#else + gchar *locale, *p; + /* get the default language from the locale + (based on gtk_get_default_language in gtkmain.c) */ + locale = g_strdup(setlocale(LC_CTYPE, NULL)); + if ((p = strchr(locale, '.'))) *p = '\0'; /* strip off the . */ + if ((p = strchr(locale, '@'))) *p = '\0'; /* strip off the @ */ + lang = pango_language_from_string(locale); + g_free(locale); +#endif + } /* measure the ascent and descent */ - metrics = pango_context_get_metrics(inst->pango, f->font_desc, - pango_language_from_string(locale)); + metrics = pango_context_get_metrics(inst->pango, f->font_desc, lang); f->ascent = pango_font_metrics_get_ascent(metrics); f->descent = pango_font_metrics_get_descent(metrics); pango_font_metrics_unref(metrics); - g_free(locale); } RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size, @@ -66,6 +73,8 @@ RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size, out->font_desc = pango_font_description_new(); out->layout = pango_layout_new(inst->pango); out->shortcut_underline = pango_attr_underline_new(PANGO_UNDERLINE_LOW); + out->shortcut_underline->start_index = 0; + out->shortcut_underline->end_index = 0; attrlist = pango_attr_list_new(); /* shortcut_underline is owned by the attrlist */ @@ -219,16 +228,6 @@ void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area) break; } - t->font->shortcut_underline->start_index = 0; - t->font->shortcut_underline->end_index = 0; - /* the attributes are owned by the layout. - re-add the attributes to the layout after changing the - start and end index */ - attrlist = pango_layout_get_attributes(t->font->layout); - pango_attr_list_ref(attrlist); - pango_layout_set_attributes(t->font->layout, attrlist); - pango_attr_list_unref(attrlist); - if (t->shadow_offset_x || t->shadow_offset_y) { c.color.red = t->shadow_color->r | t->shadow_color->r << 8; c.color.green = t->shadow_color->g | t->shadow_color->g << 8; @@ -270,4 +269,16 @@ void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area) pango_xft_render_layout_line (d, &c, pango_layout_get_line(t->font->layout, 0), x * PANGO_SCALE, y * PANGO_SCALE); + + if (t->shortcut) { + t->font->shortcut_underline->start_index = 0; + t->font->shortcut_underline->end_index = 0; + /* the attributes are owned by the layout. + re-add the attributes to the layout after changing the + start and end index */ + attrlist = pango_layout_get_attributes(t->font->layout); + pango_attr_list_ref(attrlist); + pango_layout_set_attributes(t->font->layout, attrlist); + pango_attr_list_unref(attrlist); + } } diff --git a/render/theme.c b/render/theme.c index 84561ef1..a32a11f1 100644 --- a/render/theme.c +++ b/render/theme.c @@ -131,6 +131,7 @@ RrTheme* RrThemeNew(const RrInstance *inst, gchar *name, winjust = RR_JUSTIFY_RIGHT; else if (!g_ascii_strcasecmp(str, "center")) winjust = RR_JUSTIFY_CENTER; + g_free(str); } if (menu_title_font) { @@ -145,6 +146,7 @@ RrTheme* RrThemeNew(const RrInstance *inst, gchar *name, mtitlejust = RR_JUSTIFY_RIGHT; else if (!g_ascii_strcasecmp(str, "center")) mtitlejust = RR_JUSTIFY_CENTER; + g_free(str); } if (menu_item_font) { -- 2.34.1