From fb7a71da202632c7301ada67c8b4420bfb8d8fbe Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Tue, 15 Jan 2008 21:40:15 -0500 Subject: [PATCH] can tell when a window that was "closed" has stopped responding now --- Makefile.am | 2 + openbox/client.c | 26 ++++---- openbox/event.c | 3 + openbox/mainloop.c | 4 +- openbox/ping.c | 151 +++++++++++++++++++++++++++++++++++++++++++++ openbox/ping.h | 41 ++++++++++++ openbox/prop.c | 13 +++- openbox/prop.h | 8 +++ 8 files changed, 231 insertions(+), 17 deletions(-) create mode 100644 openbox/ping.c create mode 100644 openbox/ping.h diff --git a/Makefile.am b/Makefile.am index 34ee07e4..6b474a65 100644 --- a/Makefile.am +++ b/Makefile.am @@ -243,6 +243,8 @@ openbox_openbox_SOURCES = \ openbox/mwm.h \ openbox/openbox.c \ openbox/openbox.h \ + openbox/ping.c \ + openbox/ping.h \ openbox/place.c \ openbox/place.h \ openbox/popup.c \ diff --git a/openbox/client.c b/openbox/client.c index 7019f690..0f592ebe 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -3166,10 +3166,16 @@ 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; + 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 @@ void client_close(ObClient *self) 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) diff --git a/openbox/event.c b/openbox/event.c index 44e0f532..5d85eaeb 100644 --- a/openbox/event.c +++ b/openbox/event.c @@ -767,6 +767,9 @@ static void event_handle_root(XEvent *e) 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: diff --git a/openbox/mainloop.c b/openbox/mainloop.c index 7c6a9566..c1ede830 100644 --- a/openbox/mainloop.c +++ b/openbox/mainloop.c @@ -560,7 +560,9 @@ void ob_main_loop_timeout_remove_data(ObMainLoop *loop, GSourceFunc handler, 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; diff --git a/openbox/ping.c b/openbox/ping.c new file mode 100644 index 00000000..d8c10c83 --- /dev/null +++ b/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); + } +} diff --git a/openbox/ping.h b/openbox/ping.h new file mode 100644 index 00000000..0b6dfead --- /dev/null +++ b/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 +#include + +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 diff --git a/openbox/prop.c b/openbox/prop.c index 1e343e16..b43dcd13 100644 --- a/openbox/prop.c +++ b/openbox/prop.c @@ -446,6 +446,14 @@ void prop_erase(Window win, Atom prop) 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; @@ -457,7 +465,6 @@ void prop_message(Window about, Atom messagetype, glong data0, glong data1, 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); } diff --git a/openbox/prop.h b/openbox/prop.h index fd12f98a..13c338ef 100644 --- a/openbox/prop.h +++ b/openbox/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)) @@ -244,4 +247,9 @@ void prop_message(Window about, Atom messagetype, glong data0, glong data1, (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 -- 2.39.2