X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=c90c39bf7cceefa6cc658e38c828a0dd0d203189;hb=ff78d28f8bcdbbdd42fe9501ee81185f67a4166a;hp=1931d87eaf755e0fc6f4db2b0ce6565e133b7b55;hpb=dacc9dac7a8232180fb824fe2eea7d026def99c9;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 1931d87e..c90c39bf 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -40,11 +40,16 @@ #include "keyboard.h" #include "mouse.h" #include "render/render.h" +#include "gettext.h" #ifdef HAVE_UNISTD_H # include #endif +#ifdef HAVE_SIGNAL_H +# include /* for kill() */ +#endif + #include #include @@ -93,6 +98,7 @@ static GSList *client_search_all_top_parents_internal(ObClient *self, gboolean bylayer, ObStackingLayer layer); static void client_call_notifies(ObClient *self, GSList *list); +static void client_ping_event(ObClient *self, gboolean dead); void client_startup(gboolean reconfig) @@ -140,7 +146,7 @@ void client_remove_destroy_notify(ObClientCallback func) } } -void client_set_list() +void client_set_list(void) { Window *windows, *win_it; GList *it; @@ -164,7 +170,7 @@ void client_set_list() stacking_set_list(); } -void client_manage_all() +void client_manage_all(void) { guint i, j, nchild; Window w, *children; @@ -214,7 +220,7 @@ void client_manage(Window window) ObAppSettings *settings; gboolean transient = FALSE; Rect place, *monitor; - Time map_time; + Time launch_time, map_time; grab_server(TRUE); @@ -253,6 +259,8 @@ void client_manage(Window window) ob_debug("Managing window: 0x%lx\n", window); + map_time = event_get_server_time(); + /* choose the events we want to receive on the CLIENT window */ attrib_set.event_mask = CLIENT_EVENTMASK; attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK; @@ -300,7 +308,7 @@ void client_manage(Window window) client_setup_decor_and_functions(self, FALSE); /* tell startup notification that this app started */ - map_time = sn_app_started(self->startup_id, self->class); + launch_time = sn_app_started(self->startup_id, self->class); /* do this after we have a frame.. it uses the frame to help determine the WM_STATE to apply. */ @@ -448,9 +456,18 @@ void client_manage(Window window) g_free(monitor); monitor = NULL; + ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s\n", + activate ? "yes" : "no"); if (activate) { gboolean raise = FALSE; + /* This is focus stealing prevention */ + ob_debug_type(OB_DEBUG_FOCUS, + "Want to focus new window 0x%x at time %u " + "launched at %u (last user interaction time %u)\n", + self->window, map_time, launch_time, + event_last_user_time); + if (menu_frame_visible || moveresize_in_progress) { activate = FALSE; raise = TRUE; @@ -462,10 +479,10 @@ void client_manage(Window window) /* if it's on another desktop */ else if (!(self->desktop == screen_desktop || - self->desktop == DESKTOP_ALL) && - /* the timestamp is from before you changed desktops */ - map_time && screen_desktop_user_time && - !event_time_after(map_time, screen_desktop_user_time)) + self->desktop == DESKTOP_ALL) && + /* the timestamp is from before you changed desktops */ + launch_time && screen_desktop_user_time && + !event_time_after(launch_time, screen_desktop_user_time)) { activate = FALSE; raise = TRUE; @@ -477,11 +494,21 @@ void client_manage(Window window) else if (focus_client && client_search_focus_tree_full(self) == NULL && client_search_focus_group_full(self) == NULL) { - /* 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) */ - if (client_has_parent(self)) { + /* If the user is working in another window right now, then don't + steal focus */ + if (event_last_user_time && launch_time && + event_time_after(event_last_user_time, launch_time) && + event_last_user_time != launch_time && + event_time_after(event_last_user_time, + map_time - OB_EVENT_USER_TIME_DELAY)) + { + activate = FALSE; + ob_debug_type(OB_DEBUG_FOCUS, + "Not focusing the window because the user is " + "working in another window\n"); + } + /* If its a transient (and its parents aren't focused) */ + else if (client_has_parent(self)) { activate = FALSE; ob_debug_type(OB_DEBUG_FOCUS, "Not focusing the window because it is a " @@ -507,9 +534,22 @@ void client_manage(Window window) "Not focusing the window because another window " "would get the focus anyway\n"); } + else if (!(self->desktop == screen_desktop || + self->desktop == DESKTOP_ALL)) + { + activate = FALSE; + raise = TRUE; + ob_debug_type(OB_DEBUG_FOCUS, + "Not focusing the window because it is on " + "another desktop and no relatives are focused "); + } } if (!activate) { + ob_debug_type(OB_DEBUG_FOCUS, + "Focus stealing prevention activated for %s at " + "time %u (last user interactioon time %u)\n", + self->title, map_time, event_last_user_time); /* if the client isn't focused, then hilite it so the user knows it is there */ client_hilite(self, TRUE); @@ -562,6 +602,12 @@ void client_manage(Window window) /* update the list hints */ client_set_list(); + /* watch for when the application stops responding. only do this for + normal windows, i.e. windows which have titlebars and close buttons + and things like that */ + if (self->ping && client_normal(self)) + ping_start(self, client_ping_event); + /* free the ObAppSettings shallow copy */ g_free(settings); @@ -605,7 +651,7 @@ ObClient *client_fake_manage(Window window) return self; } -void client_unmanage_all() +void client_unmanage_all(void) { while (client_list != NULL) client_unmanage(client_list->data); @@ -643,6 +689,10 @@ void client_unmanage(ObClient *self) /* remove the window from our save set */ XChangeSaveSet(ob_display, self->window, SetModeDelete); + /* stop pinging the window */ + if (self->ping && client_normal(self)) + ping_stop(self); + /* update the focus lists */ focus_order_remove(self); if (client_focused(self)) { @@ -728,6 +778,11 @@ void client_unmanage(ObClient *self) XMapWindow(ob_display, self->window); } + /* these should not be left on the window ever. other window managers + don't necessarily use them and it will mess them up (like compiz) */ + PROP_ERASE(self->window, net_wm_visible_name); + PROP_ERASE(self->window, net_wm_visible_icon_name); + /* update the list hints */ client_set_list(); @@ -1468,6 +1523,10 @@ void client_update_protocols(ObClient *self) /* if this protocol is requested, then the window will be notified whenever we want it to receive focus */ self->focus_notify = TRUE; + else if (proto[i] == prop_atoms.net_wm_ping) + /* if this protocol is requested, then the window will allow + pings to determine if it is still alive */ + self->ping = TRUE; #ifdef SYNC else if (proto[i] == prop_atoms.net_wm_sync_request) /* if this protocol is requested, then resizing the @@ -1602,11 +1661,16 @@ void client_setup_decor_and_functions(ObClient *self, gboolean reconfig) switch (self->type) { case OB_CLIENT_TYPE_NORMAL: /* normal windows retain all of the possible decorations and - functionality, and are the only windows that you can fullscreen */ + functionality, and can be fullscreen */ self->functions |= OB_CLIENT_FUNC_FULLSCREEN; break; case OB_CLIENT_TYPE_DIALOG: + /* sometimes apps make dialog windows fullscreen for some reason (for + e.g. kpdf does this..) */ + self->functions |= OB_CLIENT_FUNC_FULLSCREEN; + break; + case OB_CLIENT_TYPE_UTILITY: /* these windows don't have anything added or removed by default */ break; @@ -1899,6 +1963,15 @@ void client_update_title(ObClient *self) } else visible = data; + if (self->not_responding) { + data = visible; + if (self->close_tried_term) + visible = g_strdup_printf("%s - [%s]", data, _("Killing...")); + else + visible = g_strdup_printf("%s - [%s]", data, _("Not Responding")); + g_free(data); + } + PROP_SETS(self->window, net_wm_visible_name, visible); self->title = visible; @@ -1922,6 +1995,15 @@ void client_update_title(ObClient *self) } else visible = data; + if (self->not_responding) { + data = visible; + if (self->close_tried_term) + visible = g_strdup_printf("%s - [%s]", data, _("Killing...")); + else + visible = g_strdup_printf("%s - [%s]", data, _("Not Responding")); + g_free(data); + } + PROP_SETS(self->window, net_wm_visible_icon_name, visible); self->icon_title = visible; } @@ -2182,6 +2264,7 @@ static void client_get_session_ids(ObClient *self) if (got) { gchar localhost[128]; + guint32 pid; gethostname(localhost, 127); localhost[127] = '\0'; @@ -2189,6 +2272,11 @@ static void client_get_session_ids(ObClient *self) self->client_machine = s; else g_free(s); + + /* see if it has the PID set too (the PID requires that the + WM_CLIENT_MACHINE be set) */ + if (PROP_GET32(self->window, net_wm_pid, cardinal, &pid)) + self->pid = pid; } } @@ -3118,41 +3206,58 @@ void client_shade(ObClient *self, gboolean shade) frame_adjust_area(self->frame, FALSE, TRUE, FALSE); } -void client_close(ObClient *self) +static void client_ping_event(ObClient *self, gboolean dead) { - XEvent ce; + self->not_responding = dead; + client_update_title(self); + + if (!dead) { + /* try kill it nicely the first time again, if it started responding + at some point */ + self->close_tried_term = FALSE; + } +} +void client_close(ObClient *self) +{ if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return; /* in the case that the client provides no means to requesting that it close, we just kill it */ if (!self->delete_window) + /* don't use client_kill(), we should only kill based on PID in + response to a lack of PING replies */ + XKillClient(ob_display, self->window); + else if (self->not_responding) client_kill(self); - - /* - XXX: itd be cool to do timeouts and shit here for killing the client's - process off - like... if the window is around after 5 seconds, then the close button - turns a nice red, and if this function is called again, the client is - explicitly killed. - */ - - ce.xclient.type = ClientMessage; - ce.xclient.message_type = prop_atoms.wm_protocols; - ce.xclient.display = ob_display; - ce.xclient.window = self->window; - ce.xclient.format = 32; - ce.xclient.data.l[0] = prop_atoms.wm_delete_window; - ce.xclient.data.l[1] = event_curtime; - ce.xclient.data.l[2] = 0l; - ce.xclient.data.l[3] = 0l; - ce.xclient.data.l[4] = 0l; - XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce); + else + /* request the client to close with WM_DELETE_WINDOW */ + PROP_MSG_TO(self->window, self->window, wm_protocols, + prop_atoms.wm_delete_window, event_curtime, 0, 0, 0, + NoEventMask); } void client_kill(ObClient *self) { - XKillClient(ob_display, self->window); + if (!self->client_machine && self->pid) { + /* running on the local host */ + if (!self->close_tried_term) { + ob_debug("killing window 0x%x with pid %lu, with SIGTERM\n", + self->window, self->pid); + kill(self->pid, SIGTERM); + self->close_tried_term = TRUE; + + /* show that we're trying to kill it */ + client_update_title(self); + } + else { + ob_debug("killing window 0x%x with pid %lu, with SIGKILL\n", + self->window, self->pid); + kill(self->pid, SIGKILL); /* kill -9 */ + } + } + else + XKillClient(ob_display, self->window); } void client_hilite(ObClient *self, gboolean hilite) @@ -3773,7 +3878,7 @@ static void detect_edge(Rect area, ObDirection dir, gint edge_start, edge_size, head, tail; gboolean skip_head = FALSE, skip_tail = FALSE; - switch(dir) { + switch (dir) { case OB_DIRECTION_NORTH: case OB_DIRECTION_SOUTH: edge_start = area.x; @@ -3793,7 +3898,7 @@ static void detect_edge(Rect area, ObDirection dir, edge_start, edge_size)) return; - switch(dir) { + switch (dir) { case OB_DIRECTION_NORTH: head = RECT_BOTTOM(area); tail = RECT_TOP(area); @@ -3802,38 +3907,54 @@ static void detect_edge(Rect area, ObDirection dir, 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; + case OB_DIRECTION_EAST: + head = RECT_LEFT(area); + tail = RECT_RIGHT(area); + break; default: g_assert_not_reached(); } - switch(dir) { + switch (dir) { case OB_DIRECTION_NORTH: case OB_DIRECTION_WEST: + /* check if our window is past the head of this window */ if (my_head <= head + 1) skip_head = TRUE; + /* check if our window's tail is past the tail of this window */ if (my_head + my_size - 1 <= tail) skip_tail = TRUE; - if (head < *dest) + /* check if the head of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a tail, not a head) */ + if (head + (*near_edge ? 0 : my_size) < *dest) skip_head = TRUE; - if (tail - my_size < *dest) + /* check if the tail of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a head, not a tail) */ + if (tail - (!*near_edge ? 0 : my_size) < *dest) skip_tail = TRUE; break; case OB_DIRECTION_SOUTH: case OB_DIRECTION_EAST: + /* check if our window is past the head of this window */ if (my_head >= head - 1) skip_head = TRUE; + /* check if our window's tail is past the tail of this window */ if (my_head - my_size + 1 >= tail) skip_tail = TRUE; - if (head > *dest) + /* check if the head of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a tail, not a head) */ + if (head - (*near_edge ? 0 : my_size) > *dest) skip_head = TRUE; - if (tail + my_size > *dest) + /* check if the tail of this window is closer than the previously + chosen edge (take into account that the previously chosen + edge might have been a head, not a tail) */ + if (tail + (!*near_edge ? 0 : my_size) > *dest) skip_tail = TRUE; break; default: @@ -3852,7 +3973,6 @@ static void detect_edge(Rect area, ObDirection dir, *dest = tail; *near_edge = FALSE; } - } void client_find_edge_directional(ObClient *self, ObDirection dir, @@ -3870,7 +3990,7 @@ void client_find_edge_directional(ObClient *self, ObDirection dir, mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &self->frame->area); - switch(dir) { + switch (dir) { case OB_DIRECTION_NORTH: if (my_head >= RECT_TOP(*mon) + 1) edge = RECT_TOP(*mon) - 1; @@ -3902,7 +4022,7 @@ void client_find_edge_directional(ObClient *self, ObDirection dir, *dest = edge; *near_edge = TRUE; - for(it = client_list; it; it = g_list_next(it)) { + for (it = client_list; it; it = g_list_next(it)) { ObClient *cur = it->data; /* skip windows to not bump into */ @@ -3922,6 +4042,8 @@ void client_find_edge_directional(ObClient *self, ObDirection dir, dock_get_area(&dock_area); detect_edge(dock_area, dir, my_head, my_size, my_edge_start, my_edge_size, dest, near_edge); + g_free(a); + g_free(mon); } void client_find_move_directional(ObClient *self, ObDirection dir, @@ -4072,7 +4194,7 @@ void client_find_resize_directional(ObClient *self, ObDirection side, *h -= self->frame->size.top + self->frame->size.bottom; } -ObClient* client_under_pointer() +ObClient* client_under_pointer(void) { gint x, y; GList *it;