X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fping.c;h=7cb47669988f036fbfcdfdaa26171ff582e0e415;hb=HEAD;hp=874d020ceffe843d56fc40dea8d8cc83bcb1547c;hpb=810afd8597da355039e289218abed6c062585870;p=chaz%2Fopenbox diff --git a/openbox/ping.c b/openbox/ping.c index 874d020c..7cb47669 100644 --- a/openbox/ping.c +++ b/openbox/ping.c @@ -19,108 +19,130 @@ #include "ping.h" #include "client.h" -#include "prop.h" #include "event.h" -#include "mainloop.h" +#include "debug.h" #include "openbox.h" +#include "obt/prop.h" typedef struct _ObPingTarget { ObClient *client; ObPingEventHandler h; - Time sent; + guint32 id; + guint loopid; gint waiting; } ObPingTarget; -static GSList *ping_targets = NULL; -static gboolean active = FALSE; +static GHashTable *ping_ids = NULL; +static guint32 ping_next_id = 1; -#define PING_TIMEOUT (G_USEC_PER_SEC * 3) +#define PING_TIMEOUT 3000 /* in MS */ /*! Warn the user after this many PING_TIMEOUT intervals */ -#define PING_TIMEOUT_WARN 3 +#define PING_TIMEOUT_WARN 2 -static void ping_send(ObPingTarget *t); -static void ping_end(ObClient *client, gpointer data); +static void ping_send(ObPingTarget *t); +static void ping_end(ObClient *client, gpointer data); static gboolean ping_timeout(gpointer data); +static gboolean find_client(gpointer key, gpointer value, gpointer client); + +void ping_startup(gboolean reconfigure) +{ + if (reconfigure) return; + + ping_ids = g_hash_table_new(g_int_hash, g_int_equal); + + /* listen for clients to disappear */ + client_add_destroy_notify(ping_end, NULL); +} + +void ping_shutdown(gboolean reconfigure) +{ + if (reconfigure) return; + + g_hash_table_unref(ping_ids); + ping_ids = NULL; + + client_remove_destroy_notify(ping_end); +} void ping_start(struct _ObClient *client, ObPingEventHandler h) { - GSList *it; ObPingTarget *t; + /* make sure the client supports ping! */ g_assert(client->ping == TRUE); - /* 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; - } + /* make sure we're not already pinging the client */ + if (g_hash_table_find(ping_ids, find_client, client) != NULL) return; - t = g_new(ObPingTarget, 1); + t = g_slice_new0(ObPingTarget); 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); - } -} + t->loopid = g_timeout_add_full(G_PRIORITY_DEFAULT, PING_TIMEOUT, + ping_timeout, t, NULL); + /* act like we just timed out immediately, to start the pinging process + now instead of after the first delay. this makes sure the client + ends up in the ping_ids hash table now. */ + ping_timeout(t); -void ping_stop(struct _ObClient *c) -{ - ping_end(c, NULL); + /* make sure we can remove the client later */ + g_assert(g_hash_table_find(ping_ids, find_client, client) != NULL); } -void ping_got_pong(Time timestamp) +void ping_got_pong(guint32 id) { - 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 ((t = g_hash_table_lookup(ping_ids, &id))) { + /*ob_debug("-PONG: '%s' (id %u)", t->client->title, t->id);*/ + 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 */ + + /* we got a pong so we're happy now */ + ping_end(t->client, NULL); } + else + ob_debug("Got PONG with id %u but not waiting for one", id); +} - if (it == NULL) - ob_debug("Got PONG with timestamp %lu but not waiting for one\n", - timestamp); +static gboolean find_client(gpointer key, gpointer value, gpointer client) +{ + ObPingTarget *t = value; + return t->client == client; } 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); + /* t->id is 0 when it hasn't been assigned an id ever yet. + we can reuse ids when t->waiting == 0, because we won't be getting a + pong for that id in the future again. that way for apps that aren't + timing out we don't need to remove/add them from/to the hash table */ + if (t->id == 0 || t->waiting > 0) { + /* pick an id, and reinsert in the hash table with the new id */ + if (t->id) g_hash_table_remove(ping_ids, &t->id); + t->id = ping_next_id; + if (++ping_next_id == 0) ++ping_next_id; /* skip 0 on wraparound */ + g_hash_table_insert(ping_ids, &t->id, t); + } + + /*ob_debug("+PING: '%s' (id %u)", t->client->title, t->id);*/ + OBT_PROP_MSG_TO(t->client->window, t->client->window, WM_PROTOCOLS, + OBT_PROP_ATOM(NET_WM_PING), t->id, 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); - } + ping_send(t); + /* if the client hasn't been responding then do something about it */ if (t->waiting == PING_TIMEOUT_WARN) t->h(t->client, TRUE); /* notify that the client isn't responding */ @@ -131,23 +153,13 @@ static gboolean ping_timeout(gpointer data) 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; - } - } + if ((t = g_hash_table_find(ping_ids, find_client, client))) { + g_hash_table_remove(ping_ids, &t->id); + + g_source_remove(t->loopid); - /* stop listening if we're not waiting for any more pings */ - if (!ping_targets) { - active = TRUE; - client_remove_destroy_notify(ping_end); - } + g_slice_free(ObPingTarget, t); + } }