X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=3c6aee45ac3d3de50af2ee69c4a4e6c42ed6f31f;hb=fc32204f3c1733b289d7f03a15f9f0a568369989;hp=719f46a155a7f08387ba5ba8c2e189c8dc08a4f2;hpb=083a7565c7d7c5cf28d0f91b6c28a893429a7e83;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 719f46a1..f38a0c01 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -64,34 +64,39 @@ typedef struct GList *client_list = NULL; -static GSList *client_destructors = NULL; +extern ObDock *dock; + +static GSList *client_destroy_notifies = NULL; static void client_get_all(ObClient *self, gboolean real); -static void client_toggle_border(ObClient *self, gboolean show); static void client_get_startup_id(ObClient *self); static void client_get_session_ids(ObClient *self); static void client_get_area(ObClient *self); static void client_get_desktop(ObClient *self); static void client_get_state(ObClient *self); -static void client_get_layer(ObClient *self); static void client_get_shaped(ObClient *self); static void client_get_mwm_hints(ObClient *self); static void client_get_colormap(ObClient *self); static void client_change_allowed_actions(ObClient *self); static void client_change_state(ObClient *self); static void client_change_wm_state(ObClient *self); -static void client_apply_startup_state(ObClient *self, gint x, gint y); +static void client_apply_startup_state(ObClient *self, + gint x, gint y, gint w, gint h); static void client_restore_session_state(ObClient *self); static gboolean client_restore_session_stacking(ObClient *self); static ObAppSettings *client_get_settings_state(ObClient *self); static void client_update_transient_tree(ObClient *self, ObGroup *oldgroup, ObGroup *newgroup, + gboolean oldgtran, gboolean newgtran, ObClient* oldparent, ObClient *newparent); -static void client_present(ObClient *self, gboolean here, gboolean raise); +static void client_present(ObClient *self, gboolean here, gboolean raise, + gboolean unshade); static GSList *client_search_all_top_parents_internal(ObClient *self, gboolean bylayer, ObStackingLayer layer); +static void client_call_notifies(ObClient *self, GSList *list); + void client_startup(gboolean reconfig) { @@ -105,23 +110,34 @@ void client_shutdown(gboolean reconfig) if (reconfig) return; } -void client_add_destructor(ObClientCallback func, gpointer data) +static void client_call_notifies(ObClient *self, GSList *list) +{ + GSList *it; + + for (it = list; it; it = g_slist_next(it)) { + ClientCallback *d = it->data; + d->func(self, d->data); + } +} + +void client_add_destroy_notify(ObClientCallback func, gpointer data) { ClientCallback *d = g_new(ClientCallback, 1); d->func = func; d->data = data; - client_destructors = g_slist_prepend(client_destructors, d); + client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d); } -void client_remove_destructor(ObClientCallback func) +void client_remove_destroy_notify(ObClientCallback func) { GSList *it; - for (it = client_destructors; it; it = g_slist_next(it)) { + for (it = client_destroy_notifies; it; it = g_slist_next(it)) { ClientCallback *d = it->data; if (d->func == func) { g_free(d); - client_destructors = g_slist_delete_link(client_destructors, it); + client_destroy_notifies = + g_slist_delete_link(client_destroy_notifies, it); break; } } @@ -151,37 +167,6 @@ void client_set_list() stacking_set_list(); } -/* - void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data) - { - GSList *it; - - for (it = self->transients; it; it = g_slist_next(it)) { - if (!func(it->data, data)) return; - client_foreach_transient(it->data, func, data); - } - } - - void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data) - { - if (self->transient_for) { - if (self->transient_for != OB_TRAN_GROUP) { - if (!func(self->transient_for, data)) return; - client_foreach_ancestor(self->transient_for, func, data); - } else { - GSList *it; - - for (it = self->group->members; it; it = g_slist_next(it)) - if (it->data != self && - !((ObClient*)it->data)->transient_for) { - if (!func(it->data, data)) return; - client_foreach_ancestor(it->data, func, data); - } - } - } - } -*/ - void client_manage_all() { guint i, j, nchild; @@ -230,7 +215,8 @@ void client_manage(Window window) XWMHints *wmhint; gboolean activate = FALSE; ObAppSettings *settings; - gint newx, newy; + gint placex, placey, placew, placeh; + gboolean transient = FALSE; grab_server(TRUE); @@ -267,7 +253,7 @@ void client_manage(Window window) XFree(wmhint); } - ob_debug("Managing window: %lx\n", window); + ob_debug("Managing window: 0x%lx\n", window); /* choose the events we want to receive on the CLIENT window */ attrib_set.event_mask = CLIENT_EVENTMASK; @@ -283,33 +269,16 @@ void client_manage(Window window) /* non-zero defaults */ self->wmstate = WithdrawnState; /* make sure it gets updated first time */ - self->layer = -1; + 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); - /* per-app settings override stuff, and return the settings for other - uses too */ - settings = client_get_settings_state(self); - /* the session should get the last say */ - client_restore_session_state(self); - - client_setup_decor_and_functions(self); - client_calc_layer(self); - - { - Time t = sn_app_started(self->startup_id, self->class); - if (t) self->user_time = t; - } - - /* update the focus lists, do this before the call to change_state or - it can end up in the list twice! */ - focus_order_add_new(self); + ob_debug("Window type: %d\n", self->type); + ob_debug("Window group: 0x%x\n", self->group?self->group->leader:0); - /* remove the client's border (and adjust re gravity) */ - client_toggle_border(self, FALSE); - /* specify that if we exit, the window should not be destroyed and should be reparented back to root automatically */ XChangeSaveSet(ob_display, window, SetModeInsert); @@ -319,46 +288,86 @@ void client_manage(Window window) frame_grab_client(self->frame); + /* we've grabbed everything and set everything that we need to at mapping + time now */ + grab_server(FALSE); + + /* 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); + /* the session should get the last say though */ + client_restore_session_state(self); + + /* 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; + } + /* do this after we have a frame.. it uses the frame to help determine the WM_STATE to apply. */ client_change_state(self); - grab_server(FALSE); + /* add ourselves to the focus order */ + focus_order_add_new(self); - stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); + /* do this to add ourselves to the stacking list in a non-intrusive way */ + client_calc_layer(self); /* focus the new window? */ if (ob_state() != OB_STATE_STARTING && (!self->session || self->session->focused) && - !self->iconic && /* this means focus=true for window is same as config_focus_new=true */ ((config_focus_new || (settings && settings->focus == 1)) || - client_search_focus_parent(self)) && + client_search_focus_tree_full(self)) && /* this checks for focus=false for the window */ (!settings || settings->focus != 0) && - /* note the check against Type_Normal/Dialog, not client_normal(self), - which would also include other types. in this case we want more - strict rules for focus */ - (self->type == OB_CLIENT_TYPE_NORMAL || - self->type == OB_CLIENT_TYPE_DIALOG)) + focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE)) { activate = TRUE; } - /* get the current position */ - newx = self->area.x; - newy = self->area.y; + /* remove the client's border */ + XSetWindowBorderWidth(ob_display, self->window, 0); - /* figure out placement for the window */ - if (ob_state() == OB_STATE_RUNNING) { - gboolean transient; + /* adjust the frame to the client's size before showing or placing + the window */ + frame_adjust_area(self->frame, FALSE, TRUE, FALSE); + frame_adjust_client_area(self->frame); - transient = place_client(self, &newx, &newy, settings); + /* 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; + + /* figure out placement for the window if the window is new */ + if (ob_state() == OB_STATE_RUNNING) { + ob_debug("Positioned: %s @ %d %d\n", + (!self->positioned ? "no" : + (self->positioned == PPosition ? "program specified" : + (self->positioned == USPosition ? "user specified" : + (self->positioned == (PPosition | USPosition) ? + "program + user specified" : + "BADNESS !?")))), placex, placey); + + ob_debug("Sized: %s @ %d %d\n", + (!self->sized ? "no" : + (self->sized == PSize ? "program specified" : + (self->sized == USSize ? "user specified" : + (self->sized == (PSize | USSize) ? + "program + user specified" : + "BADNESS !?")))), placew, placeh); + + /* splash screens are also returned as TRUE for transient, + and so will be forced on screen below */ + transient = place_client(self, &placex, &placey, settings); /* make sure the window is visible. */ - client_find_onscreen(self, &newx, &newy, - self->area.width, - self->area.height, + client_find_onscreen(self, &placex, &placey, placew, placeh, /* non-normal clients has less rules, and windows that are being restored from a session do also. we can assume you want @@ -368,27 +377,71 @@ void client_manage(Window window) place.c or by the user are allowed partially off-screen and on xinerama divides (ie, it is up to the placement routines to avoid - the xinerama divides) */ - transient || - (((self->positioned & PPosition) && - !(self->positioned & USPosition)) && - client_normal(self) && - !self->session)); + the xinerama divides) + + splash screens get "transient" set to TRUE by + the place_client call + */ + ob_state() == OB_STATE_RUNNING && + (transient || + (!(self->positioned & USPosition) && + client_normal(self) && + !self->session))); } - /* do this after the window is placed, so the premax/prefullscreen numbers - won't be all wacko!! - also, this moves the window to the position where it has been placed + /* if the window isn't user-sized, then make it fit inside + the visible screen area on its monitor. Use basically the same rules + for forcing the window on screen in the client_find_onscreen call. + + do this after place_client, it chooses the monitor! + + splash screens get "transient" set to TRUE by + the place_client call */ - ob_debug("placing window 0x%x at %d, %d with size %d x %d\n", - self->window, newx, newy, self->area.width, self->area.height); + if (ob_state() == OB_STATE_RUNNING && + (transient || + (!(self->sized & USSize || self->positioned & USPosition) && + client_normal(self) && + !self->session))) + { + Rect placer; + + RECT_SET(placer, placex, placey, placew, placeh); + frame_rect_to_frame(self->frame, &placer); + + Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &placer); + + /* 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; + + /* 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); + + 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); if (self->session) - ob_debug("session requested %d %d\n", - self->session->x, self->session->y); + ob_debug(" but session requested %d, %d %d x %d instead, " + "overriding\n", + self->session->x, self->session->y, + self->session->w, self->session->h); - client_apply_startup_state(self, newx, newy); + /* do this after the window is placed, so the premax/prefullscreen numbers + won't be all wacko!! - mouse_grab_for_client(self, TRUE); + this also places the window + */ + client_apply_startup_state(self, placex, placey, placew, placeh); if (activate) { guint32 last_time = focus_client ? @@ -411,8 +464,9 @@ void client_manage(Window window) "Not focusing the window because its on another " "desktop\n"); } - /* If something is focused, and it's not our parent... */ - else if (focus_client && client_search_focus_parent(self) == NULL) + /* If something is focused, and it's not our relative... */ + 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 && @@ -423,15 +477,37 @@ void client_manage(Window window) "Not focusing the window because the time is " "too old\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)) + { + activate = FALSE; + ob_debug_type(OB_DEBUG_FOCUS, + "Not focusing the window because it is a " + "transient, and the time is very ambiguous\n"); + } /* Don't steal focus from globally active clients. I stole this idea from KWin. It seems nice. */ - if (!(focus_client->can_focus || focus_client->focus_notify)) { + else if (!(focus_client->can_focus || + focus_client->focus_notify)) + { activate = FALSE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because a globally " "active client has focus\n"); } + /* Don't move focus if it's not going to go to this window + anyway */ + else if (client_focus_target(self) != self) { + activate = FALSE; + ob_debug_type(OB_DEBUG_FOCUS, + "Not focusing the window because another window " + "would get the focus anyway\n"); + } } if (!activate) { @@ -456,19 +532,25 @@ void client_manage(Window window) 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 */ - client_show(self); + { + 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); + } - /* use client_focus instead of client_activate cuz client_activate does - stuff like switch desktops etc and I'm not interested in all that when - a window maps since its not based on an action from the user like - clicking a window to activate it. so keep the new window out of the way - but do focus it. */ if (activate) { gboolean stacked = client_restore_session_stacking(self); - client_present(self, FALSE, !stacked); + client_present(self, FALSE, !stacked, TRUE); } /* add to client list/map */ @@ -482,7 +564,11 @@ void client_manage(Window window) /* update the list hints */ client_set_list(); - ob_debug("Managed window 0x%lx (%s)\n", window, self->class); + /* free the ObAppSettings shallow copy */ + g_free(settings); + + ob_debug("Managed window 0x%lx plate 0x%x (%s)\n", + window, self->frame->window, self->class); return; } @@ -502,15 +588,22 @@ ObClient *client_fake_manage(Window window) client_get_all(self, FALSE); /* per-app settings override stuff, and return the settings for other - uses too */ + uses too. this returns a shallow copy that needs to be freed */ settings = client_get_settings_state(self); - client_setup_decor_and_functions(self); - /* adjust the decorations so we know the sizes */ - frame_adjust_area(self->frame, FALSE, TRUE, TRUE); + client_setup_decor_and_functions(self, FALSE); - /* create the decoration frame for the client window */ + /* 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); + + ob_debug("gave extents left %d right %d top %d bottom %d\n", + self->frame->size.left, self->frame->size.right, + self->frame->size.top, self->frame->size.bottom); + + /* free the ObAppSettings shallow copy */ + g_free(settings); + return self; } @@ -524,8 +617,10 @@ void client_unmanage(ObClient *self) { guint j; GSList *it; + gulong ignore_start; - ob_debug("Unmanaging window: %lx (%s) (%s)\n", self->window, + ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n", + self->window, self->frame->window, self->class, self->title ? self->title : ""); g_assert(self != NULL); @@ -534,13 +629,16 @@ void client_unmanage(ObClient *self) don't generate more events */ XSelectInput(ob_display, self->window, NoEventMask); + /* ignore enter events from the unmap so it doesnt mess with the focus */ + if (!config_focus_under_mouse) + ignore_start = event_start_ignore_all_enters(); + frame_hide(self->frame); /* flush to send the hide to the server quickly */ XFlush(ob_display); - /* ignore enter events from the unmap so it doesnt mess with the - focus */ - event_ignore_queued_enters(); + if (!config_focus_under_mouse) + event_end_ignore_all_enters(ignore_start); mouse_grab_for_client(self, FALSE); @@ -566,28 +664,19 @@ void client_unmanage(ObClient *self) if (STRUT_EXISTS(self->strut)) screen_update_areas(); - for (it = client_destructors; it; it = g_slist_next(it)) { - ClientCallback *d = it->data; - d->func(self, d->data); - } + client_call_notifies(self, client_destroy_notifies); /* tell our parent(s) that we're gone */ - if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */ - for (it = self->group->members; it; it = g_slist_next(it)) - if (it->data != self) - ((ObClient*)it->data)->transients = - g_slist_remove(((ObClient*)it->data)->transients,self); - } else if (self->transient_for) { /* transient of window */ - self->transient_for->transients = - g_slist_remove(self->transient_for->transients, self); - } + for (it = self->parents; it; it = g_slist_next(it)) + ((ObClient*)it->data)->transients = + g_slist_remove(((ObClient*)it->data)->transients,self); /* tell our transients that we're gone */ for (it = self->transients; it; it = g_slist_next(it)) { - if (((ObClient*)it->data)->transient_for != OB_TRAN_GROUP) { - ((ObClient*)it->data)->transient_for = NULL; - client_calc_layer(it->data); - } + ((ObClient*)it->data)->parents = + g_slist_remove(((ObClient*)it->data)->parents, self); + /* we could be keeping our children in a higher layer */ + client_calc_layer(it->data); } /* remove from its group */ @@ -598,7 +687,9 @@ void client_unmanage(ObClient *self) /* restore the window's original geometry so it is not lost */ { - Rect a = self->area; + Rect a; + + a = self->area; if (self->fullscreen) a = self->pre_fullscreen_area; @@ -613,12 +704,14 @@ void client_unmanage(ObClient *self) } } - /* give the client its border back */ - client_toggle_border(self, TRUE); - self->fullscreen = self->max_horz = self->max_vert = FALSE; + /* let it be moved and resized no matter what */ + self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE; self->decorations = 0; /* unmanaged windows have no decor */ + /* give the client its border back */ + XSetWindowBorderWidth(ob_display, self->window, self->border_width); + client_move_resize(self, a.x, a.y, a.width, a.height); } @@ -670,73 +763,82 @@ void client_fake_unmanage(ObClient *self) g_free(self); } +/*! Returns a new structure containing the per-app settings for this client. + The returned structure needs to be freed with g_free. */ static ObAppSettings *client_get_settings_state(ObClient *self) { - ObAppSettings *settings = NULL; + ObAppSettings *settings; GSList *it; + settings = config_create_app_settings(); + for (it = config_per_app_settings; it; it = g_slist_next(it)) { ObAppSettings *app = it->data; - - if ((app->name && !app->class && !strcmp(app->name, self->name)) - || (app->class && !app->name && !strcmp(app->class, self->class)) - || (app->class && app->name && !strcmp(app->class, self->class) - && !strcmp(app->name, self->name))) - { - /* Match if no role was specified in the per app setting, or if the - * string matches the beginning of the role, since apps like to set - * the role to things like browser-window-23c4b2f */ - if (!app->role - || !strncmp(app->role, self->role, strlen(app->role))) - { - ob_debug("Window matching: %s\n", app->name); - /* use this one */ - settings = app; - break; - } + gboolean match = TRUE; + + g_assert(app->name != NULL || app->class != NULL); + + /* 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->class && + !g_pattern_match(app->class, + strlen(self->class), self->class, NULL)) + match = FALSE; + else if (app->role && + !g_pattern_match(app->role, + strlen(self->role), self->role, NULL)) + match = FALSE; + + if (match) { + ob_debug("Window matching: %s\n", app->name); + + /* copy the settings to our struct, overriding the existing + settings if they are not defaults */ + config_app_settings_copy_non_defaults(app, settings); } } - if (settings) { - if (settings->shade != -1) - self->shaded = !!settings->shade; - if (settings->decor != -1) - self->undecorated = !settings->decor; - if (settings->iconic != -1) - self->iconic = !!settings->iconic; - if (settings->skip_pager != -1) - self->skip_pager = !!settings->skip_pager; - if (settings->skip_taskbar != -1) - self->skip_taskbar = !!settings->skip_taskbar; - - if (settings->max_vert != -1) - self->max_vert = !!settings->max_vert; - if (settings->max_horz != -1) - self->max_horz = !!settings->max_horz; - - if (settings->fullscreen != -1) - self->fullscreen = !!settings->fullscreen; - - if (settings->desktop) { - if (settings->desktop == DESKTOP_ALL) - self->desktop = settings->desktop; - else if (settings->desktop > 0 && - settings->desktop <= screen_num_desktops) - self->desktop = settings->desktop - 1; - } + if (settings->shade != -1) + self->shaded = !!settings->shade; + if (settings->decor != -1) + self->undecorated = !settings->decor; + if (settings->iconic != -1) + self->iconic = !!settings->iconic; + if (settings->skip_pager != -1) + self->skip_pager = !!settings->skip_pager; + if (settings->skip_taskbar != -1) + self->skip_taskbar = !!settings->skip_taskbar; + + if (settings->max_vert != -1) + self->max_vert = !!settings->max_vert; + if (settings->max_horz != -1) + self->max_horz = !!settings->max_horz; + + if (settings->fullscreen != -1) + self->fullscreen = !!settings->fullscreen; + + if (settings->desktop) { + if (settings->desktop == DESKTOP_ALL) + self->desktop = settings->desktop; + else if (settings->desktop > 0 && + settings->desktop <= screen_num_desktops) + self->desktop = settings->desktop - 1; + } - if (settings->layer == -1) { - self->below = TRUE; - self->above = FALSE; - } - else if (settings->layer == 0) { - self->below = FALSE; - self->above = FALSE; - } - else if (settings->layer == 1) { - self->below = FALSE; - self->above = TRUE; - } + if (settings->layer == -1) { + self->below = TRUE; + self->above = FALSE; + } + else if (settings->layer == 0) { + self->below = FALSE; + self->above = FALSE; + } + else if (settings->layer == 1) { + self->below = FALSE; + self->above = TRUE; } return settings; } @@ -761,6 +863,7 @@ static void client_restore_session_state(ObClient *self) RECT_SET_POINT(self->area, self->session->x, self->session->y); self->positioned = USPosition; + self->sized = USSize; if (self->session->w > 0) self->area.width = self->session->w; if (self->session->h > 0) @@ -825,45 +928,24 @@ void client_move_onscreen(ObClient *self, gboolean rude) gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, gboolean rude) { - Rect *mon_a, *all_a; gint ox = *x, oy = *y; gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude; gint fw, fh; + Rect desired; + guint i; - all_a = screen_area(self->desktop); - mon_a = screen_area_monitor(self->desktop, client_monitor(self)); + RECT_SET(desired, *x, *y, w, h); + frame_rect_to_frame(self->frame, &desired); /* get where the frame would be */ - frame_client_gravity(self->frame, x, y, w, h); + frame_client_gravity(self->frame, x, y); /* get the requested size of the window with decorations */ fw = self->frame->size.left + w + self->frame->size.right; fh = self->frame->size.top + h + self->frame->size.bottom; - /* This makes sure windows aren't entirely outside of the screen so you - can't see them at all. - It makes sure 10% of the window is on the screen at least. At don't let - it move itself off the top of the screen, which would hide the titlebar - on you. (The user can still do this if they want too, it's only limiting - the application. - - XXX watch for xinerama dead areas... - */ - if (client_normal(self)) { - if (!self->strut.right && *x + fw/10 >= all_a->x + all_a->width - 1) - *x = all_a->x + all_a->width - fw/10; - if (!self->strut.bottom && *y + fh/10 >= all_a->y + all_a->height - 1) - *y = all_a->y + all_a->height - fh/10; - if (!self->strut.left && *x + fw*9/10 - 1 < all_a->x) - *x = all_a->x - fw*9/10; - if (!self->strut.top && *y + fh*9/10 - 1 < all_a->y) - *y = all_a->y - fw*9/10; - } - - /* If rudeness wasn't requested, then figure out of the client is currently - entirely on the screen. If it is, and the position isn't changing by - request, and it is enlarging, then be rude even though it wasn't - requested */ + /* If rudeness wasn't requested, then still be rude in a given direction + if the client is not moving, only resizing in that direction */ if (!rude) { Point oldtl, oldtr, oldbl, oldbr; Point newtl, newtr, newbl, newbr; @@ -881,10 +963,10 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, POINT_SET(newbl, newtl.x, newbr.y); /* is it moving or just resizing from some corner? */ - stationary_l = oldtl.x == oldtl.x; - stationary_r = oldtr.x == oldtr.x; - stationary_t = oldtl.y == oldtl.y; - stationary_b = oldbl.y == oldbl.y; + stationary_l = oldtl.x == newtl.x; + stationary_r = oldtr.x == newtr.x; + stationary_t = oldtl.y == newtl.y; + stationary_b = oldbl.y == newbl.y; /* if left edge is growing and didnt move right edge */ if (stationary_r && newtl.x < oldtl.x) @@ -900,94 +982,55 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, rudeb = TRUE; } - /* This here doesn't let windows even a pixel outside the struts/screen. - * When called from client_manage, programs placing themselves are - * forced completely onscreen, while things like - * xterm -geometry resolution-width/2 will work fine. Trying to - * place it completely offscreen will be handled in the above code. - * Sorry for this confused comment, i am tired. */ - if (fw <= mon_a->width) { - if (rudel && !self->strut.left && *x < mon_a->x) *x = mon_a->x; - if (ruder && !self->strut.right && *x + fw > mon_a->x + mon_a->width) - *x = mon_a->x + mon_a->width - fw; - } - if (fh <= mon_a->height) { - if (rudet && !self->strut.top && *y < mon_a->y) *y = mon_a->y; - if (rudeb && !self->strut.bottom && *y + fh > mon_a->y + mon_a->height) - *y = mon_a->y + mon_a->height - fh; - } + for (i = 0; i < screen_num_monitors; ++i) { + Rect *a; - /* get where the client should be */ - frame_frame_gravity(self->frame, x, y, w, h); + if (!screen_physical_area_monitor_contains(i, &desired)) + continue; - return ox != *x || oy != *y; -} + a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired); -static void client_toggle_border(ObClient *self, gboolean show) -{ - /* adjust our idea of where the client is, based on its border. When the - border is removed, the client should now be considered to be in a - different position. - when re-adding the border to the client, the same operation needs to be - reversed. */ - gint oldx = self->area.x, oldy = self->area.y; - gint x = oldx, y = oldy; - switch(self->gravity) { - default: - case NorthWestGravity: - case WestGravity: - case SouthWestGravity: - break; - case NorthEastGravity: - case EastGravity: - case SouthEastGravity: - if (show) x -= self->border_width * 2; - else x += self->border_width * 2; - break; - case NorthGravity: - case SouthGravity: - case CenterGravity: - case ForgetGravity: - case StaticGravity: - if (show) x -= self->border_width; - else x += self->border_width; - break; - } - switch(self->gravity) { - default: - case NorthWestGravity: - case NorthGravity: - case NorthEastGravity: - break; - case SouthWestGravity: - case SouthGravity: - case SouthEastGravity: - if (show) y -= self->border_width * 2; - else y += self->border_width * 2; - break; - case WestGravity: - case EastGravity: - case CenterGravity: - case ForgetGravity: - case StaticGravity: - if (show) y -= self->border_width; - else y += self->border_width; - break; + /* This makes sure windows aren't entirely outside of the screen so you + can't see them at all. + It makes sure 10% of the window is on the screen at least. At don't + let it move itself off the top of the screen, which would hide the + titlebar on you. (The user can still do this if they want too, it's + only limiting the application. + */ + if (client_normal(self)) { + if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1) + *x = a->x + a->width - fw/10; + if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1) + *y = a->y + a->height - fh/10; + 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; + } + + /* This here doesn't let windows even a pixel outside the + struts/screen. When called from client_manage, programs placing + themselves are forced completely onscreen, while things like + xterm -geometry resolution-width/2 will work fine. Trying to + place it completely offscreen will be handled in the above code. + Sorry for this confused comment, i am tired. */ + if (rudel && !self->strut.left && *x < a->x) *x = a->x; + if (ruder && !self->strut.right && *x + fw > a->x + a->width) + *x = a->x + MAX(0, a->width - fw); + + if (rudet && !self->strut.top && *y < a->y) *y = a->y; + if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height) + *y = a->y + MAX(0, a->height - fh); + + g_free(a); } - self->area.x = x; - self->area.y = y; - if (show) { - XSetWindowBorderWidth(ob_display, self->window, self->border_width); + /* get where the client should be */ + frame_frame_gravity(self->frame, x, y); - /* set border_width to 0 because there is no border to add into - calculations anymore */ - self->border_width = 0; - } else - XSetWindowBorderWidth(ob_display, self->window, 0); + return ox != *x || oy != *y; } - static void client_get_all(ObClient *self, gboolean real) { /* this is needed for the frame to set itself up */ @@ -1005,14 +1048,18 @@ static void client_get_all(ObClient *self, gboolean real) from per-app settings */ client_get_session_ids(self); + /* now we got everything that can affect the decorations */ if (!real) return; + /* get this early so we have it for debugging */ + client_update_title(self); + client_update_protocols(self); client_update_wmhints(self); /* this may have already been called from client_update_wmhints */ - if (self->transient_for == NULL) + if (!self->parents && !self->transient_for_group) client_update_transient_for(self); client_get_startup_id(self); @@ -1020,9 +1067,6 @@ static void client_get_all(ObClient *self, gboolean real) desktop is not specified */ client_get_shaped(self); - client_get_layer(self); /* if layer hasn't been specified, get it from - other sources if possible */ - { /* a couple type-based defaults for new windows */ @@ -1036,7 +1080,6 @@ static void client_get_all(ObClient *self, gboolean real) #endif client_get_colormap(self); - client_update_title(self); client_update_strut(self); client_update_icons(self); client_update_user_time_window(self); @@ -1065,8 +1108,8 @@ static void client_get_area(ObClient *self) POINT_SET(self->root_pos, wattrib.x, wattrib.y); self->border_width = wattrib.border_width; - ob_debug("client area: %d %d %d %d\n", wattrib.x, wattrib.y, - wattrib.width, wattrib.height); + ob_debug("client area: %d %d %d %d bw %d\n", wattrib.x, wattrib.y, + wattrib.width, wattrib.height, wattrib.border_width); } static void client_get_desktop(ObClient *self) @@ -1078,69 +1121,45 @@ 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); } else { - gboolean trdesk = FALSE; + GSList *it; + gboolean first = TRUE; + guint all = screen_num_desktops; /* not a valid value */ - if (self->transient_for) { - if (self->transient_for != OB_TRAN_GROUP) { - self->desktop = self->transient_for->desktop; - trdesk = TRUE; - } else { - GSList *it; + /* if they are all on one desktop, then open it on the + same desktop */ + for (it = self->parents; it; it = g_slist_next(it)) { + ObClient *c = it->data; - for (it = self->group->members; it; it = g_slist_next(it)) - if (it->data != self && - !((ObClient*)it->data)->transient_for) { - self->desktop = ((ObClient*)it->data)->desktop; - trdesk = TRUE; - break; - } + if (c->desktop == DESKTOP_ALL) continue; + + if (first) { + all = c->desktop; + first = FALSE; } + else if (all != c->desktop) + all = screen_num_desktops; /* make it invalid */ } - if (!trdesk) { - /* try get from the startup-notification protocol */ - if (sn_get_desktop(self->startup_id, &self->desktop)) { - if (self->desktop >= screen_num_desktops && - self->desktop != DESKTOP_ALL) - self->desktop = screen_num_desktops - 1; - } else - /* defaults to the current desktop */ - self->desktop = screen_desktop; - } - } -} + if (all != screen_num_desktops) { + self->desktop = all; -static void client_get_layer(ObClient *self) -{ - if (!(self->above || self->below)) { - if (self->group) { - /* apply stuff from the group */ - GSList *it; - gint layer = -2; - - for (it = self->group->members; it; it = g_slist_next(it)) { - ObClient *c = it->data; - if (c != self && !client_search_transient(self, c) && - client_normal(self) && client_normal(c)) - { - layer = MAX(layer, - (c->above ? 1 : (c->below ? -1 : 0))); - } - } - switch (layer) { - case -1: - self->below = TRUE; - break; - case -2: - case 0: - break; - case 1: - self->above = TRUE; - break; - default: - g_assert_not_reached(); - break; - } + ob_debug("client desktop set from parents: 0x%x\n", + self->desktop); + } + /* try get from the startup-notification protocol */ + else if (sn_get_desktop(self->startup_id, &self->desktop)) { + if (self->desktop >= screen_num_desktops && + self->desktop != DESKTOP_ALL) + self->desktop = screen_num_desktops - 1; + ob_debug("client desktop set from startup-notification: 0x%x\n", + self->desktop); + } + /* defaults to the current desktop */ + else { + self->desktop = screen_desktop; + ob_debug("client desktop set to the current desktop: %d\n", + self->desktop); } } } @@ -1175,7 +1194,7 @@ static void client_get_state(ObClient *self) self->below = TRUE; else if (state[i] == prop_atoms.net_wm_state_demands_attention) self->demands_attention = TRUE; - else if (state[i] == prop_atoms.openbox_wm_state_undecorated) + else if (state[i] == prop_atoms.ob_wm_state_undecorated) self->undecorated = TRUE; } @@ -1206,6 +1225,7 @@ void client_update_transient_for(ObClient *self) { Window t = None; ObClient *target = NULL; + gboolean trangroup = FALSE; if (XGetTransientForHint(ob_display, self->window, &t)) { if (t != self->window) { /* cant be transient to itself! */ @@ -1217,149 +1237,138 @@ void client_update_transient_for(ObClient *self) a dockapp, for example */ target = NULL; } - - /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat.. - - Setting the transient_for to Root is actually illegal, however - applications from time have done this to specify transient for - their group. - - Now you can do that by being a TYPE_DIALOG and not setting - the transient_for hint at all on your window. But people still - use Root, and Kwin is very strange in this regard. - - KWin 3.0 will not consider windows with transient_for set to - Root as transient for their group *UNLESS* they are also modal. - In that case, it will make them transient for the group. This - leads to all sorts of weird behavior from KDE apps which are - only tested in KWin. I'd like to follow their behavior just to - make this work right with KDE stuff, but that seems wrong. - */ - if (!target && self->group) { - /* not transient to a client, see if it is transient for a - group */ - if (t == RootWindow(ob_display, ob_screen)) { - /* window is a transient for its group! */ - target = OB_TRAN_GROUP; - } - } } - } else if (self->transient && self->group) - target = OB_TRAN_GROUP; + + /* Setting the transient_for to Root is actually illegal, however + applications from time have done this to specify transient for + their group */ + if (!target && self->group && t == RootWindow(ob_display, ob_screen)) + trangroup = TRUE; + } else if (self->group && self->transient) + trangroup = TRUE; client_update_transient_tree(self, self->group, self->group, - self->transient_for, target); - self->transient_for = target; + self->transient_for_group, trangroup, + client_direct_parent(self), target); + self->transient_for_group = trangroup; } static void client_update_transient_tree(ObClient *self, ObGroup *oldgroup, ObGroup *newgroup, + gboolean oldgtran, gboolean newgtran, ObClient* oldparent, ObClient *newparent) { GSList *it, *next; ObClient *c; - /* No change has occured */ - if (oldgroup == newgroup && oldparent == newparent) return; + g_assert(!oldgtran || oldgroup); + g_assert(!newgtran || newgroup); + g_assert((!oldgtran && !oldparent) || + (oldgtran && !oldparent) || + (!oldgtran && oldparent)); + g_assert((!newgtran && !newparent) || + (newgtran && !newparent) || + (!newgtran && newparent)); - /** Remove the client from the transient tree wherever it has changed **/ + /* * * + Group transient windows are not allowed to have other group + transient windows as their children. + * * */ - /* If the window is becoming a direct transient for a window in its group - then that window can't be a child of this window anymore */ - if (oldparent != newparent && - newparent != NULL && newparent != OB_TRAN_GROUP && - newparent->transient_for == OB_TRAN_GROUP && - newgroup != NULL && newgroup == oldgroup) - { - self->transients = g_slist_remove(self->transients, newparent); - } - - /* 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 || - (newparent == OB_TRAN_GROUP && oldparent != newparent)) && - oldgroup != NULL && oldparent != OB_TRAN_GROUP) - { - for (it = self->transients; it; it = next) { - next = g_slist_next(it); - c = it->data; - if (c->group == oldgroup) - self->transients = g_slist_delete_link(self->transients, it); - } - } + /* No change has occured */ + if (oldgroup == newgroup && + oldgtran == newgtran && + oldparent == newparent) return; - /* 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 (oldparent == OB_TRAN_GROUP && (oldgroup != newgroup || - oldparent != newparent)) - { - for (it = oldgroup->members; it; it = g_slist_next(it)) { - c = it->data; - if (c != self && (!c->transient_for || - c->transient_for != OB_TRAN_GROUP)) - c->transients = g_slist_remove(c->transients, self); - } - } - /* 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 != NULL && oldparent != OB_TRAN_GROUP && - oldparent != newparent) - oldparent->transients = g_slist_remove(oldparent->transients, self); + /** Remove the client from the transient tree **/ + 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); + } + 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); + } - /** Re-add the client to the transient tree wherever it has changed **/ + /** Re-add the client to the transient tree **/ - /* 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 (newparent == OB_TRAN_GROUP && (oldgroup != newgroup || - oldparent != newparent)) - { - 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 || - c->transient_for != OB_TRAN_GROUP)) + 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 != NULL && newparent != OB_TRAN_GROUP && - newparent != oldparent && + else if (newparent && /* don't make ourself its child if it is already our child */ - !client_is_direct_child(self, newparent)) + !client_is_direct_child(self, newparent) && + client_normal(newparent)) + { newparent->transients = g_slist_prepend(newparent->transients, 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 - B is a member of the group and transient for A + B is transient for A + C is transient for B + A can't be transient for C or we have a cycle */ - if (oldgroup != newgroup && newgroup != NULL && - newparent != OB_TRAN_GROUP) + 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)) { c = it->data; - if (c != self && c->transient_for == OB_TRAN_GROUP && + if (c != self && c->transient_for_group && /* Don't make it our child if it is already our parent */ !client_is_direct_child(c, self)) { self->transients = g_slist_prepend(self->transients, c); + c->parents = g_slist_prepend(c->parents, 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) @@ -1496,14 +1505,22 @@ void client_get_colormap(ObClient *self) void client_update_colormap(ObClient *self, Colormap colormap) { - self->colormap = colormap; + if (colormap == self->colormap) return; + + ob_debug("Setting client %s colormap: 0x%x\n", self->title, colormap); + + if (client_focused(self)) { + screen_install_colormap(self, FALSE); /* uninstall old one */ + self->colormap = colormap; + screen_install_colormap(self, FALSE); /* install new one */ + } else + self->colormap = colormap; } void client_update_normal_hints(ObClient *self) { XSizeHints size; glong ret; - gint oldgravity = self->gravity; /* defaults */ self->min_ratio = 0.0f; @@ -1519,20 +1536,10 @@ void client_update_normal_hints(ObClient *self) if (!client_normal(self)) */ self->positioned = (size.flags & (PPosition|USPosition)); + self->sized = (size.flags & (PSize|USSize)); - if (size.flags & PWinGravity) { + if (size.flags & PWinGravity) self->gravity = size.win_gravity; - - /* if the client has a frame, i.e. has already been mapped and - is changing its gravity */ - if (self->frame && self->gravity != oldgravity) { - /* move our idea of the client's position based on its new - gravity */ - client_convert_gravity(self, oldgravity, - &self->area.x, &self->area.y, - self->area.width, self->area.height); - } - } if (size.flags & PAspect) { if (size.min_aspect.y) @@ -1554,10 +1561,19 @@ void client_update_normal_hints(ObClient *self) if (size.flags & PResizeInc && size.width_inc && size.height_inc) SIZE_SET(self->size_inc, size.width_inc, size.height_inc); + + ob_debug("Normal hints: min size (%d %d) max size (%d %d)\n " + "size inc (%d %d) base size (%d %d)\n", + self->min_size.width, self->min_size.height, + self->max_size.width, self->max_size.height, + self->size_inc.width, self->size_inc.height, + self->base_size.width, self->base_size.height); } + else + ob_debug("Normal hints: not set\n"); } -void client_setup_decor_and_functions(ObClient *self) +void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) { /* start with everything (cept fullscreen) */ self->decorations = @@ -1577,7 +1593,10 @@ void client_setup_decor_and_functions(ObClient *self) OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_MAXIMIZE | OB_CLIENT_FUNC_SHADE | - OB_CLIENT_FUNC_CLOSE); + OB_CLIENT_FUNC_CLOSE | + OB_CLIENT_FUNC_BELOW | + OB_CLIENT_FUNC_ABOVE | + OB_CLIENT_FUNC_UNDECORATE); if (!(self->min_size.width < self->max_size.width || self->min_size.height < self->max_size.height)) @@ -1592,14 +1611,16 @@ void client_setup_decor_and_functions(ObClient *self) case OB_CLIENT_TYPE_DIALOG: case OB_CLIENT_TYPE_UTILITY: - /* these windows cannot be maximized */ - self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE; + /* these windows don't have anything added or removed by default */ break; case OB_CLIENT_TYPE_MENU: case OB_CLIENT_TYPE_TOOLBAR: - /* these windows get less functionality */ - self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE); + /* these windows can't iconify or maximize */ + self->decorations &= ~(OB_FRAME_DECOR_ICONIFY | + OB_FRAME_DECOR_MAXIMIZE); + self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | + OB_CLIENT_FUNC_MAXIMIZE); break; case OB_CLIENT_TYPE_SPLASH: @@ -1607,13 +1628,20 @@ void client_setup_decor_and_functions(ObClient *self) do with them is move them */ self->decorations = 0; self->functions = OB_CLIENT_FUNC_MOVE; + break; case OB_CLIENT_TYPE_DESKTOP: - case OB_CLIENT_TYPE_DOCK: /* these windows are not manipulated by the window manager */ self->decorations = 0; self->functions = 0; break; + + case OB_CLIENT_TYPE_DOCK: + /* these windows are not manipulated by the window manager, but they + can set below layer which has a special meaning */ + self->decorations = 0; + self->functions = OB_CLIENT_FUNC_BELOW; + break; } /* Mwm Hints are applied subtractively to what has already been chosen for @@ -1657,7 +1685,7 @@ void client_setup_decor_and_functions(ObClient *self) if (!(self->functions & OB_CLIENT_FUNC_ICONIFY)) self->decorations &= ~OB_FRAME_DECOR_ICONIFY; if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) - self->decorations &= ~OB_FRAME_DECOR_GRIPS; + self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE); /* can't maximize without moving/resizing */ if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) && @@ -1667,18 +1695,22 @@ void client_setup_decor_and_functions(ObClient *self) self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE; } - /* kill the handle on fully maxed windows */ - if (self->max_vert && self->max_horz) + 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) */ - if (self->undecorated) { - if (config_theme_keepborder) - self->decorations &= OB_FRAME_DECOR_BORDER; - else - self->decorations = 0; - } + if (self->undecorated) + self->decorations = 0; /* if we don't have a titlebar, then we cannot shade! */ if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR)) @@ -1694,15 +1726,14 @@ void client_setup_decor_and_functions(ObClient *self) client_change_allowed_actions(self); - if (self->frame) { - /* adjust the client's decorations, etc. */ - client_reconfigure(self); - } + if (reconfig) + /* force reconfigure to make sure decorations are updated */ + client_reconfigure(self, TRUE); } static void client_change_allowed_actions(ObClient *self) { - gulong actions[9]; + gulong actions[12]; gint num = 0; /* desktop windows are kept on all desktops */ @@ -1725,6 +1756,12 @@ static void client_change_allowed_actions(ObClient *self) actions[num++] = prop_atoms.net_wm_action_maximize_horz; actions[num++] = prop_atoms.net_wm_action_maximize_vert; } + if (self->functions & OB_CLIENT_FUNC_ABOVE) + actions[num++] = prop_atoms.net_wm_action_above; + if (self->functions & OB_CLIENT_FUNC_BELOW) + actions[num++] = prop_atoms.net_wm_action_below; + if (self->functions & OB_CLIENT_FUNC_UNDECORATE) + actions[num++] = prop_atoms.ob_wm_action_undecorate; PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num); @@ -1735,7 +1772,7 @@ static void client_change_allowed_actions(ObClient *self) else self->shaded = FALSE; } if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) { - if (self->frame) client_iconify(self, FALSE, TRUE); + if (self->frame) client_iconify(self, FALSE, TRUE, FALSE); else self->iconic = FALSE; } if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) { @@ -1749,15 +1786,6 @@ static void client_change_allowed_actions(ObClient *self) } } -void client_reconfigure(ObClient *self) -{ - /* by making this pass FALSE for user, we avoid the emacs event storm where - every configurenotify causes an update in its normal hints, i think this - is generally what we want anyways... */ - client_configure(self, self->area.x, self->area.y, - self->area.width, self->area.height, FALSE, TRUE); -} - void client_update_wmhints(ObClient *self) { XWMHints *hints; @@ -1807,8 +1835,10 @@ void client_update_wmhints(ObClient *self) /* Put ourselves into the new group's transient tree, and remove ourselves from the old group's */ client_update_transient_tree(self, oldgroup, self->group, - self->transient_for, - self->transient_for); + self->transient_for_group, + self->transient_for_group, + client_direct_parent(self), + client_direct_parent(self)); /* Lastly, being in a group, or not, can change if the window is transient for anything. @@ -1818,11 +1848,11 @@ void client_update_wmhints(ObClient *self) transient for something, even if transient_for was NULL because it wasn't in a group before. - If transient_for was NULL and oldgroup was NULL we can assume + If parents was NULL and oldgroup was NULL we can assume that when we add the new group, it will become transient for something. - If transient_for was OB_TRAN_GROUP, then it must have already + If transient_for_group is TRUE, then it must have already had a group. If it is getting a new group, the above call to client_update_transient_tree has already taken care of everything ! If it is losing all group status then it will @@ -1830,13 +1860,14 @@ void client_update_wmhints(ObClient *self) updated. */ if (self->transient && - ((self->transient_for == NULL && oldgroup == NULL) || - (self->transient_for == OB_TRAN_GROUP && !self->group))) + ((self->parents == NULL && oldgroup == NULL) || + (self->transient_for_group && !self->group))) client_update_transient_for(self); } /* the WM_HINTS can contain an icon */ - client_update_icons(self); + if (hints->flags & IconPixmapHint) + client_update_icons(self); XFree(hints); } @@ -1888,13 +1919,19 @@ void client_update_title(ObClient *self) PROP_GETS(self->window, wm_icon_name, utf8, &data))) data = g_strdup(self->title); - PROP_SETS(self->window, net_wm_visible_icon_name, data); - self->icon_title = data; -} - -void client_update_strut(ObClient *self) -{ - guint num; + if (self->client_machine) { + visible = g_strdup_printf("%s (%s)", data, self->client_machine); + g_free(data); + } else + visible = data; + + PROP_SETS(self->window, net_wm_visible_icon_name, visible); + self->icon_title = visible; +} + +void client_update_strut(ObClient *self) +{ + guint num; guint32 *data; gboolean got = FALSE; StrutPartial strut; @@ -1914,12 +1951,12 @@ void client_update_strut(ObClient *self) if (!got && PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) { if (num == 4) { - const Rect *a; + Rect *a; got = TRUE; /* use the screen's width/height */ - a = screen_physical_area(); + a = screen_physical_area_all_monitors(); STRUT_PARTIAL_SET(strut, data[0], data[2], data[1], data[3], @@ -1927,6 +1964,7 @@ 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); } @@ -2001,18 +2039,19 @@ void client_update_icons(ObClient *self) if ((hints = XGetWMHints(ob_display, self->window))) { if (hints->flags & IconPixmapHint) { - self->nicons++; + self->nicons = 1; self->icons = g_new(ObClientIcon, self->nicons); xerror_set_ignore(TRUE); if (!RrPixmapToRGBA(ob_rr_inst, hints->icon_pixmap, (hints->flags & IconMaskHint ? hints->icon_mask : None), - &self->icons[self->nicons-1].width, - &self->icons[self->nicons-1].height, - &self->icons[self->nicons-1].data)){ - g_free(&self->icons[self->nicons-1]); - self->nicons--; + &self->icons[0].width, + &self->icons[0].height, + &self->icons[0].data)) + { + g_free(self->icons); + self->nicons = 0; } xerror_set_ignore(FALSE); } @@ -2023,8 +2062,11 @@ void client_update_icons(ObClient *self) /* set the default icon onto the window in theory, this could be a race, but if a window doesn't set an icon or removes it entirely, it's not very likely it is going to set one - right away afterwards */ - if (self->nicons == 0) { + right away afterwards + + 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; gulong *data; @@ -2235,7 +2277,7 @@ static void client_change_wm_state(ObClient *self) static void client_change_state(ObClient *self) { - gulong netstate[11]; + gulong netstate[12]; guint num; num = 0; @@ -2262,7 +2304,7 @@ static void client_change_state(ObClient *self) if (self->demands_attention) netstate[num++] = prop_atoms.net_wm_state_demands_attention; if (self->undecorated) - netstate[num++] = prop_atoms.openbox_wm_state_undecorated; + netstate[num++] = prop_atoms.ob_wm_state_undecorated; PROP_SETA32(self->window, net_wm_state, atom, netstate, num); if (self->frame) @@ -2283,35 +2325,52 @@ ObClient *client_search_focus_tree(ObClient *self) ObClient *client_search_focus_tree_full(ObClient *self) { - if (self->transient_for) { - if (self->transient_for != OB_TRAN_GROUP) { - return client_search_focus_tree_full(self->transient_for); - } else { - GSList *it; - gboolean recursed = FALSE; - - for (it = self->group->members; it; it = g_slist_next(it)) - if (!((ObClient*)it->data)->transient_for) { - ObClient *c; - if ((c = client_search_focus_tree_full(it->data))) - return c; - recursed = TRUE; - } - if (recursed) - return NULL; + if (self->parents) { + GSList *it; + + for (it = self->parents; it; it = g_slist_next(it)) { + ObClient *c = it->data; + if ((c = client_search_focus_tree_full(it->data))) return c; } + + return NULL; + } + else { + /* this function checks the whole tree, the client_search_focus_tree + does not, so we need to check this window */ + if (client_focused(self)) + return self; + return client_search_focus_tree(self); } +} + +ObClient *client_search_focus_group_full(ObClient *self) +{ + GSList *it; + + if (self->group) { + for (it = self->group->members; it; it = g_slist_next(it)) { + ObClient *c = it->data; - /* this function checks the whole tree, the client_search_focus_tree~ - does not, so we need to check this window */ - if (client_focused(self)) - return self; - return client_search_focus_tree(self); + if (client_focused(c)) return c; + if ((c = client_search_focus_tree(it->data))) return c; + } + } else + if (client_focused(self)) return self; + return NULL; +} + +gboolean client_has_parent(ObClient *self) +{ + return self->parents != NULL; } 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; @@ -2320,24 +2379,25 @@ static ObStackingLayer calc_layer(ObClient *self) else l = OB_STACKING_LAYER_ABOVE; } else if ((self->fullscreen || - /* no decorations and fills the monitor = oldskool fullscreen */ - (self->frame != NULL && - (self->frame->size.right == 0 && self->frame->size.left == 0 && - self->frame->size.bottom == 0 && self->frame->size.top == 0 && - RECT_EQUAL(self->area, - *screen_physical_area_monitor - (client_monitor(self)))))) && + /* 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_focused(self) || client_search_focus_tree(self))) l = OB_STACKING_LAYER_FULLSCREEN; else if (self->above) l = OB_STACKING_LAYER_ABOVE; else if (self->below) l = OB_STACKING_LAYER_BELOW; else l = OB_STACKING_LAYER_NORMAL; + g_free(monitor); + return l; } static void client_calc_layer_recursive(ObClient *self, ObClient *orig, - ObStackingLayer min, gboolean raised) + ObStackingLayer min) { ObStackingLayer old, own; GSList *it; @@ -2346,28 +2406,14 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig, own = calc_layer(self); self->layer = MAX(own, min); + if (self->layer != old) { + stacking_remove(CLIENT_AS_WINDOW(self)); + stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); + } + for (it = self->transients; it; it = g_slist_next(it)) client_calc_layer_recursive(it->data, orig, - self->layer, - raised ? raised : self->layer > old); - - /* restack. but only if the original window is managed. - - raised is used so that only the bottom-most window in the stacking - order is raised, the others will automatically come with it. - - also only the highest windows in the stacking order (no transients) - are lowered, cuz the rest come for free - */ - if (!raised && orig->frame) { - if (self->layer > old) { - stacking_remove(CLIENT_AS_WINDOW(self)); - stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); - } else if (self->layer < old && self->transients == NULL) { - stacking_remove(CLIENT_AS_WINDOW(self)); - stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); - } - } + self->layer); } void client_calc_layer(ObClient *self) @@ -2381,7 +2427,7 @@ void client_calc_layer(ObClient *self) it = client_search_all_top_parents(self); for (; it; it = g_slist_next(it)) - client_calc_layer_recursive(it->data, orig, 0, FALSE); + client_calc_layer_recursive(it->data, orig, 0); } gboolean client_should_show(ObClient *self) @@ -2396,48 +2442,68 @@ gboolean client_should_show(ObClient *self) return FALSE; } -void client_show(ObClient *self) +gboolean client_show(ObClient *self) { + gboolean show = FALSE; if (client_should_show(self)) { frame_show(self->frame); - } + show = TRUE; - /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it - needs to be in IconicState. This includes when it is on another - desktop! - */ - client_change_wm_state(self); + /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, + it needs to be in IconicState. This includes when it is on another + desktop! + */ + client_change_wm_state(self); + } + return show; } -void client_hide(ObClient *self) +gboolean client_hide(ObClient *self) { + gboolean hide = FALSE; + if (!client_should_show(self)) { - frame_hide(self->frame); - } + if (self == focus_client) { + /* if there is a grab going on, then we need to cancel it. if we + move focus during the grab, applications will get + NotifyWhileGrabbed events and ignore them ! - /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it - needs to be in IconicState. This includes when it is on another - desktop! - */ - client_change_wm_state(self); -} + actions should not rely on being able to move focus during an + interactive grab. + */ + event_cancel_all_key_grabs(); + } -void client_showhide(ObClient *self) -{ + /* We don't need to ignore enter events here. + The window can hide/iconify in 3 different ways: + 1 - through an x message. in this case we ignore all enter events + caused by responding to the x message (unless underMouse) + 2 - by a keyboard action. in this case we ignore all enter events + caused by the action + 3 - by a mouse action. in this case they are doing stuff with the + mouse and focus _should_ move. + + Also in action_end, we simulate an enter event that can't be ignored + so trying to ignore them is futile in case 3 anyways + */ - if (client_should_show(self)) { - frame_show(self->frame); - } - else { frame_hide(self->frame); + hide = TRUE; + + /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, + it needs to be in IconicState. This includes when it is on another + desktop! + */ + client_change_wm_state(self); } + return hide; +} - /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it - needs to be in IconicState. This includes when it is on another - desktop! - */ - client_change_wm_state(self); +void client_showhide(ObClient *self) +{ + if (!client_show(self)) + client_hide(self); } gboolean client_normal(ObClient *self) { @@ -2469,69 +2535,76 @@ gboolean client_enter_focusable(ObClient *self) } -static void client_apply_startup_state(ObClient *self, gint x, gint y) +static void client_apply_startup_state(ObClient *self, + gint x, gint y, gint w, gint h) { - gboolean pos = FALSE; /* has the window's position been configured? */ - gint ox, oy; + /* save the states that we are going to apply */ + gboolean iconic = self->iconic; + gboolean fullscreen = self->fullscreen; + gboolean undecorated = self->undecorated; + gboolean shaded = self->shaded; + gboolean demands_attention = self->demands_attention; + gboolean max_horz = self->max_horz; + gboolean max_vert = self->max_vert; + Rect oldarea; + gint l; - /* save the position, and set self->area for these to use */ - ox = self->area.x; - oy = self->area.y; - self->area.x = x; - self->area.y = y; + /* turn them all off in the client, so they won't affect the window + being placed */ + self->iconic = self->fullscreen = self->undecorated = self->shaded = + self->demands_attention = self->max_horz = self->max_vert = FALSE; - /* set the desktop hint, to make sure that it always exists */ - PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop); + /* move the client to its placed position, or it it's already there, + generate a ConfigureNotify telling the client where it is. - /* these are in a carefully crafted order.. */ + do this after adjusting the frame. otherwise it gets all weird and + clients don't work right - if (self->iconic) { - self->iconic = FALSE; - client_iconify(self, TRUE, FALSE); - } - if (self->fullscreen) { - self->fullscreen = FALSE; + do this before applying the states so they have the correct + pre-max/pre-fullscreen values + */ + client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE); + ob_debug("placed window 0x%x at %d, %d with size %d x %d\n", + self->window, x, y, w, h); + /* save the area, and make it where it should be for the premax stuff */ + oldarea = self->area; + RECT_SET(self->area, x, y, w, h); + + /* apply the states. these are in a carefully crafted order.. */ + + if (iconic) + client_iconify(self, TRUE, FALSE, TRUE); + if (fullscreen) client_fullscreen(self, TRUE); - pos = TRUE; - } - if (self->undecorated) { - self->undecorated = FALSE; + if (undecorated) client_set_undecorated(self, TRUE); - } - if (self->shaded) { - self->shaded = FALSE; + if (shaded) client_shade(self, TRUE); - } - if (self->demands_attention) { - self->demands_attention = FALSE; + if (demands_attention) client_hilite(self, TRUE); - } - if (self->max_vert && self->max_horz) { - self->max_vert = self->max_horz = FALSE; + if (max_vert && max_horz) client_maximize(self, TRUE, 0); - pos = TRUE; - } else if (self->max_vert) { - self->max_vert = FALSE; + else if (max_vert) client_maximize(self, TRUE, 2); - pos = TRUE; - } else if (self->max_horz) { - self->max_horz = FALSE; + else if (max_horz) client_maximize(self, TRUE, 1); - pos = TRUE; - } - /* if the client didn't get positioned yet, then do so now. - call client_move even if the window is not being moved anywhere, because - when we reparent it and decorate it, it is getting moved and we need to - be telling it so with a ConfigureNotify event. + /* 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 + function + pho can cause this because it maps at size of the screen but not 0,0 + so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't). + then fullscreen'ing makes it go to 0,0 which it thinks it already is at + cuz thats where the pre-fullscreen will be. however the actual area is + not, so this needs to be called even if we have fullscreened/maxed */ - if (!pos) { - /* use the saved position */ - self->area.x = ox; - self->area.y = oy; - client_move(self, x, y); - } + self->area = oldarea; + client_configure(self, x, y, w, h, FALSE, TRUE, FALSE); + + /* set the desktop hint, to make sure that it always exists */ + PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop); /* nothing to do for the other states: skip_taskbar @@ -2542,34 +2615,138 @@ static void client_apply_startup_state(ObClient *self, gint x, gint y) */ } -void client_convert_gravity(ObClient *self, gint gravity, gint *x, gint *y, - gint w, gint h) +void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww) { - gint oldg = self->gravity; + /* these should be the current values. this is for when you're not moving, + just resizing */ + g_assert(*x == self->area.x); + g_assert(oldw == self->area.width); - /* get the frame's position from the requested stuff */ - self->gravity = gravity; - frame_client_gravity(self->frame, x, y, w, h); - self->gravity = oldg; + /* horizontal */ + switch (self->gravity) { + default: + case NorthWestGravity: + case WestGravity: + case SouthWestGravity: + case StaticGravity: + case ForgetGravity: + break; + case NorthGravity: + case CenterGravity: + case SouthGravity: + *x -= (neww - oldw) / 2; + break; + case NorthEastGravity: + case EastGravity: + case SouthEastGravity: + *x -= neww - oldw; + break; + } +} + +void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh) +{ + /* these should be the current values. this is for when you're not moving, + just resizing */ + g_assert(*y == self->area.y); + g_assert(oldh == self->area.height); - /* get the client's position in its true gravity from that */ - frame_frame_gravity(self->frame, x, y, w, h); + /* vertical */ + switch (self->gravity) { + default: + case NorthWestGravity: + case NorthGravity: + case NorthEastGravity: + case StaticGravity: + case ForgetGravity: + break; + case WestGravity: + case CenterGravity: + case EastGravity: + *y -= (newh - oldh) / 2; + break; + case SouthWestGravity: + case SouthGravity: + case SouthEastGravity: + *y -= newh - oldh; + break; + } } void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, gint *logicalw, gint *logicalh, gboolean user) { - Rect desired_area = {*x, *y, *w, *h}; + Rect desired = {*x, *y, *w, *h}; + frame_rect_to_frame(self->frame, &desired); /* make the frame recalculate its dimentions n shit without changing anything visible for real, this way the constraints below can work with the updated frame dimensions. */ - frame_adjust_area(self->frame, TRUE, TRUE, TRUE); + frame_adjust_area(self->frame, FALSE, TRUE, TRUE); + + /* gets the frame's position */ + frame_client_gravity(self->frame, x, y); + + /* these positions are frame positions, not client positions */ + + /* set the size and position if fullscreen */ + if (self->fullscreen) { + Rect *a; + guint i; + + i = screen_find_monitor(&desired); + a = screen_physical_area_monitor(i); + + *x = a->x; + *y = a->y; + *w = a->width; + *h = a->height; + + 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; + + /* use all possible struts when maximizing to the full screen */ + i = screen_find_monitor(&desired); + a = screen_area(self->desktop, i, + (self->max_horz && self->max_vert ? NULL : &desired)); + + /* set the size and position if maximized */ + if (self->max_horz) { + *x = a->x; + *w = a->width - self->frame->size.left - self->frame->size.right; + } + if (self->max_vert) { + *y = a->y; + *h = a->height - self->frame->size.top - self->frame->size.bottom; + } + + user = FALSE; /* ignore if the client can't be moved/resized when it + is maximizing */ + + g_free(a); + } + + /* gets the client's position */ + frame_frame_gravity(self->frame, x, y); /* work within the prefered sizes given by the window */ if (!(*w == self->area.width && *h == self->area.height)) { gint basew, baseh, minw, minh; + gint incw, inch; + 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; + minratio = self->fullscreen || (self->max_horz && self->max_vert) ? + 0 : self->min_ratio; + maxratio = self->fullscreen || (self->max_horz && self->max_vert) ? + 0 : self->max_ratio; /* base size is substituted with min size if not specified */ if (self->base_size.width || self->base_size.height) { @@ -2601,19 +2778,19 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, *h -= baseh; /* keep to the increments */ - *w /= self->size_inc.width; - *h /= self->size_inc.height; + *w /= incw; + *h /= inch; /* you cannot resize to nothing */ if (basew + *w < 1) *w = 1 - basew; if (baseh + *h < 1) *h = 1 - baseh; /* save the logical size */ - *logicalw = self->size_inc.width > 1 ? *w : *w + basew; - *logicalh = self->size_inc.height > 1 ? *h : *h + baseh; + *logicalw = incw > 1 ? *w : *w + basew; + *logicalh = inch > 1 ? *h : *h + baseh; - *w *= self->size_inc.width; - *h *= self->size_inc.height; + *w *= incw; + *h *= inch; *w += basew; *h += baseh; @@ -2623,77 +2800,31 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, *w -= self->base_size.width; *h -= self->base_size.height; - if (!self->fullscreen) { - if (self->min_ratio) - if (*h * self->min_ratio > *w) { - *h = (gint)(*w / self->min_ratio); + if (minratio) + if (*h * minratio > *w) { + *h = (gint)(*w / minratio); - /* you cannot resize to nothing */ - if (*h < 1) { - *h = 1; - *w = (gint)(*h * self->min_ratio); - } + /* you cannot resize to nothing */ + if (*h < 1) { + *h = 1; + *w = (gint)(*h * minratio); } - if (self->max_ratio) - if (*h * self->max_ratio < *w) { - *h = (gint)(*w / self->max_ratio); - - /* you cannot resize to nothing */ - if (*h < 1) { - *h = 1; - *w = (gint)(*h * self->min_ratio); - } + } + if (maxratio) + if (*h * maxratio < *w) { + *h = (gint)(*w / maxratio); + + /* you cannot resize to nothing */ + if (*h < 1) { + *h = 1; + *w = (gint)(*h * minratio); } - } + } *w += self->base_size.width; *h += self->base_size.height; } - /* gets the frame's position */ - frame_client_gravity(self->frame, x, y, *w, *h); - - /* these positions are frame positions, not client positions */ - - /* set the size and position if fullscreen */ - if (self->fullscreen) { - Rect *a; - guint i; - - i = screen_find_monitor(&desired_area); - a = screen_physical_area_monitor(i); - - *x = a->x; - *y = a->y; - *w = a->width; - *h = a->height; - - user = FALSE; /* ignore if the client can't be moved/resized when it - is entering fullscreen */ - } else if (self->max_horz || self->max_vert) { - Rect *a; - guint i; - - i = screen_find_monitor(&desired_area); - a = screen_area_monitor(self->desktop, i); - - /* set the size and position if maximized */ - if (self->max_horz) { - *x = a->x; - *w = a->width - self->frame->size.left - self->frame->size.right; - } - if (self->max_vert) { - *y = a->y; - *h = a->height - self->frame->size.top - self->frame->size.bottom; - } - - /* maximizing is not allowed if the user can't move+resize the window - */ - } - - /* gets the client's position */ - frame_frame_gravity(self->frame, x, y, *w, *h); - /* these override the above states! if you cant move you can't move! */ if (user) { if (!(self->functions & OB_CLIENT_FUNC_MOVE)) { @@ -2711,15 +2842,16 @@ void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, } -void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h, - gboolean user, gboolean final, - gboolean force_reply) +void client_configure(ObClient *self, gint x, gint y, gint w, gint h, + gboolean user, gboolean final, gboolean force_reply) { - gint oldw, oldh, oldrx, oldry; + gint oldw, oldh; gboolean send_resize_client; gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE; + gboolean fmoved, fresized; guint fdecor = self->frame->decorations; gboolean fhorz = self->frame->max_horz; + gboolean fvert = self->frame->max_vert; gint logicalw, logicalh; /* find the new x, y, width, and height (and logical size) */ @@ -2730,8 +2862,8 @@ void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h, SIZE_SET(self->logical_size, logicalw, logicalh); /* figure out if we moved or resized or what */ - moved = x != self->area.x || y != self->area.y; - resized = w != self->area.width || h != self->area.height; + 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; @@ -2745,61 +2877,97 @@ void client_configure_full(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 && user && (w > oldw || h > oldh)) { - XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh)); - /* resize the plate to show the client padding color underneath */ + if (send_resize_client && (w > oldw || h > oldh)) { + XMoveResizeWindow(ob_display, self->window, + self->frame->size.left, self->frame->size.top, + MAX(w, oldw), MAX(h, oldh)); frame_adjust_client_area(self->frame); } /* find the frame's dimensions and move/resize it */ - if (self->decorations != fdecor || self->max_horz != fhorz) - moved = resized = TRUE; - if (moved || resized) - frame_adjust_area(self->frame, moved, resized, FALSE); - - /* find the client's position relative to the root window */ - oldrx = self->root_pos.x; - oldry = self->root_pos.y; - rootmoved = (oldrx != (signed)(self->frame->area.x + - self->frame->size.left - - self->border_width) || - oldry != (signed)(self->frame->area.y + - self->frame->size.top - - self->border_width)); - - if (force_reply || ((!user || (user && final)) && rootmoved)) + fmoved = moved; + fresized = resized; + + /* if decorations changed, then readjust everything for the frame */ + if (self->decorations != fdecor || + self->max_horz != fhorz || self->max_vert != fvert) { - XEvent event; + fmoved = fresized = TRUE; + } + + /* adjust the frame */ + if (fmoved || fresized) { + gulong ignore_start; + if (!user) + ignore_start = event_start_ignore_all_enters(); + frame_adjust_area(self->frame, fmoved, fresized, FALSE); + + if (!user) + event_end_ignore_all_enters(ignore_start); + } + + if (!user || final) { + gint oldrx = self->root_pos.x; + gint oldry = self->root_pos.y; + /* we have reset the client to 0 border width, so don't include + it in these coords */ POINT_SET(self->root_pos, self->frame->area.x + self->frame->size.left - self->border_width, self->frame->area.y + self->frame->size.top - self->border_width); + if (self->root_pos.x != oldrx || self->root_pos.y != oldry) + rootmoved = TRUE; + } + + /* This is kinda tricky and should not be changed.. let me explain! + + 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) + + 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. + */ + if ((!user && !resized && (rootmoved || force_reply)) || + (user && final && rootmoved)) + { + XEvent event; event.type = ConfigureNotify; event.xconfigure.display = ob_display; event.xconfigure.event = self->window; event.xconfigure.window = self->window; + ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d\n", + self->title, self->root_pos.x, self->root_pos.y, w, h); + /* root window real coords */ event.xconfigure.x = self->root_pos.x; event.xconfigure.y = self->root_pos.y; event.xconfigure.width = w; event.xconfigure.height = h; - event.xconfigure.border_width = 0; - event.xconfigure.above = self->frame->plate; + event.xconfigure.border_width = self->border_width; + event.xconfigure.above = None; event.xconfigure.override_redirect = FALSE; XSendEvent(event.xconfigure.display, event.xconfigure.window, FALSE, StructureNotifyMask, &event); } - /* if the client is shrinking, then resize the frame before the client */ - if (send_resize_client && (!user || (w <= oldw || h <= oldh))) { - /* resize the plate to show the client padding color underneath */ - frame_adjust_client_area(self->frame); + /* if the client is shrinking, then resize the frame before the client. - XResizeWindow(ob_display, self->window, w, 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)) { + frame_adjust_client_area(self->frame); + XMoveResizeWindow(ob_display, self->window, + self->frame->size.left, self->frame->size.top, w, h); } XFlush(ob_display); @@ -2814,7 +2982,6 @@ void client_fullscreen(ObClient *self, gboolean fs) self->fullscreen = fs; client_change_state(self); /* change the state hints on the client */ - client_calc_layer(self); /* and adjust out layer/stacking */ if (fs) { self->pre_fullscreen_area = self->area; @@ -2829,40 +2996,43 @@ void client_fullscreen(ObClient *self, gboolean fs) self->pre_fullscreen_area.height = self->pre_max_area.height; } - /* these are not actually used cuz client_configure will set them - as appropriate when the window is fullscreened */ - x = y = w = h = 0; + /* these will help configure_full figure out where to fullscreen + the window */ + x = self->area.x; + y = self->area.y; + w = self->area.width; + h = self->area.height; } else { - Rect *a; - - if (self->pre_fullscreen_area.width > 0 && - self->pre_fullscreen_area.height > 0) - { - x = self->pre_fullscreen_area.x; - y = self->pre_fullscreen_area.y; - w = self->pre_fullscreen_area.width; - h = self->pre_fullscreen_area.height; - RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0); - } else { - /* pick some fallbacks... */ - a = screen_area_monitor(self->desktop, 0); - x = a->x + a->width / 4; - y = a->y + a->height / 4; - w = a->width / 2; - h = a->height / 2; - } + g_assert(self->pre_fullscreen_area.width > 0 && + self->pre_fullscreen_area.height > 0); + + x = self->pre_fullscreen_area.x; + y = self->pre_fullscreen_area.y; + w = self->pre_fullscreen_area.width; + h = self->pre_fullscreen_area.height; + RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0); } - client_setup_decor_and_functions(self); + ob_debug("Window %s going fullscreen (%d)\n", + self->title, self->fullscreen); + client_setup_decor_and_functions(self, FALSE); client_move_resize(self, x, y, w, h); - /* try focus us when we go into fullscreen mode */ - client_focus(self); + /* and adjust our layer/stacking. do this after resizing the window, + and applying decorations, because windows which fill the screen are + considered "fullscreen" and it affects their layer */ + client_calc_layer(self); + + if (fs) { + /* try focus us when we go into fullscreen mode */ + client_focus(self); + } } static void client_iconify_recursive(ObClient *self, - gboolean iconic, gboolean curdesk) + gboolean iconic, gboolean curdesk, + gboolean hide_animation) { GSList *it; gboolean changed = FALSE; @@ -2879,9 +3049,8 @@ static void client_iconify_recursive(ObClient *self, self->iconic = iconic; /* update the focus lists.. iconic windows go to the bottom of - the list, put the new iconic window at the 'top of the - bottom'. */ - focus_order_to_top(self); + the list */ + focus_order_to_bottom(self); changed = TRUE; } @@ -2890,7 +3059,7 @@ static void client_iconify_recursive(ObClient *self, if (curdesk && self->desktop != screen_desktop && self->desktop != DESKTOP_ALL) - client_set_desktop(self, screen_desktop, FALSE); + client_set_desktop(self, screen_desktop, FALSE, FALSE); /* this puts it after the current focused window */ focus_order_remove(self); @@ -2902,7 +3071,7 @@ static void client_iconify_recursive(ObClient *self, if (changed) { client_change_state(self); - if (ob_state() != OB_STATE_STARTING && config_animate_iconify) + if (config_animate_iconify && !hide_animation) frame_begin_iconify_animation(self->frame, iconic); /* do this after starting the animation so it doesn't flash */ client_showhide(self); @@ -2913,15 +3082,17 @@ static void client_iconify_recursive(ObClient *self, for (it = self->transients; it; it = g_slist_next(it)) if (it->data != self) if (client_is_direct_child(self, it->data) || !iconic) - client_iconify_recursive(it->data, iconic, curdesk); + client_iconify_recursive(it->data, iconic, curdesk, + hide_animation); } -void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk) +void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk, + gboolean hide_animation) { if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) { /* move up the transient chain as far as possible first */ - self = client_search_top_normal_parent(self); - client_iconify_recursive(self, iconic, curdesk); + self = client_search_top_direct_parent(self); + client_iconify_recursive(self, iconic, curdesk, hide_animation); } } @@ -2943,8 +3114,8 @@ void client_maximize(ObClient *self, gboolean max, gint dir) if (dir == 2 && !self->max_vert) return; } - /* we just tell it to configure in the same place and client_configure - worries about filling the screen with the window */ + /* these will help configure_full figure out which screen to fill with + the window */ x = self->area.x; y = self->area.y; w = self->area.width; @@ -2962,34 +3133,23 @@ void client_maximize(ObClient *self, gboolean max, gint dir) self->pre_max_area.width, self->area.height); } } else { - Rect *a; - - a = screen_area_monitor(self->desktop, 0); if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */ - if (self->pre_max_area.width > 0) { - x = self->pre_max_area.x; - w = self->pre_max_area.width; + g_assert(self->pre_max_area.width > 0); - RECT_SET(self->pre_max_area, 0, self->pre_max_area.y, - 0, self->pre_max_area.height); - } else { - /* pick some fallbacks... */ - x = a->x + a->width / 4; - w = a->width / 2; - } + x = self->pre_max_area.x; + w = self->pre_max_area.width; + + RECT_SET(self->pre_max_area, 0, self->pre_max_area.y, + 0, self->pre_max_area.height); } if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */ - if (self->pre_max_area.height > 0) { - y = self->pre_max_area.y; - h = self->pre_max_area.height; + g_assert(self->pre_max_area.height > 0); - RECT_SET(self->pre_max_area, self->pre_max_area.x, 0, - self->pre_max_area.width, 0); - } else { - /* pick some fallbacks... */ - y = a->y + a->height / 4; - h = a->height / 2; - } + y = self->pre_max_area.y; + h = self->pre_max_area.height; + + RECT_SET(self->pre_max_area, self->pre_max_area.x, 0, + self->pre_max_area.width, 0); } } @@ -3000,8 +3160,7 @@ void client_maximize(ObClient *self, gboolean max, gint dir) client_change_state(self); /* change the state hints on the client */ - client_setup_decor_and_functions(self); - + client_setup_decor_and_functions(self, FALSE); client_move_resize(self, x, y, w, h); } @@ -3015,7 +3174,7 @@ void client_shade(ObClient *self, gboolean shade) client_change_state(self); client_change_wm_state(self); /* the window is being hidden/shown */ /* resize the frame to just the titlebar */ - frame_adjust_area(self->frame, FALSE, FALSE, FALSE); + frame_adjust_area(self->frame, FALSE, TRUE, FALSE); } void client_close(ObClient *self) @@ -3073,12 +3232,13 @@ void client_hilite(ObClient *self, gboolean hilite) void client_set_desktop_recursive(ObClient *self, guint target, - gboolean donthide) + gboolean donthide, + gboolean dontraise) { guint old; GSList *it; - if (target != self->desktop) { + if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) { ob_debug("Setting desktop %u\n", target+1); @@ -3091,33 +3251,37 @@ void client_set_desktop_recursive(ObClient *self, frame_adjust_state(self->frame); /* 'move' the window to the new desktop */ if (!donthide) - client_showhide(self); + client_hide(self); + client_show(self); /* raise if it was not already on the desktop */ - if (old != DESKTOP_ALL) + if (old != DESKTOP_ALL && !dontraise) stacking_raise(CLIENT_AS_WINDOW(self)); if (STRUT_EXISTS(self->strut)) screen_update_areas(); + else + /* the new desktop's geometry may be different, so we may need to + resize, for example if we are maximized */ + client_reconfigure(self, FALSE); } /* move all transients */ for (it = self->transients; it; it = g_slist_next(it)) if (it->data != self) if (client_is_direct_child(self, it->data)) - client_set_desktop_recursive(it->data, target, donthide); + client_set_desktop_recursive(it->data, target, + donthide, dontraise); } void client_set_desktop(ObClient *self, guint target, - gboolean donthide) + gboolean donthide, gboolean dontraise) { - self = client_search_top_normal_parent(self); - client_set_desktop_recursive(self, target, donthide); + self = client_search_top_direct_parent(self); + client_set_desktop_recursive(self, target, donthide, dontraise); } gboolean client_is_direct_child(ObClient *parent, ObClient *child) { - while (child != parent && - child->transient_for && child->transient_for != OB_TRAN_GROUP) - child = child->transient_for; + while (child != parent && (child = client_direct_parent(child))); return child == parent; } @@ -3155,10 +3319,10 @@ void client_set_wm_state(ObClient *self, glong state) switch (state) { case IconicState: - client_iconify(self, TRUE, TRUE); + client_iconify(self, TRUE, TRUE, FALSE); break; case NormalState: - client_iconify(self, FALSE, TRUE); + client_iconify(self, FALSE, TRUE, FALSE); break; } } @@ -3228,7 +3392,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) action = self->demands_attention ? prop_atoms.net_wm_state_remove : prop_atoms.net_wm_state_add; - else if (state == prop_atoms.openbox_wm_state_undecorated) + else if (state == prop_atoms.ob_wm_state_undecorated) action = undecorated ? prop_atoms.net_wm_state_remove : prop_atoms.net_wm_state_add; } @@ -3258,7 +3422,7 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) below = TRUE; } else if (state == prop_atoms.net_wm_state_demands_attention) { demands_attention = TRUE; - } else if (state == prop_atoms.openbox_wm_state_undecorated) { + } else if (state == prop_atoms.ob_wm_state_undecorated) { undecorated = TRUE; } @@ -3285,11 +3449,12 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) below = FALSE; } else if (state == prop_atoms.net_wm_state_demands_attention) { demands_attention = FALSE; - } else if (state == prop_atoms.openbox_wm_state_undecorated) { + } else if (state == prop_atoms.ob_wm_state_undecorated) { undecorated = FALSE; } } } + if (max_horz != self->max_horz || max_vert != self->max_vert) { if (max_horz != self->max_horz && max_vert != self->max_vert) { /* toggling both */ @@ -3315,24 +3480,30 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2) client_shade(self, shaded); if (undecorated != self->undecorated) client_set_undecorated(self, undecorated); + if (above != self->above || below != self->below) { + self->above = above; + self->below = below; + client_calc_layer(self); + } + if (modal != self->modal) { self->modal = modal; /* when a window changes modality, then its stacking order with its transients needs to change */ stacking_raise(CLIENT_AS_WINDOW(self)); + + /* it also may get focused. if something is focused that shouldn't + be focused anymore, then move the focus */ + if (focus_client && client_focus_target(focus_client) != focus_client) + client_focus(focus_client); } + if (iconic != self->iconic) - client_iconify(self, iconic, FALSE); + client_iconify(self, iconic, FALSE, FALSE); if (demands_attention != self->demands_attention) client_hilite(self, demands_attention); - if (above != self->above || below != self->below) { - self->above = above; - self->below = below; - client_calc_layer(self); - } - client_change_state(self); /* change the hint to reflect these changes */ } @@ -3347,8 +3518,6 @@ ObClient *client_focus_target(ObClient *self) gboolean client_can_focus(ObClient *self) { - XEvent ev; - /* choose the correct target */ self = client_focus_target(self); @@ -3358,51 +3527,49 @@ gboolean client_can_focus(ObClient *self) if (!(self->can_focus || self->focus_notify)) return FALSE; - /* do a check to see if the window has already been unmapped or destroyed - do this intelligently while watching out for unmaps we've generated - (ignore_unmaps > 0) */ - if (XCheckTypedWindowEvent(ob_display, self->window, - DestroyNotify, &ev)) { - XPutBackEvent(ob_display, &ev); - return FALSE; - } - while (XCheckTypedWindowEvent(ob_display, self->window, - UnmapNotify, &ev)) { - if (self->ignore_unmaps) { - self->ignore_unmaps--; - } else { - XPutBackEvent(ob_display, &ev); - return FALSE; - } - } - return TRUE; } gboolean client_focus(ObClient *self) { + /* 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); + /* choose the correct target */ self = client_focus_target(self); if (!client_can_focus(self)) { - if (!self->frame->visible) { - /* update the focus lists */ - focus_order_to_top(self); - } + ob_debug_type(OB_DEBUG_FOCUS, + "Client %s can't be focused\n", self->title); return FALSE; } ob_debug_type(OB_DEBUG_FOCUS, - "Focusing client \"%s\" at time %u\n", - self->title, event_curtime); + "Focusing client \"%s\" (0x%x) at time %u\n", + self->title, self->window, event_curtime); + + /* if using focus_delay, stop the timer now so that focus doesn't + go moving on us */ + event_halt_focus_delay(); + + /* if there is a grab going on, then we need to cancel it. if we move + focus during the grab, applications will get NotifyWhileGrabbed events + and ignore them ! + + actions should not rely on being able to move focus during an + interactive grab. + */ + event_cancel_all_key_grabs(); + + xerror_set_ignore(TRUE); + xerror_occured = FALSE; if (self->can_focus) { /* This can cause a BadMatch error with CurrentTime, or if an app passed in a bad time for _NET_WM_ACTIVE_WINDOW. */ - xerror_set_ignore(TRUE); XSetInputFocus(ob_display, self->window, RevertToPointerRoot, event_curtime); - xerror_set_ignore(FALSE); } if (self->focus_notify) { @@ -3420,46 +3587,31 @@ gboolean client_focus(ObClient *self) XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce); } -#ifdef DEBUG_FOCUS - ob_debug("%sively focusing %lx at %d\n", - (self->can_focus ? "act" : "pass"), - self->window, (gint) event_curtime); -#endif + xerror_set_ignore(FALSE); - /* Cause the FocusIn to come back to us. Important for desktop switches, - since otherwise we'll have no FocusIn on the queue and send it off to - the focus_backup. */ - XSync(ob_display, FALSE); - return TRUE; + ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d\n", xerror_occured); + return !xerror_occured; } -/*! Present the client to the user. - @param raise If the client should be raised or not. You should only set - raise to false if you don't care if the window is completely - hidden. -*/ -static void client_present(ObClient *self, gboolean here, gboolean raise) +static void client_present(ObClient *self, gboolean here, gboolean raise, + gboolean unshade) { - /* if using focus_delay, stop the timer now so that focus doesn't - go moving on us */ - event_halt_focus_delay(); - if (client_normal(self) && screen_showing_desktop) - screen_show_desktop(FALSE, FALSE); + screen_show_desktop(FALSE, self); if (self->iconic) - client_iconify(self, FALSE, here); + client_iconify(self, FALSE, here, FALSE); if (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop) { if (here) - client_set_desktop(self, screen_desktop, FALSE); + client_set_desktop(self, screen_desktop, FALSE, TRUE); else screen_set_desktop(self->desktop, FALSE); } else if (!self->frame->visible) /* if its not visible for other reasons, then don't mess with it */ return; - if (self->shaded) + if (self->shaded && unshade) client_shade(self, FALSE); if (raise) stacking_raise(CLIENT_AS_WINDOW(self)); @@ -3467,22 +3619,25 @@ static void client_present(ObClient *self, gboolean here, gboolean raise) client_focus(self); } -void client_activate(ObClient *self, gboolean here, gboolean user) +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 request came from the user, or if nothing is focused, then grant - the request. - if the currently focused app doesn't set a user_time, then it can't + /* 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 (user || !focus_client || !last_time) + 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_curtime && event_time_after(event_curtime, last_time); + 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), " @@ -3490,34 +3645,45 @@ void client_activate(ObClient *self, gboolean here, gboolean user) self->window, event_curtime, last_time, (user ? "user" : "application"), allow); - if (allow) { - if (event_curtime != CurrentTime) - self->user_time = event_curtime; - - client_present(self, here, TRUE); - } else + if (allow) + client_present(self, here, raise, unshade); + else /* don't focus it but tell the user it wants attention */ client_hilite(self, TRUE); } -static void client_bring_helper_windows_recursive(ObClient *self, - guint desktop) +static void client_bring_windows_recursive(ObClient *self, + guint desktop, + gboolean helpers, + gboolean modals, + gboolean iconic) { GSList *it; for (it = self->transients; it; it = g_slist_next(it)) - client_bring_helper_windows_recursive(it->data, desktop); + client_bring_windows_recursive(it->data, desktop, + helpers, modals, iconic); - if (client_helper(self) && - self->desktop != desktop && self->desktop != DESKTOP_ALL) + if (((helpers && client_helper(self)) || + (modals && self->modal)) && + ((self->desktop != desktop && self->desktop != DESKTOP_ALL) || + (iconic && self->iconic))) { - client_set_desktop(self, desktop, FALSE); + if (iconic && self->iconic) + client_iconify(self, FALSE, TRUE, FALSE); + else + client_set_desktop(self, desktop, FALSE, FALSE); } } void client_bring_helper_windows(ObClient *self) { - client_bring_helper_windows_recursive(self, self->desktop); + client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE); +} + +void client_bring_modal_windows(ObClient *self) +{ + client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE); } gboolean client_focused(ObClient *self) @@ -3528,45 +3694,38 @@ gboolean client_focused(ObClient *self) static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h) { guint i; - /* si is the smallest image >= req */ - /* li is the largest image < req */ - gulong size, smallest = 0xffffffff, largest = 0, si = 0, li = 0; + gulong min_diff, min_i; if (!self->nicons) { ObClientIcon *parent = NULL; + GSList *it; - if (self->transient_for) { - if (self->transient_for != OB_TRAN_GROUP) - parent = client_icon_recursive(self->transient_for, w, h); - else { - GSList *it; - for (it = self->group->members; it; it = g_slist_next(it)) { - ObClient *c = it->data; - if (c != self && !c->transient_for) { - if ((parent = client_icon_recursive(c, w, h))) - break; - } - } - } + for (it = self->parents; it; it = g_slist_next(it)) { + ObClient *c = it->data; + if ((parent = client_icon_recursive(c, w, h))) + break; } return parent; } - for (i = 0; i < self->nicons; ++i) { - size = self->icons[i].width * self->icons[i].height; - if (size < smallest && size >= (unsigned)(w * h)) { - smallest = size; - si = i; - } - if (size > largest && size <= (unsigned)(w * h)) { - largest = size; - li = i; + /* some kind of crappy approximation to find the icon closest in size to + what we requested, but icons are generally all the same ratio as + eachother so it's good enough. */ + + min_diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h); + min_i = 0; + + for (i = 1; i < self->nicons; ++i) { + gulong diff; + + diff = ABS(self->icons[i].width - w) + ABS(self->icons[i].height - h); + if (diff < min_diff) { + min_diff = diff; + min_i = i; } } - if (largest == 0) /* didnt find one smaller than the requested size */ - return &self->icons[si]; - return &self->icons[li]; + return &self->icons[min_i]; } const ObClientIcon* client_icon(ObClient *self, gint w, gint h) @@ -3599,16 +3758,13 @@ void client_set_layer(ObClient *self, gint layer) void client_set_undecorated(ObClient *self, gboolean undecorated) { - if (self->undecorated != undecorated) { + if (self->undecorated != undecorated && + /* don't let it undecorate if the function is missing, but let + it redecorate */ + (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated)) + { self->undecorated = undecorated; - client_setup_decor_and_functions(self); - /* Make sure the client knows it might have moved. Maybe there is a - * better way of doing this so only one client_configure is sent, but - * since 125 of these are sent per second when moving the window (with - * user = FALSE) i doubt it matters much. - */ - client_configure(self, self->area.x, self->area.y, - self->area.width, self->area.height, TRUE, TRUE); + client_setup_decor_and_functions(self, TRUE); client_change_state(self); /* reflect this in the state hints */ } } @@ -3618,11 +3774,17 @@ guint client_monitor(ObClient *self) return screen_find_monitor(&self->frame->area); } -ObClient *client_search_top_normal_parent(ObClient *self) +ObClient *client_direct_parent(ObClient *self) { - while (self->transient_for && self->transient_for != OB_TRAN_GROUP && - client_normal(self->transient_for)) - self = self->transient_for; + if (!self->parents) return NULL; + if (self->transient_for_group) return NULL; + return self->parents->data; +} + +ObClient *client_search_top_direct_parent(ObClient *self) +{ + ObClient *p; + while ((p = client_direct_parent(self))) self = p; return self; } @@ -3630,34 +3792,18 @@ static GSList *client_search_all_top_parents_internal(ObClient *self, gboolean bylayer, ObStackingLayer layer) { - GSList *ret = NULL; + GSList *ret; + ObClient *p; /* move up the direct transient chain as far as possible */ - while (self->transient_for && self->transient_for != OB_TRAN_GROUP && - (!bylayer || self->transient_for->layer == layer) && - client_normal(self->transient_for)) - self = self->transient_for; - - if (!self->transient_for) - ret = g_slist_prepend(ret, self); - else { - GSList *it; - - g_assert(self->group); - - for (it = self->group->members; it; it = g_slist_next(it)) { - ObClient *c = it->data; - - if (!c->transient_for && client_normal(c) && - (!bylayer || c->layer == layer)) - { - ret = g_slist_prepend(ret, c); - } - } + while ((p = client_direct_parent(self)) && + (!bylayer || p->layer == layer)) + self = p; - if (ret == NULL) /* no group parents */ - ret = g_slist_prepend(ret, self); - } + if (!self->parents) + ret = g_slist_prepend(NULL, self); + else + ret = g_slist_copy(self->parents); return ret; } @@ -3674,46 +3820,20 @@ GSList *client_search_all_top_parents_layer(ObClient *self) ObClient *client_search_focus_parent(ObClient *self) { - if (self->transient_for) { - if (self->transient_for != OB_TRAN_GROUP) { - if (client_focused(self->transient_for)) - return self->transient_for; - } else { - GSList *it; - - for (it = self->group->members; it; it = g_slist_next(it)) { - ObClient *c = it->data; + GSList *it; - /* checking transient_for prevents infinate loops! */ - if (c != self && !c->transient_for) - if (client_focused(c)) - return c; - } - } - } + for (it = self->parents; it; it = g_slist_next(it)) + if (client_focused(it->data)) return it->data; return NULL; } ObClient *client_search_parent(ObClient *self, ObClient *search) { - if (self->transient_for) { - if (self->transient_for != OB_TRAN_GROUP) { - if (self->transient_for == search) - return search; - } else { - GSList *it; - - for (it = self->group->members; it; it = g_slist_next(it)) { - ObClient *c = it->data; + GSList *it; - /* checking transient_for prevents infinate loops! */ - if (c != self && !c->transient_for) - if (c == search) - return search; - } - } - } + for (it = self->parents; it; it = g_slist_next(it)) + if (it->data == search) return search; return NULL; } @@ -3731,188 +3851,309 @@ ObClient *client_search_transient(ObClient *self, ObClient *search) return NULL; } -#define WANT_EDGE(cur, c) \ - if(cur == c) \ - continue; \ - if(!client_normal(cur)) \ - continue; \ - if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \ - continue; \ - if(cur->iconic) \ - continue; \ - if(cur->layer < c->layer && !config_resist_layers_below) \ - continue; - -#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) -{ - gint dest, monitor_dest; - gint my_edge_start, my_edge_end, my_offset; - GList *it; - Rect *a, *monitor; - - if(!client_list) - return -1; - - a = screen_area(c->desktop); - monitor = screen_area_monitor(c->desktop, client_monitor(c)); +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: - 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 = monitor->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; + 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(); + } - 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; + /* do we collide with this window? */ + if (!RANGES_INTERSECT(my_edge_start, my_edge_size, + edge_start, edge_size)) + return; - WANT_EDGE(cur, c) + 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_EAST: + head = RECT_LEFT(area); + tail = RECT_RIGHT(area); + break; + case OB_DIRECTION_WEST: + head = RECT_RIGHT(area); + tail = RECT_LEFT(area); + break; + default: + g_assert_not_reached(); + } + switch(dir) { + case OB_DIRECTION_NORTH: + case OB_DIRECTION_WEST: + if (my_head <= head + 1) + skip_head = TRUE; + if (my_head + my_size - 1 <= tail) + skip_tail = TRUE; + if (head < *dest) + skip_head = TRUE; + if (tail - my_size < *dest) + skip_tail = TRUE; + break; + case OB_DIRECTION_SOUTH: + case OB_DIRECTION_EAST: + if (my_head >= head - 1) + skip_head = TRUE; + if (my_head - my_size + 1 >= tail) + skip_tail = TRUE; + if (head > *dest) + skip_head = TRUE; + if (tail + my_size > *dest) + skip_tail = TRUE; + break; + default: + g_assert_not_reached(); + } - 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); + 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; + } - if(his_offset + 1 > my_offset) - continue; +} - if(his_offset < dest) - continue; +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) +{ + GList *it; + Rect *a, *mon; + gint edge; - HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) - } + 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) { + case OB_DIRECTION_NORTH: + if (my_head >= RECT_TOP(*mon)) + 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); - - /* default: bottom of screen */ - dest = a->y + a->height - (hang ? c->frame->area.height : 0); - monitor_dest = monitor->y + monitor->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; + if (my_head <= RECT_BOTTOM(*mon)) + edge = RECT_BOTTOM(*mon) + 1; + else + edge = RECT_BOTTOM(*a) + 1; + break; + case OB_DIRECTION_EAST: + if (my_head <= RECT_RIGHT(*mon)) + edge = RECT_RIGHT(*mon) + 1; + else + edge = RECT_RIGHT(*a) + 1; + break; + case OB_DIRECTION_WEST: + if (my_head >= RECT_LEFT(*mon)) + 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; - 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; + for(it = client_list; it; it = g_list_next(it)) { + ObClient *cur = it->data; - WANT_EDGE(cur, c) + /* 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; - 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); + ob_debug("trying window %s\n", cur->title); + detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start, + my_edge_size, dest, near_edge); + } + detect_edge(dock->area, dir, my_head, my_size, my_edge_start, + my_edge_size, dest, near_edge); +} - if(his_offset - 1 < my_offset) - continue; - - if(his_offset > dest) - continue; +void client_find_move_directional(ObClient *self, ObDirection dir, + gint *x, gint *y) +{ + gint head, size; + gint e, e_start, e_size; + gboolean near; - HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) - } + 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: - 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 = monitor->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); + 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(); + } - if(his_offset + 1 > my_offset) - continue; + 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: + 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 = monitor->x + monitor->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; + 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; + default: + g_assert_not_reached(); + } - HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) - } + 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; - 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 */ } - return dest; + 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() @@ -3926,6 +4167,11 @@ ObClient* client_under_pointer() if (WINDOW_IS_CLIENT(it->data)) { ObClient *c = WINDOW_AS_CLIENT(it->data); if (c->frame->visible && + /* check the desktop, this is done during desktop + switching and windows are shown/hidden status is not + reliable */ + (c->desktop == screen_desktop || + c->desktop == DESKTOP_ALL) && /* ignore all animating windows */ !frame_iconify_animating(c->frame) && RECT_CONTAINS(c->frame->area, x, y))