can tell when a window that was "closed" has stopped responding now
This commit is contained in:
parent
d790dc162d
commit
fb7a71da20
8 changed files with 231 additions and 17 deletions
|
@ -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 \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
151
openbox/ping.c
Normal 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
41
openbox/ping.h
Normal 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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue