]> Dogcows Code - chaz/openbox/blob - openbox/ping.c
Merge branch 'master' into chaz
[chaz/openbox] / openbox / ping.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3 client.h for the Openbox window manager
4 Copyright (c) 2006 Mikael Magnusson
5 Copyright (c) 2003-2008 Dana Jansens
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "ping.h"
21 #include "client.h"
22 #include "event.h"
23 #include "debug.h"
24 #include "openbox.h"
25 #include "obt/prop.h"
26
27 typedef struct _ObPingTarget
28 {
29 ObClient *client;
30 ObPingEventHandler h;
31 guint32 id;
32 guint loopid;
33 gint waiting;
34 } ObPingTarget;
35
36 static GHashTable *ping_ids = NULL;
37 static guint32 ping_next_id = 1;
38
39 #define PING_TIMEOUT 3000 /* in MS */
40 /*! Warn the user after this many PING_TIMEOUT intervals */
41 #define PING_TIMEOUT_WARN 2
42
43 static void ping_send(ObPingTarget *t);
44 static void ping_end(ObClient *client, gpointer data);
45 static gboolean ping_timeout(gpointer data);
46 static gboolean find_client(gpointer key, gpointer value, gpointer client);
47
48 void ping_startup(gboolean reconfigure)
49 {
50 if (reconfigure) return;
51
52 ping_ids = g_hash_table_new(g_int_hash, g_int_equal);
53
54 /* listen for clients to disappear */
55 client_add_destroy_notify(ping_end, NULL);
56 }
57
58 void ping_shutdown(gboolean reconfigure)
59 {
60 if (reconfigure) return;
61
62 g_hash_table_unref(ping_ids);
63 ping_ids = NULL;
64
65 client_remove_destroy_notify(ping_end);
66 }
67
68 void ping_start(struct _ObClient *client, ObPingEventHandler h)
69 {
70 ObPingTarget *t;
71
72 /* make sure the client supports ping! */
73 g_assert(client->ping == TRUE);
74
75 /* make sure we're not already pinging the client */
76 if (g_hash_table_find(ping_ids, find_client, client) != NULL) return;
77
78 t = g_slice_new0(ObPingTarget);
79 t->client = client;
80 t->h = h;
81
82 t->loopid = g_timeout_add_full(G_PRIORITY_DEFAULT, PING_TIMEOUT,
83 ping_timeout, t, NULL);
84 /* act like we just timed out immediately, to start the pinging process
85 now instead of after the first delay. this makes sure the client
86 ends up in the ping_ids hash table now. */
87 ping_timeout(t);
88
89 /* make sure we can remove the client later */
90 g_assert(g_hash_table_find(ping_ids, find_client, client) != NULL);
91 }
92
93 void ping_got_pong(guint32 id)
94 {
95 ObPingTarget *t;
96
97 if ((t = g_hash_table_lookup(ping_ids, &id))) {
98 /*ob_debug("-PONG: '%s' (id %u)", t->client->title, t->id);*/
99 if (t->waiting > PING_TIMEOUT_WARN) {
100 /* we had notified that they weren't responding, so now we
101 need to notify that they are again */
102 t->h(t->client, FALSE);
103 }
104 t->waiting = 0; /* not waiting for a reply anymore */
105
106 /* we got a pong so we're happy now */
107 ping_end(t->client, NULL);
108 }
109 else
110 ob_debug("Got PONG with id %u but not waiting for one", id);
111 }
112
113 static gboolean find_client(gpointer key, gpointer value, gpointer client)
114 {
115 ObPingTarget *t = value;
116 return t->client == client;
117 }
118
119 static void ping_send(ObPingTarget *t)
120 {
121 /* t->id is 0 when it hasn't been assigned an id ever yet.
122 we can reuse ids when t->waiting == 0, because we won't be getting a
123 pong for that id in the future again. that way for apps that aren't
124 timing out we don't need to remove/add them from/to the hash table */
125 if (t->id == 0 || t->waiting > 0) {
126 /* pick an id, and reinsert in the hash table with the new id */
127 if (t->id) g_hash_table_remove(ping_ids, &t->id);
128 t->id = ping_next_id;
129 if (++ping_next_id == 0) ++ping_next_id; /* skip 0 on wraparound */
130 g_hash_table_insert(ping_ids, &t->id, t);
131 }
132
133 /*ob_debug("+PING: '%s' (id %u)", t->client->title, t->id);*/
134 OBT_PROP_MSG_TO(t->client->window, t->client->window, WM_PROTOCOLS,
135 OBT_PROP_ATOM(NET_WM_PING), t->id, t->client->window, 0, 0,
136 NoEventMask);
137 }
138
139 static gboolean ping_timeout(gpointer data)
140 {
141 ObPingTarget *t = data;
142
143 ping_send(t);
144
145 /* if the client hasn't been responding then do something about it */
146 if (t->waiting == PING_TIMEOUT_WARN)
147 t->h(t->client, TRUE); /* notify that the client isn't responding */
148
149 ++t->waiting;
150
151 return TRUE; /* repeat */
152 }
153
154 static void ping_end(ObClient *client, gpointer data)
155 {
156 ObPingTarget *t;
157
158 if ((t = g_hash_table_find(ping_ids, find_client, client))) {
159 g_hash_table_remove(ping_ids, &t->id);
160
161 g_source_remove(t->loopid);
162
163 g_slice_free(ObPingTarget, t);
164 }
165 }
This page took 0.036705 seconds and 4 git commands to generate.