#include "xerror.h"
#include "screen.h"
#include "moveresize.h"
+#include "ping.h"
#include "place.h"
#include "prop.h"
#include "extensions.h"
#include "keyboard.h"
#include "mouse.h"
#include "render/render.h"
+#include "gettext.h"
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
+#ifdef HAVE_SIGNAL_H
+# include <signal.h> /* for kill() */
+#endif
+
#include <glib.h>
#include <X11/Xutil.h>
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_set_desktop_recursive(ObClient *self,
+ guint target,
+ gboolean donthide,
+ gboolean dontraise);
static void client_change_allowed_actions(ObClient *self);
static void client_change_state(ObClient *self);
static void client_change_wm_state(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)
}
}
-void client_set_list()
+void client_set_list(void)
{
Window *windows, *win_it;
GList *it;
stacking_set_list();
}
-void client_manage_all()
+void client_manage_all(void)
{
guint i, j, nchild;
Window w, *children;
client_setup_decor_and_functions(self, FALSE);
/* tell startup notification that this app started */
- launch_time = sn_app_started(self->startup_id, self->class);
+ launch_time = sn_app_started(self->startup_id, self->class, self->name);
/* do this after we have a frame.. it uses the frame to help determine the
WM_STATE to apply. */
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;
/* 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.
+ we don't need to stop pinging on unmanage, because it will be handled
+ automatically by the destroy callback!
+ */
+ if (self->ping && client_normal(self))
+ ping_start(self, client_ping_event);
+
/* free the ObAppSettings shallow copy */
g_free(settings);
return self;
}
-void client_unmanage_all()
+void client_unmanage_all(void)
{
while (client_list != NULL)
client_unmanage(client_list->data);
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();
g_free(self->icons[j].data);
if (self->nicons > 0)
g_free(self->icons);
+ g_free(self->startup_id);
g_free(self->wm_command);
g_free(self->title);
g_free(self->icon_title);
/* 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
}
#endif
-void client_get_colormap(ObClient *self)
+static void client_get_colormap(ObClient *self)
{
XWindowAttributes wa;
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;
} 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;
} 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;
}
}
}
+/* Avoid storing icons above this size if possible */
+#define AVOID_ABOVE 64
+
void client_update_icons(ObClient *self)
{
guint num;
guint32 *data;
guint w, h, i, j;
+ guint num_seen; /* number of icons present */
+ guint num_small_seen; /* number of icons small enough present */
+ guint smallest, smallest_area;
for (i = 0; i < self->nicons; ++i)
g_free(self->icons[i].data);
if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
/* figure out how many valid icons are in here */
i = 0;
- while (num - i > 2) {
- w = data[i++];
- h = data[i++];
- i += w * h;
- if (i > num || w*h == 0) break;
- ++self->nicons;
- }
+ num_seen = num_small_seen = 0;
+ smallest = smallest_area = 0;
+ if (num > 2)
+ while (i < num) {
+ w = data[i++];
+ h = data[i++];
+ i += w * h;
+ /* watch for it being too small for the specified size, or for
+ zero sized icons. */
+ if (i > num || w == 0 || h == 0) break;
+
+ if (!smallest_area || w*h < smallest_area) {
+ smallest = num_seen;
+ smallest_area = w*h;
+ }
+ ++num_seen;
+ if (w <= AVOID_ABOVE && h <= AVOID_ABOVE)
+ ++num_small_seen;
+ }
+ if (num_small_seen > 0)
+ self->nicons = num_small_seen;
+ else if (num_seen)
+ self->nicons = 1;
self->icons = g_new(ObClientIcon, self->nicons);
/* store the icons */
i = 0;
- for (j = 0; j < self->nicons; ++j) {
+ for (j = 0; j < self->nicons;) {
guint x, y, t;
w = self->icons[j].width = data[i++];
h = self->icons[j].height = data[i++];
- if (w*h == 0) continue;
+ /* if there are some icons smaller than the threshold, we're
+ skipping all the ones above */
+ if (num_small_seen > 0) {
+ if (w > AVOID_ABOVE || h > AVOID_ABOVE) {
+ i += w*h;
+ continue;
+ }
+ }
+ /* if there were no icons smaller than the threshold, then we are
+ only taking the smallest available one we saw */
+ else if (j != smallest) {
+ i += w*h;
+ continue;
+ }
self->icons[j].data = g_new(RrPixel32, w * h);
for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
(((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
}
g_assert(i <= num);
+
+ ++j;
}
g_free(data);
RECT_SET(self->icon_geometry, 0, 0, 0, 0);
- if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num)
- && num == 4)
+ if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num))
{
- /* don't let them set it with an area < 0 */
- RECT_SET(self->icon_geometry, data[0], data[1],
- MAX(data[2],0), MAX(data[3],0));
+ if (num == 4)
+ /* don't let them set it with an area < 0 */
+ RECT_SET(self->icon_geometry, data[0], data[1],
+ MAX(data[2],0), MAX(data[3],0));
+ g_free(data);
}
}
if (got) {
gchar localhost[128];
+ guint32 pid;
gethostname(localhost, 127);
localhost[127] = '\0';
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;
}
}
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)
}
}
-void client_set_desktop_recursive(ObClient *self,
- guint target,
- gboolean donthide,
- gboolean dontraise)
+static void client_set_desktop_recursive(ObClient *self,
+ guint target,
+ gboolean donthide,
+ gboolean dontraise)
{
guint old;
GSList *it;
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;
edge_start, edge_size))
return;
- switch(dir) {
+ switch (dir) {
case OB_DIRECTION_NORTH:
head = RECT_BOTTOM(area);
tail = RECT_TOP(area);
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:
*dest = tail;
*near_edge = FALSE;
}
-
}
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;
*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 */
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,
*h -= self->frame->size.top + self->frame->size.bottom;
}
-ObClient* client_under_pointer()
+ObClient* client_under_pointer(void)
{
gint x, y;
GList *it;