X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=6b8635be8eaa9d69c40930f6dca485ce4cf18e01;hb=55d2916c1e24e021d9b9692d2373dc4afff4c5c2;hp=e801ac1858d2990e37a0de577c0d67e3ec71ba05;hpb=6e7abeb86074fd4ea778cfa1480ef6aae7bc4eec;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index e801ac18..6b8635be 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -41,11 +41,16 @@ #include "mouse.h" #include "render/render.h" +#ifdef HAVE_UNISTD_H +# include +#endif + #include #include /*! The event mask to grab on client windows */ -#define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask) +#define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \ + ColormapChangeMask) #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \ ButtonMotionMask) @@ -70,6 +75,8 @@ static void client_get_layer(ObClient *self); static void client_get_shaped(ObClient *self); static void client_get_mwm_hints(ObClient *self); static void client_get_gravity(ObClient *self); +static void client_get_client_machine(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); @@ -77,7 +84,6 @@ static void client_apply_startup_state(ObClient *self, gint x, gint y); static void client_restore_session_state(ObClient *self); static void client_restore_session_stacking(ObClient *self); static ObAppSettings *client_get_settings_state(ObClient *self); -static void client_unfocus(ObClient *self); void client_startup(gboolean reconfig) { @@ -314,6 +320,7 @@ void client_manage(Window window) /* focus the new window? */ if (ob_state() != OB_STATE_STARTING && + !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)) && @@ -326,37 +333,6 @@ void client_manage(Window window) self->type == OB_CLIENT_TYPE_DIALOG)) { activate = TRUE; -#if 0 - if (self->desktop != screen_desktop) { - /* activate the window */ - activate = TRUE; - } else { - gboolean group_foc = FALSE; - - if (self->group) { - GSList *it; - - for (it = self->group->members; it; it = g_slist_next(it)) - { - if (client_focused(it->data)) - { - group_foc = TRUE; - break; - } - } - } - if ((group_foc || - (!self->transient_for && (!self->group || - !self->group->members->next))) || - client_search_focus_tree_full(self) || - !focus_client || - !client_normal(focus_client)) - { - /* activate the window */ - activate = TRUE; - } - } -#endif } /* get the current position */ @@ -398,7 +374,6 @@ void client_manage(Window window) self->window, newx, newy, self->area.width, self->area.height); client_apply_startup_state(self, newx, newy); - keyboard_grab_for_client(self, TRUE); mouse_grab_for_client(self, TRUE); if (activate) { @@ -409,10 +384,18 @@ void client_manage(Window window) ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n", self->window, self->user_time, last_time); - /* If a nothing at all, or a parent was focused, then focus this + /* if it's on another desktop */ + if (!(self->desktop == screen_desktop || self->desktop == DESKTOP_ALL) + && /* the timestamp is from before you changed desktops */ + self->user_time && screen_desktop_user_time && + !event_time_after(self->user_time, screen_desktop_user_time)) + { + activate = FALSE; + } + /* If nothing is focused, or a parent was focused, then focus this always */ - if (!focus_client || client_search_focus_parent(self) != NULL) + else if (!focus_client || client_search_focus_parent(self) != NULL) activate = TRUE; else { @@ -429,13 +412,7 @@ void client_manage(Window window) activate = FALSE; } - if (activate) - { - /* since focus can change the stacking orders, if we focus the - window then the standard raise it gets is not enough, we need - to queue one for after the focus change takes place */ - client_raise(self); - } else { + if (!activate) { ob_debug("Focus stealing prevention activated for %s with time %u " "(last time %u)\n", self->title, self->user_time, last_time); @@ -465,17 +442,8 @@ void client_manage(Window window) 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) { - /* if using focus_delay, stop the timer now so that focus doesn't - go moving on us */ - event_halt_focus_delay(); - client_focus(self); - } - - /* client_activate does this but we aren't using it so we have to do it - here as well */ - if (screen_showing_desktop) - screen_show_desktop(FALSE); + if (activate) + client_activate(self, FALSE, TRUE); /* add to client list/map */ client_list = g_list_append(client_list, self); @@ -507,38 +475,42 @@ void client_unmanage(ObClient *self) g_assert(self != NULL); - /* update the focus lists */ - focus_order_remove(self); - - if (focus_client == self) { - XEvent e; - - /* focus the last focused window on the desktop, and ignore enter - events from the unmap so it doesnt mess with the focus */ - while (XCheckTypedEvent(ob_display, EnterNotify, &e)); - /* remove these flags so we don't end up getting focused in the - fallback! */ - self->can_focus = FALSE; - self->focus_notify = FALSE; - self->modal = FALSE; - client_unfocus(self); - } - - /* potentially fix focusLast */ - if (config_focus_last) - grab_pointer(TRUE, OB_CURSOR_NONE); + /* we dont want events no more. do this before hiding the frame so we + don't generate more events */ + XSelectInput(ob_display, self->window, NoEventMask); frame_hide(self->frame); + /* flush to send the hide to the server quickly */ XFlush(ob_display); - keyboard_grab_for_client(self, FALSE); + /* ignore enter events from the unmap so it doesnt mess with the focus */ + event_ignore_queued_enters(); + mouse_grab_for_client(self, FALSE); /* remove the window from our save set */ XChangeSaveSet(ob_display, self->window, SetModeDelete); - /* we dont want events no more */ - XSelectInput(ob_display, self->window, NoEventMask); + /* update the focus lists */ + focus_order_remove(self); + if (client_focused(self)) { + /* we have to fall back here because we might not get a focus out. + 1. we need to xselectinput off the window before we unmap it because + otherwise we end up getting unmapnotifies we don't want and they + can mess up mapping it again quickly + 2. this means that if we unmanage from a synthetic unmapnotify, we + are the ones unmapped it, and causing the focusout. so we won't + get the focusout event. + 3. we can't handle focusin events on the root window because they + come from all screens, so the focus change gets lost + + if this ever gets removed in the future MAKE SURE to replace it + with: + /- don't leave an invalid focus_client -/ + focus_client = NULL; + */ + focus_fallback(FALSE); + } client_list = g_list_remove(client_list, self); stacking_remove(self); @@ -634,14 +606,12 @@ void client_unmanage(ObClient *self) g_free(self->name); g_free(self->class); g_free(self->role); + g_free(self->client_machine); g_free(self->sm_client_id); g_free(self); /* update the list hints */ client_set_list(); - - if (config_focus_last) - grab_pointer(FALSE, OB_CURSOR_NONE); } static ObAppSettings *client_get_settings_state(ObClient *self) @@ -686,7 +656,7 @@ static ObAppSettings *client_get_settings_state(ObClient *self) if (settings->max_vert != -1) self->max_vert = !!settings->max_vert; if (settings->max_horz != -1) - self->max_vert = !!settings->max_horz; + self->max_horz = !!settings->max_horz; if (settings->fullscreen != -1) self->fullscreen = !!settings->fullscreen; @@ -784,8 +754,10 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, Rect *a; gint ox = *x, oy = *y; - frame_client_gravity(self->frame, x, y); /* get where the frame - would be */ + /* XXX figure out if it is on screen now, and be rude if it is */ + + /* get where the frame would be */ + frame_client_gravity(self->frame, x, y, w, h); /* XXX watch for xinerama dead areas */ /* This makes sure windows aren't entirely outside of the screen so you @@ -834,8 +806,8 @@ gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h, } } - frame_frame_gravity(self->frame, x, y); /* get where the client - should be */ + /* get where the client should be */ + frame_frame_gravity(self->frame, x, y, w, h); return ox != *x || oy != *y; } @@ -946,6 +918,11 @@ static void client_get_all(ObClient *self) (min/max sizes), so we're ready to set up the decorations/functions */ client_setup_decor_and_functions(self); +#ifdef SYNC + client_update_sync_request_counter(self); +#endif + client_get_client_machine(self); + client_get_colormap(self); client_update_title(self); client_update_class(self); client_update_sm_client_id(self); @@ -1017,10 +994,6 @@ static void client_get_desktop(ObClient *self) self->desktop = screen_desktop; } } - if (self->desktop != d) { - /* set the desktop hint, to make sure that it always exists */ - PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop); - } } static void client_get_layer(ObClient *self) @@ -1178,7 +1151,8 @@ void client_update_transient_for(ObClient *self) /* remove from old parents */ for (it = self->group->members; it; it = g_slist_next(it)) { ObClient *c = it->data; - if (c != self && !c->transient_for) + if (c != self && (!c->transient_for || + c->transient_for != OB_TRAN_GROUP)) c->transients = g_slist_remove(c->transients, self); } } else if (self->transient_for != NULL) { /* transient of window */ @@ -1193,7 +1167,8 @@ void client_update_transient_for(ObClient *self) /* add to new parents */ for (it = self->group->members; it; it = g_slist_next(it)) { ObClient *c = it->data; - if (c != self && !c->transient_for) + if (c != self && (!c->transient_for || + c->transient_for != OB_TRAN_GROUP)) c->transients = g_slist_append(c->transients, self); } @@ -1295,18 +1270,37 @@ void client_update_protocols(ObClient *self) if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) { for (i = 0; i < num_return; ++i) { - if (proto[i] == prop_atoms.wm_delete_window) { + if (proto[i] == prop_atoms.wm_delete_window) /* this means we can request the window to close */ self->delete_window = TRUE; - } else if (proto[i] == prop_atoms.wm_take_focus) + else if (proto[i] == prop_atoms.wm_take_focus) /* if this protocol is requested, then the window will be notified whenever we want it to receive focus */ self->focus_notify = TRUE; +#ifdef SYNC + else if (proto[i] == prop_atoms.net_wm_sync_request) + /* if this protocol is requested, then resizing the + window will be synchronized between the frame and the + client */ + self->sync_request = TRUE; +#endif } g_free(proto); } } +#ifdef SYNC +void client_update_sync_request_counter(ObClient *self) +{ + guint32 i; + + if (PROP_GET32(self->window, net_wm_sync_request_counter, cardinal, &i)) { + self->sync_counter = i; + } else + self->sync_counter = None; +} +#endif + static void client_get_gravity(ObClient *self) { XWindowAttributes wattrib; @@ -1317,6 +1311,19 @@ static void client_get_gravity(ObClient *self) self->gravity = wattrib.win_gravity; } +void client_get_colormap(ObClient *self) +{ + XWindowAttributes wa; + + if (XGetWindowAttributes(ob_display, self->window, &wa)) + client_update_colormap(self, wa.colormap); +} + +void client_update_colormap(ObClient *self, Colormap colormap) +{ + self->colormap = colormap; +} + void client_update_normal_hints(ObClient *self) { XSizeHints size; @@ -1346,9 +1353,9 @@ void client_update_normal_hints(ObClient *self) if (self->frame && self->gravity != oldgravity) { /* move our idea of the client's position based on its new gravity */ - self->area.x = self->frame->area.x; - self->area.y = self->frame->area.y; - frame_frame_gravity(self->frame, &self->area.x, &self->area.y); + client_convert_gravity(self, oldgravity, + &self->area.x, &self->area.y, + self->area.width, self->area.height); } } @@ -1567,7 +1574,7 @@ 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, OB_CORNER_TOPLEFT, self->area.x, self->area.y, + client_configure(self, self->area.x, self->area.y, self->area.width, self->area.height, FALSE, TRUE); } @@ -1654,6 +1661,7 @@ void client_update_wmhints(ObClient *self) void client_update_title(ObClient *self) { gchar *data = NULL; + gchar *visible = NULL; g_free(self->title); @@ -1673,8 +1681,14 @@ void client_update_title(ObClient *self) } } - PROP_SETS(self->window, net_wm_visible_name, data); - self->title = data; + 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_name, visible); + self->title = visible; if (self->frame) frame_adjust_title(self->frame); @@ -1850,7 +1864,26 @@ void client_update_icons(ObClient *self) } } - if (self->frame) + /* 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) { + RrPixel32 *icon = ob_rr_theme->def_win_icon; + gulong *data; + + data = g_new(gulong, 48*48+2); + data[0] = data[1] = 48; + for (i = 0; i < 48*48; ++i) + data[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) + + (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) + + (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) + + (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0); + PROP_SETA32(self->window, net_wm_icon, cardinal, data, 48*48+2); + g_free(data); + } else if (self->frame) + /* don't draw the icon empty if we're just setting one now anyways, + we'll get the property change any second */ frame_adjust_icon(self->frame); } @@ -1873,6 +1906,21 @@ void client_update_user_time(ObClient *self) } } +static void client_get_client_machine(ObClient *self) +{ + gchar *data = NULL; + gchar localhost[128]; + + g_free(self->client_machine); + + if (PROP_GETS(self->window, wm_client_machine, locale, &data)) { + gethostname(localhost, 127); + localhost[127] = '\0'; + if (strcmp(localhost, data)) + self->client_machine = data; + } +} + static void client_change_wm_state(ObClient *self) { gulong state[2]; @@ -2117,6 +2165,9 @@ static void client_apply_startup_state(ObClient *self, gint x, gint y) self->area.x = x; self->area.y = y; + /* set the desktop hint, to make sure that it always exists */ + PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop); + /* these are in a carefully crafted order.. */ if (self->iconic) { @@ -2176,8 +2227,21 @@ static void client_apply_startup_state(ObClient *self, gint x, gint y) */ } -void client_try_configure(ObClient *self, ObCorner anchor, - gint *x, gint *y, gint *w, gint *h, +void client_convert_gravity(ObClient *self, gint gravity, gint *x, gint *y, + gint w, gint h) +{ + gint oldg = self->gravity; + + /* get the frame's position from the requested stuff */ + self->gravity = gravity; + frame_client_gravity(self->frame, x, y, w, h); + self->gravity = oldg; + + /* get the client's position in its true gravity from that */ + frame_frame_gravity(self->frame, x, y, w, h); +} + +void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h, gint *logicalw, gint *logicalh, gboolean user) { @@ -2272,7 +2336,7 @@ void client_try_configure(ObClient *self, ObCorner anchor, } /* gets the frame's position */ - frame_client_gravity(self->frame, x, y); + frame_client_gravity(self->frame, x, y, *w, *h); /* these positions are frame positions, not client positions */ @@ -2313,7 +2377,7 @@ void client_try_configure(ObClient *self, ObCorner anchor, } /* gets the client's position */ - frame_frame_gravity(self->frame, x, y); + frame_frame_gravity(self->frame, x, y, *w, *h); /* these override the above states! if you cant move you can't move! */ if (user) { @@ -2329,26 +2393,10 @@ void client_try_configure(ObClient *self, ObCorner anchor, g_assert(*w > 0); g_assert(*h > 0); - - switch (anchor) { - case OB_CORNER_TOPLEFT: - break; - case OB_CORNER_TOPRIGHT: - *x -= *w - self->area.width; - break; - case OB_CORNER_BOTTOMLEFT: - *y -= *h - self->area.height; - break; - case OB_CORNER_BOTTOMRIGHT: - *x -= *w - self->area.width; - *y -= *h - self->area.height; - break; - } } -void client_configure_full(ObClient *self, ObCorner anchor, - 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) { @@ -2360,8 +2408,7 @@ void client_configure_full(ObClient *self, ObCorner anchor, gint logicalw, logicalh; /* find the new x, y, width, and height (and logical size) */ - client_try_configure(self, anchor, &x, &y, &w, &h, - &logicalw, &logicalh, user); + client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user); /* set the logical size if things changed */ if (!(w == self->area.width && h == self->area.height)) @@ -3054,19 +3101,6 @@ gboolean client_focus(ObClient *self) return TRUE; } -/* Used when the current client is closed or otherwise hidden, focus_last will - then prevent focus from going to the mouse pointer -*/ -static void client_unfocus(ObClient *self) -{ - if (focus_client == self) { -#ifdef DEBUG_FOCUS - ob_debug("client_unfocus for %lx\n", self->window); -#endif - focus_fallback(FALSE); - } -} - void client_activate(ObClient *self, gboolean here, gboolean user) { guint32 last_time = focus_client ? focus_client->user_time : CurrentTime; @@ -3075,16 +3109,24 @@ void client_activate(ObClient *self, gboolean here, gboolean user) to activate it or not (a parent or group member is currently active)? */ - ob_debug("Want to activate window 0x%x with time %u (last time %u), " - "source=%s\n", - self->window, event_curtime, last_time, - (user ? "user" : "application")); + ob_debug_type(OB_DEBUG_FOCUS, + "Want to activate window 0x%x with time %u (last time %u), " + "source=%s\n", + self->window, event_curtime, last_time, + (user ? "user" : "application")); if (!user && event_curtime && last_time && !event_time_after(event_curtime, last_time)) { client_hilite(self, TRUE); } else { + if (event_curtime != CurrentTime) + self->user_time = event_curtime; + + /* 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); if (self->iconic) @@ -3104,11 +3146,11 @@ void client_activate(ObClient *self, gboolean here, gboolean user) client_focus(self); - /* we do this an action here. this is rather important. this is because - we want the results from the focus change to take place BEFORE we go - about raising the window. when a fullscreen window loses focus, we - need this or else the raise wont be able to raise above the - to-lose-focus fullscreen window. */ + /* we do this as an action here. this is rather important. this is + because we want the results from the focus change to take place + BEFORE we go about raising the window. when a fullscreen window + loses focus, we need this or else the raise wont be able to raise + above the to-lose-focus fullscreen window. */ client_raise(self); } } @@ -3185,107 +3227,6 @@ const ObClientIcon* client_icon(ObClient *self, gint w, gint h) return ret; } -/* this be mostly ripped from fvwm */ -ObClient *client_find_directional(ObClient *c, ObDirection dir) -{ - gint my_cx, my_cy, his_cx, his_cy; - gint offset = 0; - gint distance = 0; - gint score, best_score; - ObClient *best_client, *cur; - GList *it; - - if(!client_list) - return NULL; - - /* first, find the centre coords of the currently focused window */ - my_cx = c->frame->area.x + c->frame->area.width / 2; - my_cy = c->frame->area.y + c->frame->area.height / 2; - - best_score = -1; - best_client = NULL; - - for(it = g_list_first(client_list); it; it = g_list_next(it)) { - cur = it->data; - - /* the currently selected window isn't interesting */ - if(cur == c) - continue; - if (!client_normal(cur)) - continue; - /* using c->desktop instead of screen_desktop doesn't work if the - * current window was omnipresent, hope this doesn't have any other - * side effects */ - if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) - continue; - if(cur->iconic) - continue; - if(!(client_focus_target(cur) == cur && - client_can_focus(cur))) - continue; - - /* find the centre coords of this window, from the - * currently focused window's point of view */ - his_cx = (cur->frame->area.x - my_cx) - + cur->frame->area.width / 2; - his_cy = (cur->frame->area.y - my_cy) - + cur->frame->area.height / 2; - - if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST || - dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) { - gint tx; - /* Rotate the diagonals 45 degrees counterclockwise. - * To do this, multiply the matrix /+h +h\ with the - * vector (x y). \-h +h/ - * h = sqrt(0.5). We can set h := 1 since absolute - * distance doesn't matter here. */ - tx = his_cx + his_cy; - his_cy = -his_cx + his_cy; - his_cx = tx; - } - - switch(dir) { - case OB_DIRECTION_NORTH: - case OB_DIRECTION_SOUTH: - case OB_DIRECTION_NORTHEAST: - case OB_DIRECTION_SOUTHWEST: - offset = (his_cx < 0) ? -his_cx : his_cx; - distance = ((dir == OB_DIRECTION_NORTH || - dir == OB_DIRECTION_NORTHEAST) ? - -his_cy : his_cy); - break; - case OB_DIRECTION_EAST: - case OB_DIRECTION_WEST: - case OB_DIRECTION_SOUTHEAST: - case OB_DIRECTION_NORTHWEST: - offset = (his_cy < 0) ? -his_cy : his_cy; - distance = ((dir == OB_DIRECTION_WEST || - dir == OB_DIRECTION_NORTHWEST) ? - -his_cx : his_cx); - break; - } - - /* the target must be in the requested direction */ - if(distance <= 0) - continue; - - /* Calculate score for this window. The smaller the better. */ - score = distance + offset; - - /* windows more than 45 degrees off the direction are - * heavily penalized and will only be chosen if nothing - * else within a million pixels */ - if(offset > distance) - score += 1000000; - - if(best_score == -1 || score < best_score) - best_client = cur, - best_score = score; - } - - return best_client; -} - void client_set_layer(ObClient *self, gint layer) { if (layer < 0) { @@ -3311,7 +3252,7 @@ void client_set_undecorated(ObClient *self, gboolean undecorated) * since 125 of these are sent per second when moving the window (with * user = FALSE) i doubt it matters much. */ - client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y, + client_configure(self, self->area.x, self->area.y, self->area.width, self->area.height, TRUE, TRUE); client_change_state(self); /* reflect this in the state hints */ } @@ -3324,7 +3265,8 @@ guint client_monitor(ObClient *self) ObClient *client_search_top_parent(ObClient *self) { - while (self->transient_for && self->transient_for != OB_TRAN_GROUP) + while (self->transient_for && self->transient_for != OB_TRAN_GROUP && + client_normal(self)) self = self->transient_for; return self; }