X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fopenbox;a=blobdiff_plain;f=openbox%2Fclient.c;h=1478d840f832265471d208fac1fead0b3199ec64;hp=4d8f4e77ae014e374fc53c31ce3dadcb3bd55b46;hb=9325c92056ff4268db2263cdf8c07bb529e158aa;hpb=73575c10ce3ede1637e4f959b56e1f7d5f110b91 diff --git a/openbox/client.c b/openbox/client.c index 4d8f4e77..1478d840 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -31,6 +31,7 @@ #include "grab.h" #include "prompt.h" #include "focus.h" +#include "focus_cycle.h" #include "stacking.h" #include "openbox.h" #include "group.h" @@ -41,6 +42,7 @@ #include "obrender/render.h" #include "gettext.h" #include "obt/display.h" +#include "obt/xqueue.h" #include "obt/prop.h" #ifdef HAVE_UNISTD_H @@ -75,7 +77,7 @@ static RrImage *client_default_icon = NULL; static void client_get_all(ObClient *self, gboolean real); static void client_get_startup_id(ObClient *self); static void client_get_session_ids(ObClient *self); -static void client_save_session_ids(ObClient *self); +static void client_save_app_rule_values(ObClient *self); static void client_get_area(ObClient *self); static void client_get_desktop(ObClient *self); static void client_get_state(ObClient *self); @@ -106,23 +108,18 @@ static GSList *client_search_all_top_parents_internal(ObClient *self, static void client_call_notifies(ObClient *self, GSList *list); static void client_ping_event(ObClient *self, gboolean dead); static void client_prompt_kill(ObClient *self); -static gboolean client_can_steal_focus(ObClient *self, Time steal_time, - Time launch_time); +static gboolean client_can_steal_focus(ObClient *self, + gboolean allow_other_desktop, + gboolean request_from_user, + Time steal_time, Time launch_time); +static void client_setup_default_decor_and_functions(ObClient *self); +static void client_setup_decor_undecorated(ObClient *self); void client_startup(gboolean reconfig) { - if ((client_default_icon = RrImageCacheFind(ob_rr_icons, - ob_rr_theme->def_win_icon, - ob_rr_theme->def_win_icon_w, - ob_rr_theme->def_win_icon_h))) - RrImageRef(client_default_icon); - else { - client_default_icon = RrImageNew(ob_rr_icons); - RrImageAddPicture(client_default_icon, - ob_rr_theme->def_win_icon, - ob_rr_theme->def_win_icon_w, - ob_rr_theme->def_win_icon_h); - } + client_default_icon = RrImageNewFromData( + ob_rr_icons, ob_rr_theme->def_win_icon, + ob_rr_theme->def_win_icon_w, ob_rr_theme->def_win_icon_h); if (reconfig) return; @@ -149,7 +146,7 @@ static void client_call_notifies(ObClient *self, GSList *list) void client_add_destroy_notify(ObClientCallback func, gpointer data) { - ClientCallback *d = g_new(ClientCallback, 1); + ClientCallback *d = g_slice_new(ClientCallback); d->func = func; d->data = data; client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d); @@ -162,7 +159,22 @@ void client_remove_destroy_notify(ObClientCallback func) for (it = client_destroy_notifies; it; it = g_slist_next(it)) { ClientCallback *d = it->data; if (d->func == func) { - g_free(d); + g_slice_free(ClientCallback, d); + client_destroy_notifies = + g_slist_delete_link(client_destroy_notifies, it); + break; + } + } +} + +void client_remove_destroy_notify_data(ObClientCallback func, gpointer data) +{ + GSList *it; + + for (it = client_destroy_notifies; it; it = g_slist_next(it)) { + ClientCallback *d = it->data; + if (d->func == func && d->data == data) { + g_slice_free(ClientCallback, d); client_destroy_notifies = g_slist_delete_link(client_destroy_notifies, it); break; @@ -198,17 +210,18 @@ void client_manage(Window window, ObPrompt *prompt) { ObClient *self; XSetWindowAttributes attrib_set; - gboolean activate = FALSE; + gboolean try_activate = FALSE; + gboolean do_activate; ObAppSettings *settings; gboolean transient = FALSE; - Rect place, *monitor; - Time launch_time, map_time; + Rect place; + Time launch_time; guint32 user_time; + gboolean obplaced; + gulong ignore_start; ob_debug("Managing window: 0x%lx", window); - map_time = event_get_server_time(); - /* choose the events we want to receive on the CLIENT window (ObPrompt windows can request events too) */ attrib_set.event_mask = CLIENT_EVENTMASK | @@ -219,10 +232,11 @@ void client_manage(Window window, ObPrompt *prompt) /* create the ObClient struct, and populate it from the hints on the window */ - self = g_new0(ObClient, 1); + self = g_slice_new0(ObClient); self->obwin.type = OB_WINDOW_CLASS_CLIENT; self->window = window; self->prompt = prompt; + self->managed = TRUE; /* non-zero defaults */ self->wmstate = WithdrawnState; /* make sure it gets updated first time */ @@ -234,17 +248,27 @@ void client_manage(Window window, ObPrompt *prompt) ob_debug("Window type: %d", self->type); ob_debug("Window group: 0x%x", self->group?self->group->leader:0); - ob_debug("Window name: %s class: %s role: %s", self->name, self->class, self->role); + ob_debug("Window name: %s class: %s role: %s title: %s", + self->name, self->class, self->role, self->title); + ob_debug("Window group name: %s group class: %s", + self->group_name, self->group_class); /* per-app settings override stuff from client_get_all, and return the settings for other uses too. the returned settings is a shallow copy, that needs to be freed with g_free(). */ settings = client_get_settings_state(self); - /* now we have all of the window's information so we can set this up. - do this before creating the frame, so it can tell that we are still - mapping and doesn't go applying things right away */ - client_setup_decor_and_functions(self, FALSE); + /* the session should get the last say though */ + client_restore_session_state(self); + + /* the per-app settings/session may have changed the decorations for + the window, so we setup decorations for that here. this is a special + case because we want to place the window according to these decoration + changes. + we do this before setting up the frame so that it will reflect the + decorations of the window as it will be placed on screen. + */ + client_setup_decor_undecorated(self); /* specify that if we exit, the window should not be destroyed and should be reparented back to root automatically, unless we are managing @@ -261,14 +285,24 @@ void client_manage(Window window, ObPrompt *prompt) time now */ grab_server(FALSE); - /* the session should get the last say though */ - client_restore_session_state(self); + /* this needs to occur once we have a frame, since it sets a property on + the frame */ + client_update_opacity(self); + + /* don't put helper/modal windows on a different desktop if they are + related to the focused window. */ + if (!screen_compare_desktops(self->desktop, screen_desktop) && + focus_client && client_search_transient(focus_client, self) && + (client_helper(self) || self->modal)) + { + self->desktop = screen_desktop; + } /* tell startup notification that this app started */ launch_time = sn_app_started(self->startup_id, self->class, self->name); if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time)) - user_time = map_time; + user_time = event_time(); /* do this after we have a frame.. it uses the frame to help determine the WM_STATE to apply. */ @@ -284,16 +318,17 @@ void client_manage(Window window, ObPrompt *prompt) if (ob_state() != OB_STATE_STARTING && (!self->session || self->session->focused) && /* this means focus=true for window is same as config_focus_new=true */ - ((config_focus_new || (settings && settings->focus == 1)) || + ((config_focus_new || settings->focus == 1) || client_search_focus_tree_full(self)) && /* NET_WM_USER_TIME 0 when mapping means don't focus */ (user_time != 0) && /* this checks for focus=false for the window */ - (!settings || settings->focus != 0) && - focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE, + settings->focus != 0 && + focus_valid_target(self, self->desktop, + FALSE, FALSE, TRUE, TRUE, FALSE, FALSE, settings->focus == 1)) { - activate = TRUE; + try_activate = TRUE; } /* remove the client's border */ @@ -306,7 +341,16 @@ void client_manage(Window window, ObPrompt *prompt) /* where the frame was placed is where the window was originally */ place = self->area; - monitor = screen_physical_area_monitor(screen_find_monitor(&place)); + + ob_debug("Going to try activate new window? %s", + try_activate ? "yes" : "no"); + if (try_activate) + do_activate = client_can_steal_focus( + self, settings->focus == 1, + (!!launch_time || settings->focus == 1), + event_time(), launch_time); + else + do_activate = FALSE; /* figure out placement for the window if the window is new */ if (ob_state() == OB_STATE_RUNNING) { @@ -326,9 +370,26 @@ void client_manage(Window window, ObPrompt *prompt) "program + user specified" : "BADNESS !?")))), place.width, place.height); - /* splash screens are also returned as TRUE for transient, - and so will be forced on screen below */ - transient = place_client(self, &place.x, &place.y, settings); + obplaced = place_client(self, do_activate, &place, settings); + + /* watch for buggy apps that ask to be placed at (0,0) when there is + a strut there */ + if (!obplaced && place.x == 0 && place.y == 0 && + /* non-normal windows are allowed */ + client_normal(self) && + /* oldschool fullscreen windows are allowed */ + !client_is_oldfullscreen(self, &place)) + { + Rect *r; + + r = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, NULL); + if (r->x || r->y) { + place.x = r->x; + place.y = r->y; + ob_debug("Moving buggy app from (0,0) to (%d,%d)", r->x, r->y); + } + g_slice_free(Rect, r); + } /* make sure the window is visible. */ client_find_onscreen(self, &place.x, &place.y, @@ -344,21 +405,22 @@ void client_manage(Window window, ObPrompt *prompt) it is up to the placement routines to avoid the xinerama divides) - splash screens get "transient" set to TRUE by - the place_client call + children and splash screens are forced on + screen, but i don't remember why i decided to + do that. */ ob_state() == OB_STATE_RUNNING && - (transient || + (self->type == OB_CLIENT_TYPE_DIALOG || + self->type == OB_CLIENT_TYPE_SPLASH || (!((self->positioned & USPosition) || - (settings && settings->pos_given)) && + settings->pos_given) && client_normal(self) && !self->session && /* don't move oldschool fullscreen windows to fit inside the struts (fixes Acroread, which makes its fullscreen window fit the screen but it is not USSize'd or USPosition'd) */ - !(self->decorations == 0 && - RECT_EQUAL(place, *monitor))))); + !client_is_oldfullscreen(self, &place)))); } /* if the window isn't user-sized, then make it fit inside @@ -378,7 +440,7 @@ void client_manage(Window window, ObPrompt *prompt) /* don't shrink oldschool fullscreen windows to fit inside the struts (fixes Acroread, which makes its fullscreen window fit the screen but it is not USSize'd or USPosition'd) */ - !(self->decorations == 0 && RECT_EQUAL(place, *monitor))))) + !client_is_oldfullscreen(self, &place)))) { Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place); @@ -396,7 +458,7 @@ void client_manage(Window window, ObPrompt *prompt) place.width -= self->frame->size.left + self->frame->size.right; place.height -= self->frame->size.top + self->frame->size.bottom; - g_free(a); + g_slice_free(Rect, a); } ob_debug("placing window 0x%x at %d, %d with size %d x %d. " @@ -416,15 +478,28 @@ void client_manage(Window window, ObPrompt *prompt) client_apply_startup_state(self, place.x, place.y, place.width, place.height); - g_free(monitor); - monitor = NULL; + /* set the initial value of the desktop hint, when one wasn't requested + on map. */ + OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop); - ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s", - activate ? "yes" : "no"); - if (activate) { - activate = client_can_steal_focus(self, map_time, launch_time); + /* grab mouse bindings before showing the window */ + mouse_grab_for_client(self, TRUE); - if (!activate) { + if (!config_focus_under_mouse) + ignore_start = event_start_ignore_all_enters(); + + /* this has to happen before we try focus the window, but we want it to + happen after the client's stacking has been determined or it looks bad + */ + client_show(self); + + /* activate/hilight/raise the window */ + if (try_activate) { + if (do_activate) { + gboolean stacked = client_restore_session_stacking(self); + client_present(self, FALSE, !stacked, TRUE); + } + else { /* if the client isn't stealing focus, then hilite it so the user knows it is there, but don't do this if we're restoring from a session */ @@ -444,26 +519,8 @@ void client_manage(Window window, ObPrompt *prompt) stacking_raise(CLIENT_AS_WINDOW(self)); } - mouse_grab_for_client(self, TRUE); - - /* this has to happen before we try focus the window, but we want it to - happen after the client's stacking has been determined or it looks bad - */ - { - gulong ignore_start; - if (!config_focus_under_mouse) - ignore_start = event_start_ignore_all_enters(); - - client_show(self); - - if (!config_focus_under_mouse) - event_end_ignore_all_enters(ignore_start); - } - - if (activate) { - gboolean stacked = client_restore_session_stacking(self); - client_present(self, FALSE, !stacked, TRUE); - } + if (!config_focus_under_mouse) + event_end_ignore_all_enters(ignore_start); /* add to client list/map */ client_list = g_list_append(client_list, self); @@ -477,7 +534,7 @@ void client_manage(Window window, ObPrompt *prompt) client_set_list(); /* free the ObAppSettings shallow copy */ - g_free(settings); + g_slice_free(ObAppSettings, settings); ob_debug("Managed window 0x%lx plate 0x%x (%s)", window, self->frame->window, self->class); @@ -492,7 +549,7 @@ ObClient *client_fake_manage(Window window) /* do this minimal stuff to figure out the client's decorations */ - self = g_new0(ObClient, 1); + self = g_slice_new0(ObClient); self->window = window; client_get_all(self, FALSE); @@ -500,18 +557,18 @@ ObClient *client_fake_manage(Window window) uses too. this returns a shallow copy that needs to be freed */ settings = client_get_settings_state(self); - client_setup_decor_and_functions(self, FALSE); - /* create the decoration frame for the client window and adjust its size */ self->frame = frame_new(self); - frame_adjust_area(self->frame, FALSE, TRUE, TRUE); + + client_apply_startup_state(self, self->area.x, self->area.y, + self->area.width, self->area.height); ob_debug("gave extents left %d right %d top %d bottom %d", self->frame->size.left, self->frame->size.right, self->frame->size.top, self->frame->size.bottom); /* free the ObAppSettings shallow copy */ - g_free(settings); + g_slice_free(ObAppSettings, settings); return self; } @@ -550,6 +607,8 @@ void client_unmanage(ObClient *self) mouse_grab_for_client(self, FALSE); + self->managed = FALSE; + /* remove the window from our save set, unless we are managing an internal ObPrompt window */ if (!self->prompt) @@ -665,9 +724,11 @@ void client_unmanage(ObClient *self) g_free(self->name); g_free(self->class); g_free(self->role); + g_free(self->group_name); + g_free(self->group_class); g_free(self->client_machine); g_free(self->sm_client_id); - g_free(self); + g_slice_free(ObClient, self); } void client_fake_unmanage(ObClient *self) @@ -675,105 +736,170 @@ void client_fake_unmanage(ObClient *self) /* this is all that got allocated to get the decorations */ frame_free(self->frame); - g_free(self); + g_slice_free(ObClient, self); } -static gboolean client_can_steal_focus(ObClient *self, Time steal_time, +static gboolean client_can_steal_focus(ObClient *self, + gboolean allow_other_desktop, + gboolean request_from_user, + Time steal_time, Time launch_time) { gboolean steal; gboolean relative_focused; - gboolean parent_focused; steal = TRUE; - parent_focused = (focus_client != NULL && - client_search_focus_parent(self)); relative_focused = (focus_client != NULL && (client_search_focus_tree_full(self) != NULL || client_search_focus_group_full(self) != NULL)); /* This is focus stealing prevention */ - ob_debug_type(OB_DEBUG_FOCUS, - "Want to focus new window 0x%x at time %u " - "launched at %u (last user interaction time %u)", - self->window, steal_time, launch_time, - event_last_user_time); - - /* if it's on another desktop */ - if (!(self->desktop == screen_desktop || - self->desktop == DESKTOP_ALL) && - /* the timestamp is from before you changed desktops */ - launch_time && screen_desktop_user_time && - !event_time_after(launch_time, screen_desktop_user_time)) - { - steal = FALSE; - ob_debug_type(OB_DEBUG_FOCUS, - "Not focusing the window because its on another " - "desktop\n"); + ob_debug("Want to focus window 0x%x at time %u " + "launched at %u (last user interaction time %u) " + "request from %s, allow other desktop: %s, " + "desktop switch time %u", + self->window, steal_time, launch_time, + event_last_user_time, + (request_from_user ? "user" : "other"), + (allow_other_desktop ? "yes" : "no"), + screen_desktop_user_time); + + /* + if no launch time is provided for an application, make one up. + + if the window is related to other existing windows + and one of those windows was the last used + then we will give it a launch time equal to the last user time, + which will end up giving the window focus probably. + else + the window is related to other windows, but you are not working in + them? + seems suspicious, so we will give it a launch time of + NOW - STEAL_INTERVAL, + so it will be given focus only if we didn't use something else + during the steal interval. + else + the window is all on its own, so we can't judge it. give it a launch + time equal to the last user time, so it will probably take focus. + + this way running things from a terminal will give them focus, but popups + without a launch time shouldn't steal focus so easily. + */ + + if (!launch_time) { + if (client_has_relative(self)) { + if (event_last_user_time && client_search_focus_group_full(self)) { + /* our relative is focused */ + launch_time = event_last_user_time; + ob_debug("Unknown launch time, using %u - window in active " + "group", launch_time); + } + else if (!request_from_user) { + /* has relatives which are not being used. suspicious */ + launch_time = event_time() - OB_EVENT_USER_TIME_DELAY; + ob_debug("Unknown launch time, using %u - window in inactive " + "group", launch_time); + } + else { + /* has relatives which are not being used, but the user seems + to want to go there! */ + launch_time = event_last_user_time; + ob_debug("Unknown launch time, using %u - user request", + launch_time); + } + } + else { + /* the window is on its own, probably the user knows it is going + to appear */ + launch_time = event_last_user_time; + ob_debug("Unknown launch time, using %u - independent window", + launch_time); + } + } + + /* if it's on another desktop + and if allow_other_desktop is true, we generally let it steal focus. + but if it didn't come from the user, don't let it steal unless it was + launched before the user switched desktops. + focus, unless it was launched after we changed desktops and the request + came from the user + */ + if (!screen_compare_desktops(screen_desktop, self->desktop)) { + /* must be allowed */ + if (!allow_other_desktop) { + steal = FALSE; + ob_debug("Not focusing the window because its on another desktop"); + } + /* if we don't know when the desktop changed, but request is from an + application, don't let it change desktop on you */ + else if (!request_from_user) { + steal = FALSE; + ob_debug("Not focusing the window because non-user request"); + } } /* If something is focused... */ else if (focus_client) { /* If the user is working in another window right now, then don't steal focus */ - if (!parent_focused && - event_last_user_time && launch_time && - event_time_after(event_last_user_time, launch_time) && - event_last_user_time != launch_time && + if (!relative_focused && + event_last_user_time && + /* last user time must be strictly > launch_time to block focus */ + (event_time_after(event_last_user_time, launch_time) && + event_last_user_time != launch_time) && event_time_after(event_last_user_time, steal_time - OB_EVENT_USER_TIME_DELAY)) { steal = FALSE; - ob_debug_type(OB_DEBUG_FOCUS, - "Not focusing the window because the user is " - "working in another window that is not " - "its parent"); - } - /* If the new window is a transient (and its relatives aren't - focused) */ - else if (client_has_parent(self) && !relative_focused) { - steal = FALSE; - ob_debug_type(OB_DEBUG_FOCUS, - "Not focusing the window because it is a " - "transient, and its relatives aren't focused"); - } - /* Don't steal focus from globally active clients. - I stole this idea from KWin. It seems nice. - */ - else if (!(focus_client->can_focus || - focus_client->focus_notify)) - { - steal = FALSE; - ob_debug_type(OB_DEBUG_FOCUS, - "Not focusing the window because a globally " - "active client has focus"); + ob_debug("Not focusing the window because the user is " + "working in another window that is not its relative"); } /* Don't move focus if it's not going to go to this window anyway */ else if (client_focus_target(self) != self) { steal = FALSE; - ob_debug_type(OB_DEBUG_FOCUS, - "Not focusing the window because another window " - "would get the focus anyway"); + ob_debug("Not focusing the window because another window " + "would get the focus anyway"); } - /* Don't move focus if the window is not visible on the current - desktop and none of its relatives are focused */ - else if (!(self->desktop == screen_desktop || - self->desktop == DESKTOP_ALL) && - !relative_focused) - { - steal = FALSE; - ob_debug_type(OB_DEBUG_FOCUS, - "Not focusing the window because it is on " - "another desktop and no relatives are focused "); + /* For requests that don't come from the user */ + else if (!request_from_user) { + /* If the new window is a transient (and its relatives aren't + focused) */ + if (client_has_parent(self) && !relative_focused) { + steal = FALSE; + ob_debug("Not focusing the window because it is a " + "transient, and its relatives aren't focused"); + } + /* Don't steal focus from globally active clients. + I stole this idea from KWin. It seems nice. + */ + else if (!(focus_client->can_focus || focus_client->focus_notify)) + { + steal = FALSE; + ob_debug("Not focusing the window because a globally " + "active client has focus"); + } + /* Don't move focus if the window is not visible on the current + desktop and none of its relatives are focused */ + else if (!allow_other_desktop && + !screen_compare_desktops(self->desktop, screen_desktop) && + !relative_focused) + { + steal = FALSE; + ob_debug("Not focusing the window because it is on " + "another desktop and no relatives are focused "); + } } } if (!steal) - ob_debug_type(OB_DEBUG_FOCUS, - "Focus stealing prevention activated for %s at " - "time %u (last user interaction time %u)", - self->title, steal_time, event_last_user_time); + ob_debug("Focus stealing prevention activated for %s at " + "time %u (last user interaction time %u)", + self->title, steal_time, event_last_user_time); + else + ob_debug("Allowing focus stealing for %s at time %u (last user " + "interaction time %u)", + self->title, steal_time, event_last_user_time); return steal; } @@ -790,23 +916,38 @@ static ObAppSettings *client_get_settings_state(ObClient *self) ObAppSettings *app = it->data; gboolean match = TRUE; - g_assert(app->name != NULL || app->class != NULL); + g_assert(app->name != NULL || app->class != NULL || + app->role != NULL || app->title != NULL || + app->group_name != NULL || app->group_class != NULL || + (signed)app->type >= 0); - /* we know that either name or class is not NULL so it will have to - match to use the rule */ if (app->name && !g_pattern_match(app->name, strlen(self->name), self->name, NULL)) match = FALSE; + else if (app->group_name && + !g_pattern_match(app->group_name, + strlen(self->group_name), self->group_name, NULL)) + match = FALSE; else if (app->class && !g_pattern_match(app->class, strlen(self->class), self->class, NULL)) match = FALSE; + else if (app->group_class && + !g_pattern_match(app->group_class, + strlen(self->group_class), self->group_class, + NULL)) + match = FALSE; else if (app->role && !g_pattern_match(app->role, strlen(self->role), self->role, NULL)) match = FALSE; - else if ((signed)app->type >= 0 && app->type != self->type) + else if (app->title && + !g_pattern_match(app->title, + strlen(self->title), self->title, NULL)) + match = FALSE; + else if ((signed)app->type >= 0 && app->type != self->type) { match = FALSE; + } if (match) { ob_debug("Window matching: %s", app->name); @@ -844,6 +985,9 @@ static ObAppSettings *client_get_settings_state(ObClient *self) self->desktop = settings->desktop - 1; } + if (settings->opacity != -1) + self->opacity = settings->opacity; + if (settings->layer == -1) { self->below = TRUE; self->above = FALSE; @@ -902,6 +1046,8 @@ static void client_restore_session_state(ObClient *self) self->max_horz = self->session->max_horz; self->max_vert = self->session->max_vert; self->undecorated = self->session->undecorated; + + self->opacity = self->session->opacity; } static gboolean client_restore_session_stacking(ObClient *self) @@ -1052,7 +1198,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height) *y = a->y + MAX(0, a->height - fh); - g_free(a); + g_slice_free(Rect, a); } /* get where the client should be */ @@ -1071,20 +1217,28 @@ static void client_get_all(ObClient *self, gboolean real) 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_normal_hints(self); + /* set up the maximum possible decor/functions */ + client_setup_default_decor_and_functions(self); + + client_get_state(self); + /* get the session related properties, these can change decorations from per-app settings */ client_get_session_ids(self); - client_save_session_ids(self); - /* now we got everything that can affect the decorations */ + /* get this early so we have it for debugging, also this can be used + by app rule matching */ + client_update_title(self); + + /* now we got everything that can affect the decorations or app rule + matching */ if (!real) return; - /* get this early so we have it for debugging */ - client_update_title(self); + /* save the values of the variables used for app rule matching */ + client_save_app_rule_values(self); client_update_protocols(self); @@ -1118,11 +1272,10 @@ static void client_get_all(ObClient *self, gboolean real) static void client_get_startup_id(ObClient *self) { - if (!(OBT_PROP_GETS(self->window, NET_STARTUP_ID, utf8, - &self->startup_id))) + if (!(OBT_PROP_GETS_UTF8(self->window, NET_STARTUP_ID, &self->startup_id))) if (self->group) - OBT_PROP_GETS(self->group->leader, - NET_STARTUP_ID, utf8, &self->startup_id); + OBT_PROP_GETS_UTF8(self->group->leader, NET_STARTUP_ID, + &self->startup_id); } static void client_get_area(ObClient *self) @@ -1523,7 +1676,13 @@ void client_update_sync_request_counter(ObClient *self) if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i)) { + XSyncValue val; + self->sync_counter = i; + + /* this must be set when managing a new window according to EWMH */ + XSyncIntToValue(&val, 0); + XSyncSetCounter(obt_display, self->sync_counter, val); } else self->sync_counter = None; } @@ -1551,6 +1710,16 @@ void client_update_colormap(ObClient *self, Colormap colormap) self->colormap = colormap; } +void client_update_opacity(ObClient *self) +{ + guint32 o; + + if (OBT_PROP_GET32(self->window, NET_WM_WINDOW_OPACITY, CARDINAL, &o)) + OBT_PROP_SET32(self->frame->window, NET_WM_WINDOW_OPACITY, CARDINAL, o); + else + OBT_PROP_ERASE(self->frame->window, NET_WM_WINDOW_OPACITY); +} + void client_update_normal_hints(ObClient *self) { XSizeHints size; @@ -1607,7 +1776,7 @@ void client_update_normal_hints(ObClient *self) ob_debug("Normal hints: not set"); } -void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) +static void client_setup_default_decor_and_functions(ObClient *self) { /* start with everything (cept fullscreen) */ self->decorations = @@ -1683,6 +1852,13 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) break; } + /* If the client has no decor from its type (which never changes) then + don't allow the user to "undecorate" the window. Otherwise, allow them + to, even if there are motif hints removing the decor, because those + may change these days (e.g. chromium) */ + if (self->decorations == 0) + self->functions &= ~OB_CLIENT_FUNC_UNDECORATE; + /* Mwm Hints are applied subtractively to what has already been chosen for decor and functionality */ if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) { @@ -1733,24 +1909,31 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE; self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE; } +} - if (self->max_horz && self->max_vert) { - /* you can't resize fully maximized windows */ - self->functions &= ~OB_CLIENT_FUNC_RESIZE; - /* kill the handle on fully maxed windows */ - self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS); - } - - /* If there are no decorations to remove, don't allow the user to try - toggle the state */ - if (self->decorations == 0) - self->functions &= ~OB_CLIENT_FUNC_UNDECORATE; - - /* finally, the user can have requested no decorations, which overrides - everything (but doesnt give it a border if it doesnt have one) */ +/*! Set up decor for a client based on its undecorated state. */ +static void client_setup_decor_undecorated(ObClient *self) +{ + /* If the user requested no decorations, then remove all the decorations, + except the border. But don't add a border if there wasn't one. */ if (self->undecorated) self->decorations &= (config_theme_keepborder ? OB_FRAME_DECOR_BORDER : 0); +} + +void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) +{ + client_setup_default_decor_and_functions(self); + + client_setup_decor_undecorated(self); + + if (self->max_horz && self->max_vert) { + /* once upon a time you couldn't resize maximized windows, that is not + the case any more though ! + + but do kill the handle on fully maxed windows */ + self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS); + } /* if we don't have a titlebar, then we cannot shade! */ if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR)) @@ -1767,8 +1950,8 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) client_change_allowed_actions(self); if (reconfig) - /* force reconfigure to make sure decorations are updated */ - client_reconfigure(self, TRUE); + /* reconfigure to make sure decorations are updated */ + client_reconfigure(self, FALSE); } static void client_change_allowed_actions(ObClient *self) @@ -1911,6 +2094,8 @@ void client_update_wmhints(ObClient *self) XFree(hints); } + + focus_cycle_addremove(self, TRUE); } void client_update_title(ObClient *self) @@ -1922,15 +2107,14 @@ void client_update_title(ObClient *self) g_free(self->original_title); /* try netwm */ - if (!OBT_PROP_GETS(self->window, NET_WM_NAME, utf8, &data)) { + if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_NAME, &data)) { /* try old x stuff */ - if (!(OBT_PROP_GETS(self->window, WM_NAME, locale, &data) - || OBT_PROP_GETS(self->window, WM_NAME, utf8, &data))) { + if (!OBT_PROP_GETS(self->window, WM_NAME, &data)) { if (self->transient) { - /* - GNOME alert windows are not given titles: - http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html - */ + /* + GNOME alert windows are not given titles: + http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html + */ data = g_strdup(""); } else data = g_strdup(_("Unnamed Window")); @@ -1953,7 +2137,7 @@ void client_update_title(ObClient *self) g_free(data); } - OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, utf8, visible); + OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, visible); self->title = visible; if (self->frame) @@ -1964,10 +2148,9 @@ void client_update_title(ObClient *self) g_free(self->icon_title); /* try netwm */ - if (!OBT_PROP_GETS(self->window, NET_WM_ICON_NAME, utf8, &data)) + if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_ICON_NAME, &data)) /* try old x stuff */ - if (!(OBT_PROP_GETS(self->window, WM_ICON_NAME, locale, &data) || - OBT_PROP_GETS(self->window, WM_ICON_NAME, utf8, &data))) + if (!OBT_PROP_GETS(self->window, WM_ICON_NAME, &data)) data = g_strdup(self->title); if (self->client_machine) { @@ -1985,7 +2168,7 @@ void client_update_title(ObClient *self) g_free(data); } - OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, utf8, visible); + OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, visible); self->icon_title = visible; } @@ -2012,7 +2195,7 @@ void client_update_strut(ObClient *self) if (!got && OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) { if (num == 4) { - Rect *a; + const Rect *a; got = TRUE; @@ -2025,7 +2208,6 @@ void client_update_strut(ObClient *self) a->x, a->x + a->width - 1, a->y, a->y + a->height - 1, a->x, a->x + a->width - 1); - g_free(a); } g_free(data); } @@ -2049,7 +2231,6 @@ void client_update_icons(ObClient *self) guint num; guint32 *data; guint w, h, i, j; - guint num_seen; /* number of icons present */ RrImage *img; img = NULL; @@ -2062,13 +2243,15 @@ void client_update_icons(ObClient *self) if (OBT_PROP_GETA32(self->window, NET_WM_ICON, CARDINAL, &data, &num)) { /* figure out how many valid icons are in here */ i = 0; - num_seen = 0; while (i + 2 < num) { /* +2 is to make sure there is a w and h */ w = data[i++]; h = data[i++]; /* watch for the data being too small for the specified size, or for zero sized icons. */ - if (i + w*h > num || w == 0 || h == 0) break; + if (i + w*h > num || w == 0 || h == 0) { + i += w*h; + continue; + } /* convert it to the right bit order for ObRender */ for (j = 0; j < w*h; ++j) @@ -2078,29 +2261,13 @@ void client_update_icons(ObClient *self) (((data[i+j] >> 8) & 0xff) << RrDefaultGreenOffset) + (((data[i+j] >> 0) & 0xff) << RrDefaultBlueOffset); - /* is it in the cache? */ - img = RrImageCacheFind(ob_rr_icons, &data[i], w, h); - if (img) RrImageRef(img); /* own it */ + /* add it to the image cache as an original */ + if (!img) + img = RrImageNewFromData(ob_rr_icons, &data[i], w, h); + else + RrImageAddFromData(img, &data[i], w, h); i += w*h; - ++num_seen; - - /* don't bother looping anymore if we already found it in the cache - since we'll just use that! */ - if (img) break; - } - - /* if it's not in the cache yet, then add it to the cache now. - we have already converted it to the correct bit order above */ - if (!img && num_seen > 0) { - img = RrImageNew(ob_rr_icons); - i = 0; - for (j = 0; j < num_seen; ++j) { - w = data[i++]; - h = data[i++]; - RrImageAddPicture(img, &data[i], w, h); - i += w*h; - } } g_free(data); @@ -2124,15 +2291,10 @@ void client_update_icons(ObClient *self) if (xicon) { if (w > 0 && h > 0) { - /* is this icon in the cache yet? */ - img = RrImageCacheFind(ob_rr_icons, data, w, h); - if (img) RrImageRef(img); /* own it */ - - /* if not, then add it */ - if (!img) { - img = RrImageNew(ob_rr_icons); - RrImageAddPicture(img, data, w, h); - } + if (!img) + img = RrImageNewFromData(ob_rr_icons, data, w, h); + else + RrImageAddFromData(img, data, w, h); } g_free(data); @@ -2202,19 +2364,14 @@ static void client_get_session_ids(ObClient *self) leader = None; /* get the SM_CLIENT_ID */ - got = FALSE; - if (leader) - got = OBT_PROP_GETS(leader, SM_CLIENT_ID, locale, &self->sm_client_id); - if (!got) - OBT_PROP_GETS(self->window, SM_CLIENT_ID, locale, &self->sm_client_id); + if (leader && leader != self->window) + OBT_PROP_GETS_XPCS(leader, SM_CLIENT_ID, &self->sm_client_id); + else + OBT_PROP_GETS_XPCS(self->window, SM_CLIENT_ID, &self->sm_client_id); /* get the WM_CLASS (name and class). make them "" if they are not provided */ - got = FALSE; - if (leader) - got = OBT_PROP_GETSS(leader, WM_CLASS, locale, &ss); - if (!got) - got = OBT_PROP_GETSS(self->window, WM_CLASS, locale, &ss); + got = OBT_PROP_GETSS_TYPE(self->window, WM_CLASS, STRING_NO_CC, &ss); if (got) { if (ss[0]) { @@ -2228,12 +2385,27 @@ static void client_get_session_ids(ObClient *self) if (self->name == NULL) self->name = g_strdup(""); if (self->class == NULL) self->class = g_strdup(""); - /* get the WM_WINDOW_ROLE. make it "" if it is not provided */ - got = FALSE; + /* get the WM_CLASS (name and class) from the group leader. make them "" if + they are not provided */ if (leader) - got = OBT_PROP_GETS(leader, WM_WINDOW_ROLE, locale, &s); - if (!got) - got = OBT_PROP_GETS(self->window, WM_WINDOW_ROLE, locale, &s); + got = OBT_PROP_GETSS_TYPE(leader, WM_CLASS, STRING_NO_CC, &ss); + else + got = FALSE; + + if (got) { + if (ss[0]) { + self->group_name = g_strdup(ss[0]); + if (ss[1]) + self->group_class = g_strdup(ss[1]); + } + g_strfreev(ss); + } + + if (self->group_name == NULL) self->group_name = g_strdup(""); + if (self->group_class == NULL) self->group_class = g_strdup(""); + + /* get the WM_WINDOW_ROLE. make it "" if it is not provided */ + got = OBT_PROP_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s); if (got) self->role = s; @@ -2244,9 +2416,9 @@ static void client_get_session_ids(ObClient *self) got = FALSE; if (leader) - got = OBT_PROP_GETSS(leader, WM_COMMAND, locale, &ss); + got = OBT_PROP_GETSS(leader, WM_COMMAND, &ss); if (!got) - got = OBT_PROP_GETSS(self->window, WM_COMMAND, locale, &ss); + got = OBT_PROP_GETSS(self->window, WM_COMMAND, &ss); if (got) { /* merge/mash them all together */ @@ -2269,9 +2441,9 @@ static void client_get_session_ids(ObClient *self) /* get the WM_CLIENT_MACHINE */ got = FALSE; if (leader) - got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, locale, &s); + got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, &s); if (!got) - got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, locale, &s); + got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, &s); if (got) { gchar localhost[128]; @@ -2291,13 +2463,39 @@ static void client_get_session_ids(ObClient *self) } } -/*! Save the session IDs as seen by Openbox when the window mapped, so that - users can still access them later if the app changes them */ -static void client_save_session_ids(ObClient *self) +/*! Save the properties used for app matching rules, as seen by Openbox when + the window mapped, so that users can still access them later if the app + changes them */ +static void client_save_app_rule_values(ObClient *self) { - OBT_PROP_SETS(self->window, OB_ROLE, utf8, self->role); - OBT_PROP_SETS(self->window, OB_NAME, utf8, self->name); - OBT_PROP_SETS(self->window, OB_CLASS, utf8, self->class); + const gchar *type; + + OBT_PROP_SETS(self->window, OB_APP_ROLE, self->role); + OBT_PROP_SETS(self->window, OB_APP_NAME, self->name); + OBT_PROP_SETS(self->window, OB_APP_CLASS, self->class); + OBT_PROP_SETS(self->window, OB_APP_GROUP_NAME, self->group_name); + OBT_PROP_SETS(self->window, OB_APP_GROUP_CLASS, self->group_class); + OBT_PROP_SETS(self->window, OB_APP_TITLE, self->original_title); + + switch (self->type) { + case OB_CLIENT_TYPE_NORMAL: + type = "normal"; break; + case OB_CLIENT_TYPE_DIALOG: + type = "dialog"; break; + case OB_CLIENT_TYPE_UTILITY: + type = "utility"; break; + case OB_CLIENT_TYPE_MENU: + type = "menu"; break; + case OB_CLIENT_TYPE_TOOLBAR: + type = "toolbar"; break; + case OB_CLIENT_TYPE_SPLASH: + type = "splash"; break; + case OB_CLIENT_TYPE_DESKTOP: + type = "desktop"; break; + case OB_CLIENT_TYPE_DOCK: + type = "dock"; break; + } + OBT_PROP_SETS(self->window, OB_APP_TYPE, type); } static void client_change_wm_state(ObClient *self) @@ -2414,12 +2612,32 @@ gboolean client_has_parent(ObClient *self) return self->parents != NULL; } +gboolean client_has_children(ObClient *self) +{ + return self->transients != NULL; +} + +gboolean client_is_oldfullscreen(const ObClient *self, + const Rect *area) +{ + const Rect *monitor, *allmonitors; + + /* No decorations and fills the monitor = oldskool fullscreen. + But not for maximized windows. + */ + + if (self->decorations || self->max_horz || self->max_vert) return FALSE; + + monitor = screen_physical_area_monitor(screen_find_monitor(area)); + allmonitors = screen_physical_area_all_monitors(); + + return (RECT_EQUAL(*area, *monitor) || + RECT_EQUAL(*area, *allmonitors)); +} + static ObStackingLayer calc_layer(ObClient *self) { ObStackingLayer l; - Rect *monitor; - - monitor = screen_physical_area_monitor(client_monitor(self)); if (self->type == OB_CLIENT_TYPE_DESKTOP) l = OB_STACKING_LAYER_DESKTOP; @@ -2428,12 +2646,7 @@ static ObStackingLayer calc_layer(ObClient *self) else l = OB_STACKING_LAYER_ABOVE; } else if ((self->fullscreen || - /* No decorations and fills the monitor = oldskool fullscreen. - But not for maximized windows. - */ - (self->decorations == 0 && - !(self->max_horz && self->max_vert) && - RECT_EQUAL(self->area, *monitor))) && + client_is_oldfullscreen(self, &self->area)) && /* you are fullscreen while you or your children are focused.. */ (client_focused(self) || client_search_focus_tree(self) || /* you can be fullscreen if you're on another desktop */ @@ -2448,8 +2661,6 @@ static ObStackingLayer calc_layer(ObClient *self) else if (self->below) l = OB_STACKING_LAYER_BELOW; else l = OB_STACKING_LAYER_NORMAL; - g_free(monitor); - return l; } @@ -2604,6 +2815,12 @@ gboolean client_helper(ObClient *self) self->type == OB_CLIENT_TYPE_TOOLBAR); } +gboolean client_occupies_space(ObClient *self) +{ + return !(self->type == OB_CLIENT_TYPE_DESKTOP || + self->type == OB_CLIENT_TYPE_SPLASH); +} + gboolean client_mouse_focusable(ObClient *self) { return !(self->type == OB_CLIENT_TYPE_MENU || @@ -2630,6 +2847,7 @@ static void client_apply_startup_state(ObClient *self, gboolean demands_attention = self->demands_attention; gboolean max_horz = self->max_horz; gboolean max_vert = self->max_vert; + guint8 opacity = self->opacity; Rect oldarea; gint l; @@ -2658,8 +2876,6 @@ static void client_apply_startup_state(ObClient *self, if (iconic) client_iconify(self, TRUE, FALSE, TRUE); - if (fullscreen) - client_fullscreen(self, TRUE); if (undecorated) client_set_undecorated(self, TRUE); if (shaded) @@ -2674,6 +2890,16 @@ static void client_apply_startup_state(ObClient *self, else if (max_horz) client_maximize(self, TRUE, 1); + /* fullscreen removes the ability to apply other states */ + if (fullscreen) + client_fullscreen(self, TRUE); + + /* make sure client_setup_decor_and_functions() is called at least once */ + client_setup_decor_and_functions(self, FALSE); + + /* make the client semi-transparent */ + client_set_opacity(self, opacity); + /* if the window hasn't been configured yet, then do so now, in fact the x,y,w,h may _not_ be the same as the area rect, which can end up meaning that the client isn't properly moved/resized by the fullscreen @@ -2687,9 +2913,6 @@ static void client_apply_startup_state(ObClient *self, self->area = oldarea; client_configure(self, x, y, w, h, FALSE, TRUE, FALSE); - /* set the desktop hint, to make sure that it always exists */ - OBT_PROP_SET32(self->window, NET_WM_DESKTOP, CARDINAL, self->desktop); - /* nothing to do for the other states: skip_taskbar skip_pager @@ -2769,6 +2992,14 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, the updated frame dimensions. */ frame_adjust_area(self->frame, FALSE, TRUE, TRUE); + /* cap any X windows at the size of an unsigned short */ + *w = MIN(*w, + (gint)G_MAXUSHORT + - self->frame->size.left - self->frame->size.right); + *h = MIN(*h, + (gint)G_MAXUSHORT + - self->frame->size.top - self->frame->size.bottom); + /* gets the frame's position */ frame_client_gravity(self->frame, x, y); @@ -2776,7 +3007,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, /* set the size and position if fullscreen */ if (self->fullscreen) { - Rect *a; + const Rect *a; guint i; i = screen_find_monitor(&desired); @@ -2789,8 +3020,6 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, user = FALSE; /* ignore if the client can't be moved/resized when it is fullscreening */ - - g_free(a); } else if (self->max_horz || self->max_vert) { Rect *a; guint i; @@ -2813,7 +3042,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, user = FALSE; /* ignore if the client can't be moved/resized when it is maximizing */ - g_free(a); + g_slice_free(Rect, a); } /* gets the client's position */ @@ -2824,11 +3053,11 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, through this code */ { gint basew, baseh, minw, minh; - gint incw, inch; + gint incw, inch, maxw, maxh; gfloat minratio, maxratio; - incw = self->fullscreen || self->max_horz ? 1 : self->size_inc.width; - inch = self->fullscreen || self->max_vert ? 1 : self->size_inc.height; + incw = self->size_inc.width; + inch = self->size_inc.height; minratio = self->fullscreen || (self->max_horz && self->max_vert) ? 0 : self->min_ratio; maxratio = self->fullscreen || (self->max_horz && self->max_vert) ? @@ -2864,6 +3093,10 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, *w -= basew; *h -= baseh; + /* the sizes to used for maximized */ + maxw = *w; + maxh = *h; + /* keep to the increments */ *w /= incw; *h /= inch; @@ -2879,6 +3112,10 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, *w *= incw; *h *= inch; + /* if maximized/fs then don't use the size increments */ + if (self->fullscreen || self->max_horz) *w = maxw; + if (self->fullscreen || self->max_vert) *h = maxh; + *w += basew; *h += baseh; @@ -2931,8 +3168,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, void client_configure(ObClient *self, gint x, gint y, gint w, gint h, gboolean user, gboolean final, gboolean force_reply) { - Rect oldframe; - gint oldw, oldh; + Rect oldframe, oldclient; gboolean send_resize_client; gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE; gboolean fmoved, fresized; @@ -2952,9 +3188,8 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h, moved = (x != self->area.x || y != self->area.y); resized = (w != self->area.width || h != self->area.height); - oldw = self->area.width; - oldh = self->area.height; oldframe = self->frame->area; + oldclient = self->area; RECT_SET(self->area, x, y, w, h); /* for app-requested resizes, always resize if 'resized' is true. @@ -2965,10 +3200,10 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h, (resized && config_resize_redraw)))); /* if the client is enlarging, then resize the client before the frame */ - if (send_resize_client && (w > oldw || h > oldh)) { + if (send_resize_client && (w > oldclient.width || h > oldclient.height)) { XMoveResizeWindow(obt_display, self->window, self->frame->size.left, self->frame->size.top, - MAX(w, oldw), MAX(h, oldh)); + MAX(w, oldclient.width), MAX(h, oldclient.height)); frame_adjust_client_area(self->frame); } @@ -3018,16 +3253,18 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h, When user = FALSE, then the request is coming from the application itself, and we are more strict about when to send a synthetic ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5 - in this case (if force_reply is true) + in this case (or send one if force_reply is true) When user = TRUE, then the request is coming from "us", like when we maximize a window or something. In this case we are more lenient. We used to follow the same rules as above, but _Java_ Swing can't handle this. So just to appease Swing, when user = TRUE, we always send a synthetic ConfigureNotify to give the window its root coordinates. + Lastly, if force_reply is TRUE, we always send a + ConfigureNotify, which is needed during a resize with XSYNCronization. */ if ((!user && !resized && (rootmoved || force_reply)) || - (user && final && rootmoved)) + (user && ((!resized && force_reply) || (final && rootmoved)))) { XEvent event; @@ -3056,7 +3293,8 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h, both of these resize sections may run, because the top one only resizes in the direction that is growing */ - if (send_resize_client && (w <= oldw || h <= oldh)) { + if (send_resize_client && (w <= oldclient.width || h <= oldclient.height)) + { frame_adjust_client_area(self->frame); XMoveResizeWindow(obt_display, self->window, self->frame->size.left, self->frame->size.top, w, h); @@ -3065,9 +3303,18 @@ void client_configure(ObClient *self, gint x, gint y, gint w, gint h, XFlush(obt_display); /* if it moved between monitors, then this can affect the stacking - layer of this window or others - for fullscreen windows */ - if (screen_find_monitor(&self->frame->area) != - screen_find_monitor(&oldframe)) + layer of this window or others - for fullscreen windows. + also if it changed to/from oldschool fullscreen then its layer may + change + + watch out tho, don't try change stacking stuff if the window is no + longer being managed ! + */ + if (self->managed && + (screen_find_monitor(&self->frame->area) != + screen_find_monitor(&oldframe) || + (final && (client_is_oldfullscreen(self, &oldclient) != + client_is_oldfullscreen(self, &self->area))))) { client_calc_layer(self); } @@ -3085,6 +3332,9 @@ void client_fullscreen(ObClient *self, gboolean fs) if (fs) { self->pre_fullscreen_area = self->area; + self->pre_fullscreen_max_horz = self->max_horz; + self->pre_fullscreen_max_vert = self->max_vert; + /* if the window is maximized, its area isn't all that meaningful. save its premax area instead. */ if (self->max_horz) { @@ -3106,6 +3356,17 @@ void client_fullscreen(ObClient *self, gboolean fs) g_assert(self->pre_fullscreen_area.width > 0 && self->pre_fullscreen_area.height > 0); + self->max_horz = self->pre_fullscreen_max_horz; + self->max_vert = self->pre_fullscreen_max_vert; + if (self->max_horz) { + self->pre_max_area.x = self->pre_fullscreen_area.x; + self->pre_max_area.width = self->pre_fullscreen_area.width; + } + if (self->max_vert) { + self->pre_max_area.y = self->pre_fullscreen_area.y; + self->pre_max_area.height = self->pre_fullscreen_area.height; + } + x = self->pre_fullscreen_area.x; y = self->pre_fullscreen_area.y; w = self->pre_fullscreen_area.width; @@ -3116,6 +3377,11 @@ void client_fullscreen(ObClient *self, gboolean fs) ob_debug("Window %s going fullscreen (%d)", self->title, self->fullscreen); + if (fs) { + /* make sure the window is on some monitor */ + client_find_onscreen(self, &x, &y, w, h, FALSE); + } + client_setup_decor_and_functions(self, FALSE); client_move_resize(self, x, y, w, h); @@ -3148,7 +3414,7 @@ static void client_iconify_recursive(ObClient *self, self->iconic = iconic; /* update the focus lists.. iconic windows go to the bottom of - the list */ + the list. this will also call focus_cycle_addremove(). */ focus_order_to_bottom(self); changed = TRUE; @@ -3160,9 +3426,10 @@ static void client_iconify_recursive(ObClient *self, self->desktop != DESKTOP_ALL) client_set_desktop(self, screen_desktop, FALSE, FALSE); - /* this puts it after the current focused window */ - focus_order_remove(self); - focus_order_add_new(self); + /* this puts it after the current focused window, this will + also cause focus_cycle_addremove() to be called for the + client */ + focus_order_like_new(self); changed = TRUE; } @@ -3257,6 +3524,11 @@ void client_maximize(ObClient *self, gboolean max, gint dir) if (dir == 0 || dir == 2) /* vert */ self->max_vert = max; + if (max) { + /* make sure the window is on some monitor */ + client_find_onscreen(self, &x, &y, w, h, FALSE); + } + client_change_state(self); /* change the state hints on the client */ client_setup_decor_and_functions(self, FALSE); @@ -3317,7 +3589,7 @@ void client_close(ObClient *self) else { /* request the client to close with WM_DELETE_WINDOW */ OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS, - OBT_PROP_ATOM(WM_DELETE_WINDOW), event_curtime, + OBT_PROP_ATOM(WM_DELETE_WINDOW), event_time(), 0, 0, 0, NoEventMask); /* we're trying to close the window, so see if it is responding. if it @@ -3495,6 +3767,8 @@ static void client_set_desktop_recursive(ObClient *self, /* the new desktop's geometry may be different, so we may need to resize, for example if we are maximized */ client_reconfigure(self, FALSE); + + focus_cycle_addremove(self, FALSE); } /* move all transients */ @@ -3510,6 +3784,14 @@ void client_set_desktop(ObClient *self, guint target, { self = client_search_top_direct_parent(self); client_set_desktop_recursive(self, target, donthide, dontraise); + + focus_cycle_addremove(NULL, TRUE); +} + +void client_set_opacity(ObClient *self, guint8 opacity) +{ + OBT_PROP_SET32(self->window, NET_WM_WINDOW_OPACITY, CARDINAL, + opacity * 16777216); } gboolean client_is_direct_child(ObClient *parent, ObClient *child) @@ -3531,36 +3813,31 @@ ObClient *client_search_modal_child(ObClient *self) return NULL; } -static gboolean client_validate_unmap(ObClient *self, int n) -{ - XEvent e; - gboolean ret = TRUE; - - if (XCheckTypedWindowEvent(obt_display, self->window, UnmapNotify, &e)) { - if (n < self->ignore_unmaps) // ignore this one, but look for more - ret = client_validate_unmap(self, n+1); - else - ret = FALSE; // the window is going to become unmanaged - - /* put them back on the event stack so they end up in the same order */ - XPutBackEvent(obt_display, &e); - } +struct ObClientFindDestroyUnmap { + Window window; + gint ignore_unmaps; +}; - return ret; +static gboolean find_destroy_unmap(XEvent *e, gpointer data) +{ + struct ObClientFindDestroyUnmap *find = data; + if (e->type == DestroyNotify) + return e->xdestroywindow.window == find->window; + if (e->type == UnmapNotify && e->xunmap.window == find->window) + /* ignore the first $find->ignore_unmaps$ many unmap events */ + return --find->ignore_unmaps < 0; + return FALSE; } gboolean client_validate(ObClient *self) { - XEvent e; + struct ObClientFindDestroyUnmap find; XSync(obt_display, FALSE); /* get all events on the server */ - if (XCheckTypedWindowEvent(obt_display, self->window, DestroyNotify, &e)) { - XPutBackEvent(obt_display, &e); - return FALSE; - } - - if (!client_validate_unmap(self, 0)) + find.window = self->window; + find.ignore_unmaps = self->ignore_unmaps; + if (xqueue_exists_local(find_destroy_unmap, &find)) return FALSE; return TRUE; @@ -3723,6 +4000,8 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) client_hilite(self, demands_attention); client_change_state(self); /* change the hint to reflect these changes */ + + focus_cycle_addremove(self, TRUE); } ObClient *client_focus_target(ObClient *self) @@ -3750,6 +4029,8 @@ gboolean client_can_focus(ObClient *self) gboolean client_focus(ObClient *self) { + if (!client_validate(self)) return FALSE; + /* we might not focus this window, so if we have modal children which would be focused instead, bring them to this desktop */ client_bring_modal_windows(self); @@ -3763,9 +4044,12 @@ gboolean client_focus(ObClient *self) return FALSE; } + /* if we have helper windows they should be there with the window */ + client_bring_helper_windows(self); + ob_debug_type(OB_DEBUG_FOCUS, "Focusing client \"%s\" (0x%x) at time %u", - self->title, self->window, event_curtime); + self->title, self->window, event_time()); /* if using focus_delay, stop the timer now so that focus doesn't go moving on us */ @@ -3777,7 +4061,7 @@ gboolean client_focus(ObClient *self) /* This can cause a BadMatch error with CurrentTime, or if an app passed in a bad time for _NET_WM_ACTIVE_WINDOW. */ XSetInputFocus(obt_display, self->window, RevertToPointerRoot, - event_curtime); + event_time()); } if (self->focus_notify) { @@ -3788,7 +4072,7 @@ gboolean client_focus(ObClient *self) ce.xclient.window = self->window; ce.xclient.format = 32; ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS); - ce.xclient.data.l[1] = event_curtime; + ce.xclient.data.l[1] = event_time(); ce.xclient.data.l[2] = 0l; ce.xclient.data.l[3] = 0l; ce.xclient.data.l[4] = 0l; @@ -3833,13 +4117,10 @@ void client_activate(ObClient *self, gboolean desktop, gboolean here, gboolean raise, gboolean unshade, gboolean user) { - if ((user && (desktop || - self->desktop == DESKTOP_ALL || - self->desktop == screen_desktop)) || - client_can_steal_focus(self, event_curtime, CurrentTime)) - { + self = client_focus_target(self); + + if (client_can_steal_focus(self, desktop, user, event_time(), CurrentTime)) client_present(self, here, raise, unshade); - } else client_hilite(self, TRUE); } @@ -3858,7 +4139,7 @@ static void client_bring_windows_recursive(ObClient *self, if (((helpers && client_helper(self)) || (modals && self->modal)) && - ((self->desktop != desktop && self->desktop != DESKTOP_ALL) || + (!screen_compare_desktops(self->desktop, desktop) || (iconic && self->iconic))) { if (iconic && self->iconic) @@ -4168,7 +4449,7 @@ void client_find_edge_directional(ObClient *self, ObDirection dir, Rect *area = screen_area(self->desktop, i, NULL); detect_edge(*area, dir, my_head, my_size, my_edge_start, my_edge_size, dest, near_edge); - g_free(area); + g_slice_free(Rect, area); } /* search for edges of clients */ @@ -4192,7 +4473,8 @@ void client_find_edge_directional(ObClient *self, ObDirection dir, dock_get_area(&dock_area); detect_edge(dock_area, dir, my_head, my_size, my_edge_start, my_edge_size, dest, near_edge); - g_free(a); + + g_slice_free(Rect, a); } void client_find_move_directional(ObClient *self, ObDirection dir, @@ -4377,6 +4659,13 @@ gboolean client_has_group_siblings(ObClient *self) return self->group && self->group->members->next; } +gboolean client_has_relative(ObClient *self) +{ + return client_has_parent(self) || + client_has_group_siblings(self) || + client_has_children(self); +} + /*! Returns TRUE if the client is running on the same machine as Openbox */ gboolean client_on_localhost(ObClient *self) {