all repos — openbox @ fb7a71da202632c7301ada67c8b4420bfb8d8fbe

openbox fork - make it a bit more like ryudo

can tell when a window that was "closed" has stopped responding now
Dana Jansens danakj@orodu.net
commit

fb7a71da202632c7301ada67c8b4420bfb8d8fbe

parent

d790dc162d24cfdcc236114b50c51af62612646a

M Makefile.amMakefile.am

@@ -243,6 +243,8 @@ openbox/moveresize.h \

openbox/mwm.h \ openbox/openbox.c \ openbox/openbox.h \ + openbox/ping.c \ + openbox/ping.h \ openbox/place.c \ openbox/place.h \ openbox/popup.c \
M openbox/client.copenbox/client.c

@@ -3166,10 +3166,16 @@ /* resize the frame to just the titlebar */

frame_adjust_area(self->frame, FALSE, TRUE, FALSE); } -void client_close(ObClient *self) +static void client_ping_event(ObClient *self, gboolean dead) { - XEvent ce; + if (dead) + ob_debug("client 0x%x window 0x%x is not responding !!\n"); + else + ob_debug("client 0x%x window 0x%x started responding again..\n"); +} +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

@@ -3185,17 +3191,11 @@ 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); + PROP_MSG_TO(self->window, self->window, wm_protocols, + prop_atoms.wm_delete_window, event_curtime, 0, 0, 0, + NoEventMask); + + ping_start(self, client_ping_event); } void client_kill(ObClient *self)
M openbox/event.copenbox/event.c

@@ -767,6 +767,9 @@ else if (e->xclient.data.l[0] == 2)

ob_restart(); else if (e->xclient.data.l[0] == 3) ob_exit(0); + } else if (msgtype == prop_atoms.wm_protocols) { + if (e->xclient.data.l[0] == prop_atoms.net_wm_ping) + ping_got_pong(e->xclient.data.l[1]); } break; case PropertyNotify:
M openbox/mainloop.copenbox/mainloop.c

@@ -560,7 +560,9 @@ GSList *it;

for (it = loop->timers; it; it = g_slist_next(it)) { ObMainLoopTimer *t = it->data; - if (t->func == handler && t->equal(t->data, data)) { + if (t->func == handler && + (t->equal ? t->equal(t->data, data) : (t->data == data))) + { t->del_me = TRUE; if (cancel_dest) t->destroy = NULL;
A openbox/ping.c

@@ -0,0 +1,151 @@

+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#include "ping.h" +#include "client.h" +#include "prop.h" +#include "event.h" +#include "mainloop.h" +#include "openbox.h" + +typedef struct _ObPingTarget +{ + ObClient *client; + ObPingEventHandler h; + Time sent; + gint waiting; +} ObPingTarget; + +static GSList *ping_targets = NULL; +static gboolean active = FALSE; + +#define PING_TIMEOUT (G_USEC_PER_SEC * 1) +/*! Warn the user after this many PING_TIMEOUT intervals */ +#define PING_TIMEOUT_WARN 3 + +static void ping_send(ObPingTarget *t); +static void ping_end(ObClient *client, gpointer data); +static gboolean ping_timeout(gpointer data); + +void ping_start(struct _ObClient *client, ObPingEventHandler h) +{ + GSList *it; + ObPingTarget *t; + + /* make sure we're not already pinging it */ + for (it = ping_targets; it != NULL; it = g_slist_next(it)) { + t = it->data; + if (t->client == client) return; + } + + t = g_new(ObPingTarget, 1); + t->client = client; + t->h = h; + t->waiting = 1; /* first wait for a reply */ + + ping_send(t); + ping_targets = g_slist_prepend(ping_targets, t); + ob_main_loop_timeout_add(ob_main_loop, PING_TIMEOUT, ping_timeout, + t, NULL, NULL); + + if (!active) { + active = TRUE; + /* listen for the client to disappear */ + client_add_destroy_notify(ping_end, NULL); + } +} + +void ping_stop(struct _ObClient *c) +{ + ping_end(c, NULL); +} + +void ping_got_pong(Time timestamp) +{ + GSList *it; + ObPingTarget *t; + + /* make sure we're not already pinging it */ + for (it = ping_targets; it != NULL; it = g_slist_next(it)) { + t = it->data; + if (t->sent == timestamp) { + ob_debug("Got PONG with timestamp %lu\n", timestamp); + if (t->waiting > PING_TIMEOUT_WARN) { + /* we had notified that they weren't responding, so now we + need to notify that they are again */ + t->h(t->client, FALSE); + } + t->waiting = 0; /* not waiting for a reply anymore */ + break; + } + } + + if (it == NULL) + ob_debug("Got PONG with timestamp %lu but not waiting for one\n", + timestamp); +} + +static void ping_send(ObPingTarget *t) +{ + t->sent = event_get_server_time(); + ob_debug("PINGing client 0x%x at %lu\n", t->client->window, t->sent); + PROP_MSG_TO(t->client->window, t->client->window, wm_protocols, + prop_atoms.net_wm_ping, t->sent, t->client->window, 0, 0, + NoEventMask); +} + +static gboolean ping_timeout(gpointer data) +{ + ObPingTarget *t = data; + + if (t->waiting == 0) { /* got a reply already */ + /* send another ping to make sure it's still alive */ + ping_send(t); + } + + if (t->waiting == PING_TIMEOUT_WARN) + t->h(t->client, TRUE); /* notify that the client isn't responding */ + + ++t->waiting; + + return TRUE; /* repeat */ +} + +static void ping_end(ObClient *client, gpointer data) +{ + GSList *it; + ObPingTarget *t; + + for (it = ping_targets; it != NULL; it = g_slist_next(it)) { + t = it->data; + if (t->client == client) { + ping_targets = g_slist_remove_link(ping_targets, it); + ob_main_loop_timeout_remove_data(ob_main_loop, ping_timeout, t, + FALSE); + g_free(t); + break; + } + } + + /* stop listening if we're not waiting for any more pings */ + if (!ping_targets) { + active = TRUE; + client_remove_destroy_notify(ping_end); + } +}
A openbox/ping.h

@@ -0,0 +1,41 @@

+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- + + client.h for the Openbox window manager + Copyright (c) 2006 Mikael Magnusson + Copyright (c) 2003-2008 Dana Jansens + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + See the COPYING file for a copy of the GNU General Public License. +*/ + +#ifndef __ping_h +#define __ping_h + +#include <X11/Xlib.h> +#include <glib.h> + +struct _ObClient; + +/*! + Notifies when the client application isn't responding to pings, or when it + starts responding again. + @param dead TRUE if the app isn't responding, FALSE if it starts responding + again +*/ +typedef void (*ObPingEventHandler) (struct _ObClient *c, gboolean dead); + +void ping_start(struct _ObClient *c, ObPingEventHandler h); +void ping_stop(struct _ObClient *c); + +void ping_got_pong(Time timestamp); + +#endif
M openbox/prop.copenbox/prop.c

@@ -447,6 +447,14 @@

void prop_message(Window about, Atom messagetype, glong data0, glong data1, glong data2, glong data3, glong mask) { + prop_message_to(RootWindow(ob_display, ob_screen), about, messagetype, + data0, data1, data2, data3, 0, mask); +} + +void prop_message_to(Window to, Window about, Atom messagetype, + glong data0, glong data1, glong data2, + glong data3, glong data4, glong mask) +{ XEvent ce; ce.xclient.type = ClientMessage; ce.xclient.message_type = messagetype;

@@ -457,7 +465,6 @@ ce.xclient.data.l[0] = data0;

ce.xclient.data.l[1] = data1; ce.xclient.data.l[2] = data2; ce.xclient.data.l[3] = data3; - ce.xclient.data.l[4] = 0; - XSendEvent(ob_display, RootWindow(ob_display, ob_screen), FALSE, - mask, &ce); + ce.xclient.data.l[4] = data4; + XSendEvent(ob_display, to, FALSE, mask, &ce); }
M openbox/prop.hopenbox/prop.h

@@ -218,6 +218,9 @@ void prop_erase(Window win, Atom prop);

void prop_message(Window about, Atom messagetype, glong data0, glong data1, glong data2, glong data3, glong mask); +void prop_message_to(Window to, Window about, Atom messagetype, + glong data0, glong data1, glong data2, + glong data3, glong data4, glong mask); #define PROP_GET32(win, prop, type, ret) \ (prop_get32(win, prop_atoms.prop, prop_atoms.type, ret))

@@ -243,5 +246,10 @@

#define PROP_MSG(about, msgtype, data0, data1, data2, data3) \ (prop_message(about, prop_atoms.msgtype, data0, data1, data2, data3, \ SubstructureNotifyMask | SubstructureRedirectMask)) + +#define PROP_MSG_TO(to, about, msgtype, data0, data1, data2, data3, data4, \ + mask) \ + (prop_message_to(to, about, prop_atoms.msgtype, \ + data0, data1, data2, data3, data4, mask)) #endif