openbox/mwm.h \
openbox/openbox.c \
openbox/openbox.h \
+ openbox/ping.c \
+ openbox/ping.h \
openbox/place.c \
openbox/place.h \
openbox/popup.c \
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
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)
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:
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;
--- /dev/null
+/* -*- 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);
+ }
+}
--- /dev/null
+/* -*- 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
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.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);
}
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))
(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