X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=012454c724c141ad2c099403966e21a7879ab254;hb=5d5714f01e1a7140847f6e7f2922d457f6bbe66a;hp=184ae171e8d2b50a0fee2f1a64391cd36eb6a81a;hpb=acafa38c8ea210b12ed92fc16281b915ab61542c;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 184ae171..012454c7 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" @@ -38,9 +39,10 @@ #include "menuframe.h" #include "keyboard.h" #include "mouse.h" -#include "render/render.h" +#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,8 +108,9 @@ 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, + Time steal_time, Time launch_time); void client_startup(gboolean reconfig) { @@ -149,7 +152,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 +165,7 @@ 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; @@ -201,14 +204,13 @@ void client_manage(Window window, ObPrompt *prompt) gboolean activate = FALSE; ObAppSettings *settings; gboolean transient = FALSE; - Rect place, *monitor; - Time launch_time, map_time; + Rect place; + Time launch_time; guint32 user_time; + gboolean obplaced; 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 +221,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,18 +237,14 @@ 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); /* 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); - /* specify that if we exit, the window should not be destroyed and should be reparented back to root automatically, unless we are managing an internal ObPrompt window */ @@ -268,7 +267,7 @@ void client_manage(Window window, ObPrompt *prompt) 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,13 +283,14 @@ 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; @@ -306,7 +306,6 @@ 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)); /* figure out placement for the window if the window is new */ if (ob_state() == OB_STATE_RUNNING) { @@ -326,9 +325,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, &place.x, &place.y, 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 +360,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 +395,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 +413,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,13 +433,11 @@ 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; - 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); + activate = client_can_steal_focus(self, settings->focus, + event_time(), launch_time); if (!activate) { /* if the client isn't stealing focus, then hilite it so the user @@ -477,7 +492,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 +507,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 +515,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 +565,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) @@ -667,7 +684,7 @@ void client_unmanage(ObClient *self) g_free(self->role); 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,10 +692,12 @@ 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, + Time steal_time, Time launch_time) { gboolean steal; @@ -695,17 +714,20 @@ static gboolean client_can_steal_focus(ObClient *self, Time steal_time, /* This is focus stealing prevention */ ob_debug_type(OB_DEBUG_FOCUS, - "Want to focus new window 0x%x at time %u " + "Want to focus 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 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)) + /* and (we dont know when it launched, and we don't want to allow + focus stealing from other desktops */ + ((!launch_time && !allow_other_desktop) || + /* or the timestamp is from before you changed desktops) */ + (screen_desktop_user_time && + !event_time_after(launch_time, screen_desktop_user_time)))) { steal = FALSE; ob_debug_type(OB_DEBUG_FOCUS, @@ -790,10 +812,10 @@ 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 || + (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; @@ -805,8 +827,13 @@ static ObAppSettings *client_get_settings_state(ObClient *self) !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); @@ -1052,7 +1079,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,13 +1098,19 @@ 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 decor/functions before getting the state. the states may + affect which functions are available, but we want to know the maximum + decor/functions are available to this window, so we can then apply them + in client_apply_startup_state() */ + client_setup_decor_and_functions(self, FALSE); + + 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 */ if (!real) @@ -1086,6 +1119,9 @@ static void client_get_all(ObClient *self, gboolean real) /* 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); client_update_wmhints(self); @@ -1118,11 +1154,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 +1558,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; } @@ -1683,6 +1724,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) { @@ -1735,17 +1783,13 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) } 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 */ + /* 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 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) */ if (self->undecorated) @@ -1767,8 +1811,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 +1955,8 @@ void client_update_wmhints(ObClient *self) XFree(hints); } + + focus_cycle_addremove(self, TRUE); } void client_update_title(ObClient *self) @@ -1922,15 +1968,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 +1998,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 +2009,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 +2029,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 +2056,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 +2069,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); } @@ -2202,19 +2245,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]) { @@ -2229,11 +2267,7 @@ static void client_get_session_ids(ObClient *self) if (self->class == NULL) self->class = g_strdup(""); /* get the WM_WINDOW_ROLE. make it "" if it is not provided */ - got = FALSE; - 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_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s); if (got) self->role = s; @@ -2244,9 +2278,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 +2303,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 +2325,37 @@ 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_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 +2472,27 @@ gboolean client_has_parent(ObClient *self) return self->parents != 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 +2501,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 +2516,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; } @@ -2658,8 +2724,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 +2738,10 @@ 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); + /* 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 @@ -2776,7 +2844,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 +2857,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 +2879,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 +2890,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 +2930,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 +2949,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 +3005,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 +3025,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 +3037,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 +3090,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 +3130,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 +3140,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 +3169,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 +3193,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 +3214,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 +3251,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 +3263,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 +3361,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 +3426,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 +3604,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 +3621,8 @@ void client_set_desktop(ObClient *self, guint target, { self = client_search_top_direct_parent(self); client_set_desktop_recursive(self, target, donthide, dontraise); + + focus_cycle_addremove(NULL, TRUE); } gboolean client_is_direct_child(ObClient *parent, ObClient *child) @@ -3531,36 +3644,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 +struct ObClientFindDestroyUnmap { + Window window; + gint ignore_unmaps; +}; - /* put them back on the event stack so they end up in the same order */ - XPutBackEvent(obt_display, &e); - } - - 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 +3831,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 +3860,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); @@ -3765,7 +3877,7 @@ gboolean client_focus(ObClient *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 +3889,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 +3900,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; @@ -3836,7 +3948,7 @@ void client_activate(ObClient *self, gboolean desktop, if ((user && (desktop || self->desktop == DESKTOP_ALL || self->desktop == screen_desktop)) || - client_can_steal_focus(self, event_curtime, CurrentTime)) + client_can_steal_focus(self, desktop, event_time(), CurrentTime)) { client_present(self, here, raise, unshade); } @@ -4168,31 +4280,38 @@ 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 */ - for (it = client_list; it; it = g_list_next(it)) { - ObClient *cur = it->data; + if (((dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH) && + !self->max_vert) || + ((dir == OB_DIRECTION_EAST || dir == OB_DIRECTION_WEST) && + !self->max_horz)) + { + for (it = client_list; it; it = g_list_next(it)) { + ObClient *cur = it->data; - /* skip windows to not bump into */ - if (cur == self) - continue; - if (cur->iconic) - continue; - if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL && - cur->desktop != screen_desktop) - continue; + /* skip windows to not bump into */ + if (cur == self) + continue; + if (cur->iconic) + continue; + if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL && + cur->desktop != screen_desktop) + continue; - ob_debug("trying window %s", cur->title); + ob_debug("trying window %s", cur->title); - detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start, + detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start, + my_edge_size, dest, near_edge); + } + dock_get_area(&dock_area); + detect_edge(dock_area, dir, my_head, my_size, my_edge_start, my_edge_size, dest, near_edge); } - 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,