X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=22eb840379787e0de81a0abdd12634d29c7cbe0e;hb=6dfc3c726a2164ba70cfac3df436ee035822bdb1;hp=e58cc753733078743560748dcc0c7399e545aa73;hpb=52b63adfe1120b2e6bc12df4b9f867e9e7c6ab31;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index e58cc753..22eb8403 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -1,5 +1,5 @@ /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- - + client.c for the Openbox window manager Copyright (c) 2006 Mikael Magnusson Copyright (c) 2003-2007 Dana Jansens @@ -32,7 +32,6 @@ #include "event.h" #include "grab.h" #include "focus.h" -#include "propwin.h" #include "stacking.h" #include "openbox.h" #include "group.h" @@ -141,7 +140,7 @@ void client_remove_destroy_notify(ObClientCallback func) } } -void client_set_list() +void client_set_list(void) { Window *windows, *win_it; GList *it; @@ -165,7 +164,7 @@ void client_set_list() stacking_set_list(); } -void client_manage_all() +void client_manage_all(void) { guint i, j, nchild; Window w, *children; @@ -213,8 +212,9 @@ void client_manage(Window window) XWMHints *wmhint; gboolean activate = FALSE; ObAppSettings *settings; - gint placex, placey, placew, placeh; gboolean transient = FALSE; + Rect place, *monitor; + Time launch_time, map_time; grab_server(TRUE); @@ -237,7 +237,7 @@ void client_manage(Window window) grab_server(FALSE); return; /* don't manage it */ } - + /* is the window a docking app */ if ((wmhint = XGetWMHints(ob_display, window))) { if ((wmhint->flags & StateHint) && @@ -253,6 +253,8 @@ void client_manage(Window window) ob_debug("Managing window: 0x%lx\n", window); + map_time = event_get_server_time(); + /* choose the events we want to receive on the CLIENT window */ attrib_set.event_mask = CLIENT_EVENTMASK; attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK; @@ -269,11 +271,13 @@ void client_manage(Window window) self->wmstate = WithdrawnState; /* make sure it gets updated first time */ self->gravity = NorthWestGravity; self->desktop = screen_num_desktops; /* always an invalid value */ - self->user_time = focus_client ? focus_client->user_time : CurrentTime; /* get all the stuff off the window */ client_get_all(self, TRUE); + ob_debug("Window type: %d\n", self->type); + ob_debug("Window group: 0x%x\n", self->group?self->group->leader:0); + /* specify that if we exit, the window should not be destroyed and should be reparented back to root automatically */ XChangeSaveSet(ob_display, window, SetModeInsert); @@ -297,10 +301,8 @@ void client_manage(Window window) /* now we have all of the window's information so we can set this up */ client_setup_decor_and_functions(self, FALSE); - { - Time t = sn_app_started(self->startup_id, self->class); - if (t) self->user_time = t; - } + /* tell startup notification that this app started */ + launch_time = sn_app_started(self->startup_id, self->class); /* do this after we have a frame.. it uses the frame to help determine the WM_STATE to apply. */ @@ -320,7 +322,7 @@ void client_manage(Window window) client_search_focus_tree_full(self)) && /* this checks for focus=false for the window */ (!settings || settings->focus != 0) && - focus_valid_target(self, FALSE, TRUE, FALSE, FALSE)) + focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE)) { activate = TRUE; } @@ -334,10 +336,8 @@ void client_manage(Window window) frame_adjust_client_area(self->frame); /* where the frame was placed is where the window was originally */ - placex = self->area.x; - placey = self->area.y; - placew = self->area.width; - placeh = self->area.height; + 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) { @@ -347,7 +347,7 @@ void client_manage(Window window) (self->positioned == USPosition ? "user specified" : (self->positioned == (PPosition | USPosition) ? "program + user specified" : - "BADNESS !?")))), placex, placey); + "BADNESS !?")))), place.x, place.y); ob_debug("Sized: %s @ %d %d\n", (!self->sized ? "no" : @@ -355,14 +355,15 @@ void client_manage(Window window) (self->sized == USSize ? "user specified" : (self->sized == (PSize | USSize) ? "program + user specified" : - "BADNESS !?")))), placew, placeh); + "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, &placex, &placey, settings); + transient = place_client(self, &place.x, &place.y, settings); /* make sure the window is visible. */ - client_find_onscreen(self, &placex, &placey, placew, placeh, + client_find_onscreen(self, &place.x, &place.y, + place.width, place.height, /* non-normal clients has less rules, and windows that are being restored from a session do also. we can assume you want @@ -379,9 +380,16 @@ void client_manage(Window window) */ ob_state() == OB_STATE_RUNNING && (transient || - (!(self->positioned & USPosition) && + (!((self->positioned & USPosition) || + (settings && settings->pos_given)) && client_normal(self) && - !self->session))); + !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))))); } /* if the window isn't user-sized, then make it fit inside @@ -397,34 +405,34 @@ void client_manage(Window window) (transient || (!(self->sized & USSize || self->positioned & USPosition) && client_normal(self) && - !self->session))) + !self->session && + /* 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))))) { - Rect placer; + Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place); - RECT_SET(placer, placex, placey, placew, placeh); - frame_rect_to_frame(self->frame, &placer); + /* get the size of the frame */ + place.width += self->frame->size.left + self->frame->size.right; + place.height += self->frame->size.top + self->frame->size.bottom; - Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &placer); + /* fit the window inside the area */ + place.width = MIN(place.width, a->width); + place.height = MIN(place.height, a->height); - /* shrink by the frame's area */ - a->width -= self->frame->size.left + self->frame->size.right; - a->height -= self->frame->size.top + self->frame->size.bottom; + ob_debug("setting window size to %dx%d\n", place.width, place.height); - /* fit the window inside the area */ - if (placew > a->width || self->area.height > a->height) { - placew = MIN(self->area.width, a->width); - placeh = MIN(self->area.height, a->height); + /* get the size of the client back */ + place.width -= self->frame->size.left + self->frame->size.right; + place.height -= self->frame->size.top + self->frame->size.bottom; - ob_debug("setting window size to %dx%d\n", - self->area.width, self->area.height); - } g_free(a); } - ob_debug("placing window 0x%x at %d, %d with size %d x %d. " "some restrictions may apply\n", - self->window, placex, placey, placew, placeh); + self->window, place.x, place.y, place.width, place.height); if (self->session) ob_debug(" but session requested %d, %d %d x %d instead, " "overriding\n", @@ -436,25 +444,42 @@ void client_manage(Window window) this also places the window */ - client_apply_startup_state(self, placex, placey, placew, placeh); + 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\n", + activate ? "yes" : "no"); if (activate) { - guint32 last_time = focus_client ? - focus_client->user_time : CurrentTime; + gboolean raise = FALSE; /* This is focus stealing prevention */ ob_debug_type(OB_DEBUG_FOCUS, - "Want to focus new window 0x%x with time %u " - "(last time %u)\n", - self->window, self->user_time, last_time); + "Want to focus new window 0x%x at time %u " + "launched at %u (last user interaction time %u)\n", + self->window, map_time, launch_time, + event_last_user_time); + + if (menu_frame_visible || moveresize_in_progress) { + activate = FALSE; + raise = TRUE; + ob_debug_type(OB_DEBUG_FOCUS, + "Not focusing the window because the user is inside " + "an Openbox menu or is move/resizing a window and " + "we don't want to interrupt them\n"); + } /* if it's on another desktop */ - if (!(self->desktop == screen_desktop || self->desktop == DESKTOP_ALL) - && /* the timestamp is from before you changed desktops */ - self->user_time && screen_desktop_user_time && - !event_time_after(self->user_time, screen_desktop_user_time)) + else 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)) { activate = FALSE; + raise = TRUE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because its on another " "desktop\n"); @@ -463,26 +488,25 @@ void client_manage(Window window) else if (focus_client && client_search_focus_tree_full(self) == NULL && client_search_focus_group_full(self) == NULL) { - /* If time stamp is old, don't steal focus */ - if (self->user_time && last_time && - !event_time_after(self->user_time, last_time)) + /* If the user is working in another window right now, then don't + steal focus */ + if (event_last_user_time && launch_time && + event_time_after(event_last_user_time, launch_time) && + event_last_user_time != launch_time && + event_time_after(event_last_user_time, + map_time - OB_EVENT_USER_TIME_DELAY)) { activate = FALSE; ob_debug_type(OB_DEBUG_FOCUS, - "Not focusing the window because the time is " - "too old\n"); + "Not focusing the window because the user is " + "working in another window\n"); } - /* If its a transient (and parents aren't focused) and the time - is ambiguous (either the current focus target doesn't have - a timestamp, or they are the same (we probably inherited it - from them) */ - else if (client_has_parent(self) && - (!last_time || self->user_time == last_time)) - { + /* If its a transient (and its parents aren't focused) */ + else if (client_has_parent(self)) { activate = FALSE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because it is a " - "transient, and the time is very ambiguous\n"); + "transient, and its relatives aren't focused\n"); } /* Don't steal focus from globally active clients. I stole this idea from KWin. It seems nice. @@ -499,20 +523,33 @@ void client_manage(Window window) anyway */ else if (client_focus_target(self) != self) { activate = FALSE; + raise = TRUE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because another window " "would get the focus anyway\n"); } + else if (!(self->desktop == screen_desktop || + self->desktop == DESKTOP_ALL)) + { + activate = FALSE; + raise = TRUE; + ob_debug_type(OB_DEBUG_FOCUS, + "Not focusing the window because it is on " + "another desktop and no relatives are focused "); + } } if (!activate) { ob_debug_type(OB_DEBUG_FOCUS, - "Focus stealing prevention activated for %s with " - "time %u (last time %u)\n", - self->title, self->user_time, last_time); + "Focus stealing prevention activated for %s at " + "time %u (last user interactioon time %u)\n", + self->title, map_time, event_last_user_time); /* if the client isn't focused, then hilite it so the user knows it is there */ client_hilite(self, TRUE); + /* we may want to raise it even tho we're not activating it */ + if (raise && !client_restore_session_stacking(self)) + stacking_raise(CLIENT_AS_WINDOW(self)); } } else { @@ -593,7 +630,7 @@ ObClient *client_fake_manage(Window window) frame_adjust_area(self->frame, FALSE, TRUE, TRUE); ob_debug("gave extents left %d right %d top %d bottom %d\n", - self->frame->size.left, self->frame->size.right, + self->frame->size.left, self->frame->size.right, self->frame->size.top, self->frame->size.bottom); /* free the ObAppSettings shallow copy */ @@ -602,7 +639,7 @@ ObClient *client_fake_manage(Window window) return self; } -void client_unmanage_all() +void client_unmanage_all(void) { while (client_list != NULL) client_unmanage(client_list->data); @@ -640,9 +677,6 @@ void client_unmanage(ObClient *self) /* remove the window from our save set */ XChangeSaveSet(ob_display, self->window, SetModeDelete); - /* kill the property windows */ - propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self); - /* update the focus lists */ focus_order_remove(self); if (client_focused(self)) { @@ -980,10 +1014,15 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, for (i = 0; i < screen_num_monitors; ++i) { Rect *a; - if (!screen_physical_area_monitor_contains(i, &desired)) - continue; + if (!screen_physical_area_monitor_contains(i, &desired)) { + if (i < screen_num_monitors - 1) + continue; - a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired); + /* the window is not inside any monitor! so just use the first + one */ + a = screen_area(self->desktop, 0, NULL); + } else + a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired); /* This makes sure windows aren't entirely outside of the screen so you can't see them at all. @@ -1000,7 +1039,7 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, if (!self->strut.left && *x + fw*9/10 - 1 < a->x) *x = a->x - fw*9/10; if (!self->strut.top && *y + fh*9/10 - 1 < a->y) - *y = a->y - fw*9/10; + *y = a->y - fh*9/10; } /* This here doesn't let windows even a pixel outside the @@ -1069,7 +1108,7 @@ static void client_get_all(ObClient *self, gboolean real) if (self->type == OB_CLIENT_TYPE_DESKTOP) self->desktop = DESKTOP_ALL; } - + #ifdef SYNC client_update_sync_request_counter(self); #endif @@ -1077,9 +1116,6 @@ static void client_get_all(ObClient *self, gboolean real) client_get_colormap(self); client_update_strut(self); client_update_icons(self); - client_update_user_time_window(self); - if (!self->user_time_window) /* check if this would have been called */ - client_update_user_time(self); client_update_icon_geometry(self); } @@ -1095,7 +1131,7 @@ static void client_get_area(ObClient *self) { XWindowAttributes wattrib; Status ret; - + ret = XGetWindowAttributes(ob_display, self->window, &wattrib); g_assert(ret != BadWindow); @@ -1116,7 +1152,7 @@ static void client_get_desktop(ObClient *self) self->desktop = screen_num_desktops - 1; else self->desktop = d; - ob_debug("client requested desktop 0x%x\n", self->desktop); + ob_debug("client requested desktop 0x%x\n", self->desktop); } else { GSList *it; gboolean first = TRUE; @@ -1163,7 +1199,7 @@ static void client_get_state(ObClient *self) { guint32 *state; guint num; - + if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) { gulong i; for (i = 0; i < num; ++i) { @@ -1246,7 +1282,7 @@ void client_update_transient_for(ObClient *self) self->transient_for_group, trangroup, client_direct_parent(self), target); self->transient_for_group = trangroup; - + } static void client_update_transient_tree(ObClient *self, @@ -1278,88 +1314,45 @@ static void client_update_transient_tree(ObClient *self, oldgtran == newgtran && oldparent == newparent) return; - /** Remove the client from the transient tree wherever it has changed **/ + /** Remove the client from the transient tree **/ - /* If the window is becoming a direct transient for a window in its group - then any group transients which were our children and are now becoming - our parents need to stop being our children. - - Group transients can't be children of group transients already, but - we could have any number of direct parents above up, any of which could - be transient for the group, and we need to remove it from our children. - */ - if (!oldgtran && oldparent != newparent && newparent != NULL && - newgroup != NULL && newgroup == oldgroup && - client_normal(newparent)) - { - ObClient *look = client_search_top_direct_parent(newparent); - self->transients = g_slist_remove(self->transients, look); - look->parents = g_slist_remove(look->parents, self); + for (it = self->transients; it; it = next) { + next = g_slist_next(it); + c = it->data; + self->transients = g_slist_delete_link(self->transients, it); + c->parents = g_slist_remove(c->parents, self); } - - - /* If the group changed, or if we are just becoming transient for the - group, then we need to remove any old group transient windows - from our children. But if we were already transient for the group, then - other group transients are not our children. */ - if ((oldgroup != newgroup || (newgtran && oldgtran != newgtran)) && - oldgroup != NULL && !oldgtran) - { - for (it = self->transients; it; it = next) { - next = g_slist_next(it); - c = it->data; - if (c->group == oldgroup && client_normal(self)) { - self->transients = g_slist_delete_link(self->transients, it); - c->parents = g_slist_remove(c->parents, self); - } - } + for (it = self->parents; it; it = next) { + next = g_slist_next(it); + c = it->data; + self->parents = g_slist_delete_link(self->parents, it); + c->transients = g_slist_remove(c->transients, self); } - /* If we used to be transient for a group and now we are not, or we're - transient for a new group, then we need to remove ourselves from all - our ex-parents */ - if (oldgtran && (oldgroup != newgroup || oldgtran != newgtran)) - { - for (it = self->parents; it; it = next) { - next = g_slist_next(it); - c = it->data; - if (!c->transient_for_group && client_normal(c)) { - c->transients = g_slist_remove(c->transients, self); - self->parents = g_slist_delete_link(self->parents, it); - } - } - } - /* If we used to be transient for a single window and we are no longer - transient for it, then we need to remove ourself from its children */ - else if (oldparent && oldparent != newparent && - client_normal(oldparent)) - { - oldparent->transients = g_slist_remove(oldparent->transients, self); - self->parents = g_slist_remove(self->parents, oldparent); - } + /** Re-add the client to the transient tree **/ - /** Re-add the client to the transient tree wherever it has changed **/ - - /* If we're now transient for a group and we weren't transient for it - before then we need to add ourselves to all our new parents */ - if (newgtran && (oldgroup != newgroup || oldgtran != newgtran)) - { - for (it = oldgroup->members; it; it = g_slist_next(it)) { + /* If we're transient for a group then we need to add ourselves to all our + parents */ + if (newgtran) { + for (it = newgroup->members; it; it = g_slist_next(it)) { c = it->data; - if (c != self && !c->transient_for_group && client_normal(c)) + if (c != self && + !client_search_top_direct_parent(c)->transient_for_group && + client_normal(c)) { c->transients = g_slist_prepend(c->transients, self); self->parents = g_slist_prepend(self->parents, c); } } } - /* If we are now transient for a single window which we weren't before, - we need to add ourselves to its children + + /* If we are now transient for a single window we need to add ourselves to + its children WARNING: Cyclical transient ness is possible if two windows are transient for eachother. */ - else if (newparent && newparent != oldparent && + else if (newparent && /* don't make ourself its child if it is already our child */ !client_is_direct_child(self, newparent) && client_normal(newparent)) @@ -1368,9 +1361,8 @@ static void client_update_transient_tree(ObClient *self, self->parents = g_slist_prepend(self->parents, newparent); } - /* If the group changed then we need to add any new group transient - windows to our children. But if we're transient for the group, then - other group transients are not our children. + /* Add any group transient windows to our children. But if we're transient + for the group, then other group transients are not our children. WARNING: Cyclical transient-ness is possible. For e.g. if: A is transient for the group @@ -1378,7 +1370,9 @@ static void client_update_transient_tree(ObClient *self, C is transient for B A can't be transient for C or we have a cycle */ - if (oldgroup != newgroup && newgroup != NULL && !newgtran && + if (!newgtran && newgroup && + (!newparent || + !client_search_top_direct_parent(newparent)->transient_for_group) && client_normal(self)) { for (it = newgroup->members; it; it = g_slist_next(it)) { @@ -1392,6 +1386,20 @@ static void client_update_transient_tree(ObClient *self, } } } + + /** If we change our group transient-ness, our children change their + effect group transient-ness, which affects how they relate to other + group windows **/ + + for (it = self->transients; it; it = g_slist_next(it)) { + c = it->data; + if (!c->transient_for_group) + client_update_transient_tree(c, c->group, c->group, + c->transient_for_group, + c->transient_for_group, + client_direct_parent(c), + client_direct_parent(c)); + } } static void client_get_mwm_hints(ObClient *self) @@ -1420,7 +1428,7 @@ void client_get_type_and_transientness(ObClient *self) self->type = -1; self->transient = FALSE; - + if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) { /* use the first value that we know about in the array */ for (i = 0; i < num; ++i) { @@ -1456,7 +1464,7 @@ void client_get_type_and_transientness(ObClient *self) if (XGetTransientForHint(ob_display, self->window, &t)) self->transient = TRUE; - + if (self->type == (ObClientType) -1) { /*the window type hint was not set, which means we either classify ourself as a normal window or a dialog, depending on if we are a @@ -1495,7 +1503,7 @@ void client_update_protocols(ObClient *self) notified whenever we want it to receive focus */ self->focus_notify = TRUE; #ifdef SYNC - else if (proto[i] == prop_atoms.net_wm_sync_request) + else if (proto[i] == prop_atoms.net_wm_sync_request) /* if this protocol is requested, then resizing the window will be synchronized between the frame and the client */ @@ -1575,13 +1583,13 @@ void client_update_normal_hints(ObClient *self) if (size.flags & PMinSize) SIZE_SET(self->min_size, size.min_width, size.min_height); - + if (size.flags & PMaxSize) SIZE_SET(self->max_size, size.max_width, size.max_height); - + if (size.flags & PBaseSize) SIZE_SET(self->base_size, size.base_width, size.base_height); - + if (size.flags & PResizeInc && size.width_inc && size.height_inc) SIZE_SET(self->size_inc, size.width_inc, size.height_inc); @@ -1628,11 +1636,16 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) switch (self->type) { case OB_CLIENT_TYPE_NORMAL: /* normal windows retain all of the possible decorations and - functionality, and are the only windows that you can fullscreen */ + functionality, and can be fullscreen */ self->functions |= OB_CLIENT_FUNC_FULLSCREEN; break; case OB_CLIENT_TYPE_DIALOG: + /* sometimes apps make dialog windows fullscreen for some reason (for + e.g. kpdf does this..) */ + self->functions |= OB_CLIENT_FUNC_FULLSCREEN; + break; + case OB_CLIENT_TYPE_UTILITY: /* these windows don't have anything added or removed by default */ break; @@ -1815,7 +1828,7 @@ void client_update_wmhints(ObClient *self) /* assume a window takes input if it doesnt specify */ self->can_focus = TRUE; - + if ((hints = XGetWMHints(ob_display, self->window)) != NULL) { gboolean ur; @@ -1902,17 +1915,17 @@ void client_update_title(ObClient *self) gchar *visible = NULL; g_free(self->title); - + /* try netwm */ if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) { /* try old x stuff */ if (!(PROP_GETS(self->window, wm_name, locale, &data) || PROP_GETS(self->window, wm_name, utf8, &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"); @@ -2030,7 +2043,7 @@ void client_update_icons(ObClient *self) } self->icons = g_new(ObClientIcon, self->nicons); - + /* store the icons */ i = 0; for (j = 0; j < self->nicons; ++j) { @@ -2087,7 +2100,7 @@ void client_update_icons(ObClient *self) or removes it entirely, it's not very likely it is going to set one right away afterwards - if it has parents, then one of them will have an icon already + if it has parents, then one of them will have an icon already */ if (self->nicons == 0 && !self->parents) { RrPixel32 *icon = ob_rr_theme->def_win_icon; @@ -2108,62 +2121,6 @@ void client_update_icons(ObClient *self) frame_adjust_icon(self->frame); } -void client_update_user_time(ObClient *self) -{ - guint32 time; - gboolean got = FALSE; - - if (self->user_time_window) - got = PROP_GET32(self->user_time_window, - net_wm_user_time, cardinal, &time); - if (!got) - got = PROP_GET32(self->window, net_wm_user_time, cardinal, &time); - - if (got) { - /* we set this every time, not just when it grows, because in practice - sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes - backward we don't want all windows to stop focusing. we'll just - assume noone is setting times older than the last one, cuz that - would be pretty stupid anyways - */ - self->user_time = time; - - /*ob_debug("window %s user time %u\n", self->title, time);*/ - } -} - -void client_update_user_time_window(ObClient *self) -{ - guint32 w; - - if (!PROP_GET32(self->window, net_wm_user_time_window, window, &w)) - w = None; - - if (w != self->user_time_window) { - /* remove the old window */ - propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self); - self->user_time_window = None; - - if (self->group && self->group->leader == w) { - ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its " - "_NET_WM_USER_TYPE_WINDOW to its group leader\n"); - /* do it anyways..? */ - } - else if (w == self->window) { - ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its " - "_NET_WM_USER_TIME_WINDOW to itself\n"); - w = None; /* don't do it */ - } - - /* add the new window */ - propwin_add(w, OB_PROPWIN_USER_TIME, self); - self->user_time_window = w; - - /* and update from it */ - client_update_user_time(self); - } -} - void client_update_icon_geometry(ObClient *self) { guint num; @@ -2461,7 +2418,7 @@ gboolean client_should_show(ObClient *self) return FALSE; if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL) return TRUE; - + return FALSE; } @@ -2605,7 +2562,7 @@ static void client_apply_startup_state(ObClient *self, client_shade(self, TRUE); if (demands_attention) client_hilite(self, TRUE); - + if (max_vert && max_horz) client_maximize(self, TRUE, 0); else if (max_vert) @@ -2807,7 +2764,7 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, /* you cannot resize to nothing */ if (basew + *w < 1) *w = 1 - basew; if (baseh + *h < 1) *h = 1 - baseh; - + /* save the logical size */ *logicalw = incw > 1 ? *w : *w + basew; *logicalh = inch > 1 ? *h : *h + baseh; @@ -3122,7 +3079,7 @@ void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk, void client_maximize(ObClient *self, gboolean max, gint dir) { gint x, y, w, h; - + g_assert(dir == 0 || dir == 1 || dir == 2); if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */ @@ -3210,7 +3167,7 @@ void client_close(ObClient *self) close, we just kill it */ if (!self->delete_window) client_kill(self); - + /* XXX: itd be cool to do timeouts and shit here for killing the client's process off @@ -3312,7 +3269,7 @@ ObClient *client_search_modal_child(ObClient *self) { GSList *it; ObClient *ret; - + for (it = self->transients; it; it = g_slist_next(it)) { ObClient *c = it->data; if ((ret = client_search_modal_child(c))) return ret; @@ -3323,7 +3280,7 @@ ObClient *client_search_modal_child(ObClient *self) gboolean client_validate(ObClient *self) { - XEvent e; + XEvent e; XSync(ob_display, FALSE); /* get all events on the server */ @@ -3339,7 +3296,7 @@ gboolean client_validate(ObClient *self) void client_set_wm_state(ObClient *self, glong state) { if (state == self->wmstate) return; /* no change */ - + switch (state) { case IconicState: client_iconify(self, TRUE, TRUE, FALSE); @@ -3368,11 +3325,11 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) action == prop_atoms.net_wm_state_remove || action == prop_atoms.net_wm_state_toggle)) /* an invalid action was passed to the client message, ignore it */ - return; + return; for (i = 0; i < 2; ++i) { Atom state = i == 0 ? data1 : data2; - + if (!state) continue; /* if toggling, then pick whether we're adding or removing */ @@ -3419,7 +3376,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) action = undecorated ? prop_atoms.net_wm_state_remove : prop_atoms.net_wm_state_add; } - + if (action == prop_atoms.net_wm_state_add) { if (state == prop_atoms.net_wm_state_modal) { modal = TRUE; @@ -3645,34 +3602,7 @@ static void client_present(ObClient *self, gboolean here, gboolean raise, void client_activate(ObClient *self, gboolean here, gboolean raise, gboolean unshade, gboolean user) { - guint32 last_time = focus_client ? focus_client->user_time : CurrentTime; - gboolean allow = FALSE; - - /* if the currently focused app doesn't set a user_time, then it can't - benefit from any focus stealing prevention. - - if the timestamp is missing in the request then let it go through - even if it is source=app, because EVERY APPLICATION DOES THIS because - GTK IS VERY BUGGY AND HARDCODES source=application... WHY!? - */ - if (!last_time || !event_curtime) - allow = TRUE; - /* otherwise, if they didn't give a time stamp or if it is too old, they - don't get focus */ - else - allow = event_time_after(event_curtime, last_time); - - ob_debug_type(OB_DEBUG_FOCUS, - "Want to activate window 0x%x with time %u (last time %u), " - "source=%s allowing? %d\n", - self->window, event_curtime, last_time, - (user ? "user" : "application"), allow); - - if (allow) - client_present(self, here, raise, unshade); - else - /* don't focus it but tell the user it wants attention */ - client_hilite(self, TRUE); + client_present(self, here, raise, unshade); } static void client_bring_windows_recursive(ObClient *self, @@ -3728,7 +3658,7 @@ static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h) if ((parent = client_icon_recursive(c, w, h))) break; } - + return parent; } @@ -3782,7 +3712,7 @@ void client_set_layer(ObClient *self, gint layer) void client_set_undecorated(ObClient *self, gboolean undecorated) { if (self->undecorated != undecorated && - /* don't let it undecorate if the function is missing, but let + /* don't let it undecorate if the function is missing, but let it redecorate */ (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated)) { @@ -3802,7 +3732,7 @@ ObClient *client_direct_parent(ObClient *self) if (!self->parents) return NULL; if (self->transient_for_group) return NULL; return self->parents->data; -} +} ObClient *client_search_top_direct_parent(ObClient *self) { @@ -3817,7 +3747,7 @@ static GSList *client_search_all_top_parents_internal(ObClient *self, { GSList *ret; ObClient *p; - + /* move up the direct transient chain as far as possible */ while ((p = client_direct_parent(self)) && (!bylayer || p->layer == layer)) @@ -3874,191 +3804,331 @@ ObClient *client_search_transient(ObClient *self, ObClient *search) return NULL; } -#define WANT_EDGE(cur, c) \ - if (cur == c) \ - continue; \ - if (c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL && \ - cur->desktop != screen_desktop) \ - continue; \ - if (cur->iconic) \ - continue; +static void detect_edge(Rect area, ObDirection dir, + gint my_head, gint my_size, + gint my_edge_start, gint my_edge_size, + gint *dest, gboolean *near_edge) +{ + gint edge_start, edge_size, head, tail; + gboolean skip_head = FALSE, skip_tail = FALSE; + + switch (dir) { + case OB_DIRECTION_NORTH: + case OB_DIRECTION_SOUTH: + edge_start = area.x; + edge_size = area.width; + break; + case OB_DIRECTION_EAST: + case OB_DIRECTION_WEST: + edge_start = area.y; + edge_size = area.height; + break; + default: + g_assert_not_reached(); + } + + /* do we collide with this window? */ + if (!RANGES_INTERSECT(my_edge_start, my_edge_size, + edge_start, edge_size)) + return; + + switch (dir) { + case OB_DIRECTION_NORTH: + head = RECT_BOTTOM(area); + tail = RECT_TOP(area); + break; + case OB_DIRECTION_SOUTH: + head = RECT_TOP(area); + tail = RECT_BOTTOM(area); + break; + case OB_DIRECTION_WEST: + head = RECT_RIGHT(area); + tail = RECT_LEFT(area); + break; + case OB_DIRECTION_EAST: + head = RECT_LEFT(area); + tail = RECT_RIGHT(area); + break; + default: + g_assert_not_reached(); + } + switch (dir) { + case OB_DIRECTION_NORTH: + case OB_DIRECTION_WEST: + /* check if our window is past the head of this window */ + if (my_head <= head + 1) + skip_head = TRUE; + /* check if our window's tail is past the tail of this window */ + if (my_head + my_size - 1 <= tail) + skip_tail = TRUE; + /* check if the head of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a tail, not a head) */ + if (head + (*near_edge ? 0 : my_size) < *dest) + skip_head = TRUE; + /* check if the tail of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a head, not a tail) */ + if (tail - (!*near_edge ? 0 : my_size) < *dest) + skip_tail = TRUE; + break; + case OB_DIRECTION_SOUTH: + case OB_DIRECTION_EAST: + /* check if our window is past the head of this window */ + if (my_head >= head - 1) + skip_head = TRUE; + /* check if our window's tail is past the tail of this window */ + if (my_head - my_size + 1 >= tail) + skip_tail = TRUE; + /* check if the head of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a tail, not a head) */ + if (head - (*near_edge ? 0 : my_size) > *dest) + skip_head = TRUE; + /* check if the tail of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a head, not a tail) */ + if (tail + (!*near_edge ? 0 : my_size) > *dest) + skip_tail = TRUE; + break; + default: + g_assert_not_reached(); + } -#define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \ - if ((his_edge_start >= my_edge_start && \ - his_edge_start <= my_edge_end) || \ - (my_edge_start >= his_edge_start && \ - my_edge_start <= his_edge_end)) \ - dest = his_offset; - -/* finds the nearest edge in the given direction from the current client - * note to self: the edge is the -frame- edge (the actual one), not the - * client edge. - */ -gint client_directional_edge_search(ObClient *c, ObDirection dir, gboolean hang) + ob_debug("my head %d size %d\n", my_head, my_size); + ob_debug("head %d tail %d deest %d\n", head, tail, *dest); + if (!skip_head) { + ob_debug("using near edge %d\n", head); + *dest = head; + *near_edge = TRUE; + } + else if (!skip_tail) { + ob_debug("using far edge %d\n", tail); + *dest = tail; + *near_edge = FALSE; + } +} + +void client_find_edge_directional(ObClient *self, ObDirection dir, + gint my_head, gint my_size, + gint my_edge_start, gint my_edge_size, + gint *dest, gboolean *near_edge) { - gint dest, monitor_dest; - gint my_edge_start, my_edge_end, my_offset; GList *it; Rect *a, *mon; - - if(!client_list) - return -1; + Rect dock_area; + gint edge; - a = screen_area(c->desktop, SCREEN_AREA_ALL_MONITORS, &c->frame->area); - mon = screen_area(c->desktop, SCREEN_AREA_ONE_MONITOR, &c->frame->area); + a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, + &self->frame->area); + mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, + &self->frame->area); - switch(dir) { + switch (dir) { case OB_DIRECTION_NORTH: - my_edge_start = c->frame->area.x; - my_edge_end = c->frame->area.x + c->frame->area.width; - my_offset = c->frame->area.y + (hang ? c->frame->area.height : 0); - - /* default: top of screen */ - dest = a->y + (hang ? c->frame->area.height : 0); - monitor_dest = mon->y + (hang ? c->frame->area.height : 0); - /* if the monitor edge comes before the screen edge, */ - /* use that as the destination instead. (For xinerama) */ - if (monitor_dest != dest && my_offset > monitor_dest) - dest = monitor_dest; - - for(it = client_list; it && my_offset != dest; it = g_list_next(it)) { - gint his_edge_start, his_edge_end, his_offset; - ObClient *cur = it->data; - - WANT_EDGE(cur, c) - - his_edge_start = cur->frame->area.x; - his_edge_end = cur->frame->area.x + cur->frame->area.width; - his_offset = cur->frame->area.y + - (hang ? 0 : cur->frame->area.height); - - if(his_offset + 1 > my_offset) - continue; - - if(his_offset < dest) - continue; - - HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) - } + if (my_head >= RECT_TOP(*mon) + 1) + edge = RECT_TOP(*mon) - 1; + else + edge = RECT_TOP(*a) - 1; break; case OB_DIRECTION_SOUTH: - my_edge_start = c->frame->area.x; - my_edge_end = c->frame->area.x + c->frame->area.width; - my_offset = c->frame->area.y + (hang ? 0 : c->frame->area.height); + if (my_head <= RECT_BOTTOM(*mon) - 1) + edge = RECT_BOTTOM(*mon) + 1; + else + edge = RECT_BOTTOM(*a) + 1; + break; + case OB_DIRECTION_EAST: + if (my_head <= RECT_RIGHT(*mon) - 1) + edge = RECT_RIGHT(*mon) + 1; + else + edge = RECT_RIGHT(*a) + 1; + break; + case OB_DIRECTION_WEST: + if (my_head >= RECT_LEFT(*mon) + 1) + edge = RECT_LEFT(*mon) - 1; + else + edge = RECT_LEFT(*a) - 1; + break; + default: + g_assert_not_reached(); + } + /* default to the far edge, then narrow it down */ + *dest = edge; + *near_edge = TRUE; - /* default: bottom of screen */ - dest = a->y + a->height - (hang ? c->frame->area.height : 0); - monitor_dest = mon->y + mon->height - - (hang ? c->frame->area.height : 0); - /* if the monitor edge comes before the screen edge, */ - /* use that as the destination instead. (For xinerama) */ - if (monitor_dest != dest && my_offset < monitor_dest) - dest = monitor_dest; + for (it = client_list; it; it = g_list_next(it)) { + ObClient *cur = it->data; - for(it = client_list; it && my_offset != dest; it = g_list_next(it)) { - gint his_edge_start, his_edge_end, his_offset; - 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; - WANT_EDGE(cur, c) + ob_debug("trying window %s\n", cur->title); - his_edge_start = cur->frame->area.x; - his_edge_end = cur->frame->area.x + cur->frame->area.width; - his_offset = cur->frame->area.y + - (hang ? cur->frame->area.height : 0); + 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); + g_free(a); + g_free(mon); +} +void client_find_move_directional(ObClient *self, ObDirection dir, + gint *x, gint *y) +{ + gint head, size; + gint e, e_start, e_size; + gboolean near; - if(his_offset - 1 < my_offset) - continue; - - if(his_offset > dest) - continue; + switch (dir) { + case OB_DIRECTION_EAST: + head = RECT_RIGHT(self->frame->area); + size = self->frame->area.width; + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + break; + case OB_DIRECTION_WEST: + head = RECT_LEFT(self->frame->area); + size = self->frame->area.width; + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + break; + case OB_DIRECTION_NORTH: + head = RECT_TOP(self->frame->area); + size = self->frame->area.height; + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + break; + case OB_DIRECTION_SOUTH: + head = RECT_BOTTOM(self->frame->area); + size = self->frame->area.height; + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + break; + default: + g_assert_not_reached(); + } - HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) - } + client_find_edge_directional(self, dir, head, size, + e_start, e_size, &e, &near); + *x = self->frame->area.x; + *y = self->frame->area.y; + switch (dir) { + case OB_DIRECTION_EAST: + if (near) e -= self->frame->area.width; + else e++; + *x = e; break; case OB_DIRECTION_WEST: - my_edge_start = c->frame->area.y; - my_edge_end = c->frame->area.y + c->frame->area.height; - my_offset = c->frame->area.x + (hang ? c->frame->area.width : 0); - - /* default: leftmost egde of screen */ - dest = a->x + (hang ? c->frame->area.width : 0); - monitor_dest = mon->x + (hang ? c->frame->area.width : 0); - /* if the monitor edge comes before the screen edge, */ - /* use that as the destination instead. (For xinerama) */ - if (monitor_dest != dest && my_offset > monitor_dest) - dest = monitor_dest; - - for(it = client_list; it && my_offset != dest; it = g_list_next(it)) { - gint his_edge_start, his_edge_end, his_offset; - ObClient *cur = it->data; - - WANT_EDGE(cur, c) - - his_edge_start = cur->frame->area.y; - his_edge_end = cur->frame->area.y + cur->frame->area.height; - his_offset = cur->frame->area.x + - (hang ? 0 : cur->frame->area.width); - - if(his_offset + 1 > my_offset) - continue; + if (near) e++; + else e -= self->frame->area.width; + *x = e; + break; + case OB_DIRECTION_NORTH: + if (near) e++; + else e -= self->frame->area.height; + *y = e; + break; + case OB_DIRECTION_SOUTH: + if (near) e -= self->frame->area.height; + else e++; + *y = e; + break; + default: + g_assert_not_reached(); + } + frame_frame_gravity(self->frame, x, y); +} - if(his_offset < dest) - continue; +void client_find_resize_directional(ObClient *self, ObDirection side, + gboolean grow, + gint *x, gint *y, gint *w, gint *h) +{ + gint head; + gint e, e_start, e_size, delta; + gboolean near; + ObDirection dir; - HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) - } - break; + switch (side) { case OB_DIRECTION_EAST: - my_edge_start = c->frame->area.y; - my_edge_end = c->frame->area.y + c->frame->area.height; - my_offset = c->frame->area.x + (hang ? 0 : c->frame->area.width); - - /* default: rightmost edge of screen */ - dest = a->x + a->width - (hang ? c->frame->area.width : 0); - monitor_dest = mon->x + mon->width - - (hang ? c->frame->area.width : 0); - /* if the monitor edge comes before the screen edge, */ - /* use that as the destination instead. (For xinerama) */ - if (monitor_dest != dest && my_offset < monitor_dest) - dest = monitor_dest; - - for(it = client_list; it && my_offset != dest; it = g_list_next(it)) { - gint his_edge_start, his_edge_end, his_offset; - ObClient *cur = it->data; - - WANT_EDGE(cur, c) - - his_edge_start = cur->frame->area.y; - his_edge_end = cur->frame->area.y + cur->frame->area.height; - his_offset = cur->frame->area.x + - (hang ? cur->frame->area.width : 0); - - if(his_offset - 1 < my_offset) - continue; - - if(his_offset > dest) - continue; - - HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) - } + head = RECT_RIGHT(self->frame->area) + + (self->size_inc.width - 1) * (grow ? 1 : -1); + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST; + break; + case OB_DIRECTION_WEST: + head = RECT_LEFT(self->frame->area) - + (self->size_inc.width - 1) * (grow ? 1 : -1); + e_start = RECT_TOP(self->frame->area); + e_size = self->frame->area.height; + dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST; + break; + case OB_DIRECTION_NORTH: + head = RECT_TOP(self->frame->area) - + (self->size_inc.height - 1) * (grow ? 1 : -1); + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH; + break; + case OB_DIRECTION_SOUTH: + head = RECT_BOTTOM(self->frame->area) + + (self->size_inc.height - 1) * (grow ? 1 : -1); + e_start = RECT_LEFT(self->frame->area); + e_size = self->frame->area.width; + dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH; break; - case OB_DIRECTION_NORTHEAST: - case OB_DIRECTION_SOUTHEAST: - case OB_DIRECTION_NORTHWEST: - case OB_DIRECTION_SOUTHWEST: - /* not implemented */ default: g_assert_not_reached(); - dest = 0; /* suppress warning */ } - g_free(a); - g_free(mon); - return dest; + ob_debug("head %d dir %d\n", head, dir); + client_find_edge_directional(self, dir, head, 1, + e_start, e_size, &e, &near); + ob_debug("edge %d\n", e); + *x = self->frame->area.x; + *y = self->frame->area.y; + *w = self->frame->area.width; + *h = self->frame->area.height; + switch (side) { + case OB_DIRECTION_EAST: + if (grow == near) --e; + delta = e - RECT_RIGHT(self->frame->area); + *w += delta; + break; + case OB_DIRECTION_WEST: + if (grow == near) ++e; + delta = RECT_LEFT(self->frame->area) - e; + *x -= delta; + *w += delta; + break; + case OB_DIRECTION_NORTH: + if (grow == near) ++e; + delta = RECT_TOP(self->frame->area) - e; + *y -= delta; + *h += delta; + break; + case OB_DIRECTION_SOUTH: + if (grow == near) --e; + delta = e - RECT_BOTTOM(self->frame->area); + *h += delta; + break; + default: + g_assert_not_reached(); + } + frame_frame_gravity(self->frame, x, y); + *w -= self->frame->size.left + self->frame->size.right; + *h -= self->frame->size.top + self->frame->size.bottom; } -ObClient* client_under_pointer() +ObClient* client_under_pointer(void) { gint x, y; GList *it; @@ -4091,30 +4161,3 @@ gboolean client_has_group_siblings(ObClient *self) { return self->group && self->group->members->next; } - -ObClientIcon *client_thumbnail(ObClient *self, gint wantw, gint wanth) -{ - ObClientIcon *ret; - RrPixel32 *data; - gint w, h; - - if (!self->frame->pixmap) return NULL; - if (!RrPixmapToRGBA(ob_rr_inst, self->frame->pixmap, None, &w, &h, &data)) - return NULL; - - /* resize the thumbnail (within aspect ratio) to the given sizes */ - - ret = g_new(ObClientIcon, 1); - ret->data = data; - ret->width = w; - ret->height = h; - return ret; -} - -void clienticon_free(ObClientIcon *ci) -{ - if (ci) { - g_free(ci->data); - g_free(ci); - } -}