can tell when a window that was "closed" has stopped responding now

This commit is contained in:
Dana Jansens 2008-01-15 21:40:15 -05:00
parent d790dc162d
commit fb7a71da20
8 changed files with 231 additions and 17 deletions

View file

@ -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 \

View file

@ -3166,10 +3166,16 @@ void client_shade(ObClient *self, gboolean shade)
frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
}
static void client_ping_event(ObClient *self, gboolean dead)
{
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)
{
XEvent ce;
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)

View file

@ -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:

View file

@ -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;

151
openbox/ping.c Normal file
View file

@ -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);
}
}

41
openbox/ping.h Normal file
View file

@ -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

View file

@ -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);
}

View file

@ -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