Merge lp:~ted/indicator-appmenu/destruction-will-happen-in-time into lp:indicator-appmenu/0.3

Proposed by Ted Gould
Status: Merged
Merged at revision: 113
Proposed branch: lp:~ted/indicator-appmenu/destruction-will-happen-in-time
Merge into: lp:indicator-appmenu/0.3
Diff against target: 217 lines (+90/-51)
1 file modified
src/indicator-appmenu.c (+90/-51)
To merge this branch: bzr merge lp:~ted/indicator-appmenu/destruction-will-happen-in-time
Reviewer Review Type Date Requested Status
Neil J. Patel (community) Approve
Review via email: mp+56641@code.launchpad.net

Description of the change

So there's a glitch in the information coming from BAMF. It's a small glitch, but we were responding to it harshly. Destroying everything when it told us to. But, now we know we can't trust it. So this branch removes that trust.

It solves it the same way that you'd solve audio glitches, with a low pass filter. Basically a timeout for the computer scientists. We're averaging the signals. Which means we're only freeing the menus after we're told, *and* there's been enough time we're sure that BAMF has it's head on straight. If BAMF changes it's mind in that time, we assume it's a glitch.

Unfortunately this has a set of reprocussions. One of those is that we can get registrations for menus that we already "have" as we're still waiting to see if BAMF changed it's mind. To handle that we're allowing reregistration and destroying the previous menus. This shouldn't happen regularly, so it's not going to be that expensive.

To post a comment you must log in.
Revision history for this message
Neil J. Patel (njpatel) wrote :

guint xid = bamf_window_get_xid(window) needs to be guint32 but that's it really, nice work. Approving to avoid another review cycle.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/indicator-appmenu.c'
2--- src/indicator-appmenu.c 2011-03-16 03:49:25 +0000
3+++ src/indicator-appmenu.c 2011-04-06 20:01:00 +0000
4@@ -106,6 +106,8 @@
5 GDBusConnection * bus;
6 guint owner_id;
7 guint dbus_registration;
8+
9+ GHashTable * destruction_timers;
10 };
11
12
13@@ -213,6 +215,9 @@
14 gpointer user_data);
15 static void menus_destroyed (GObject * menus,
16 gpointer user_data);
17+static void source_unregister (gpointer user_data);
18+static GVariant * unregister_window (IndicatorAppmenu * iapp,
19+ guint windowid);
20
21 /* Unique error codes for debug interface */
22 enum {
23@@ -302,6 +307,9 @@
24 self->desktop_windows = g_hash_table_new(g_direct_hash, g_direct_equal);
25 self->desktop_menu = NULL; /* Starts NULL until found */
26
27+ /* Set up the hashtable of destruction timers */
28+ self->destruction_timers = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, source_unregister);
29+
30 build_window_menus(self);
31
32 /* Get the default BAMF matcher */
33@@ -424,6 +432,14 @@
34 iapp->dbus_registration = 0;
35 }
36
37+ if (iapp->destruction_timers != NULL) {
38+ /* These are in dispose and not finalize becuase the dereference
39+ function removes timers that could need the object to be in
40+ a valid state, so it's better to have them in dispose */
41+ g_hash_table_destroy(iapp->destruction_timers);
42+ iapp->destruction_timers = NULL;
43+ }
44+
45 if (iapp->bus != NULL) {
46 g_object_unref(iapp->bus);
47 iapp->bus = NULL;
48@@ -755,12 +771,16 @@
49 }
50
51 BamfWindow * window = BAMF_WINDOW(view);
52+ IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
53+ guint xid = bamf_window_get_xid(window);
54+
55+ /* Make sure we don't destroy it later */
56+ g_hash_table_remove(iapp->destruction_timers, GUINT_TO_POINTER(xid));
57+
58 if (bamf_window_get_window_type(window) != BAMF_WINDOW_DESKTOP) {
59 return;
60 }
61
62- IndicatorAppmenu * iapp = INDICATOR_APPMENU(user_data);
63- guint xid = bamf_window_get_xid(window);
64 g_hash_table_insert(iapp->desktop_windows, GUINT_TO_POINTER(xid), GINT_TO_POINTER(TRUE));
65
66 g_debug("New Desktop Window: %X", xid);
67@@ -778,6 +798,32 @@
68 return;
69 }
70
71+typedef struct _destroy_data_t destroy_data_t;
72+struct _destroy_data_t {
73+ IndicatorAppmenu * iapp;
74+ guint32 xid;
75+};
76+
77+/* Timeout to finally cleanup the window. Causes is to ignore glitches that
78+ come from BAMF/WNCK. */
79+static gboolean
80+destroy_window_timeout (gpointer user_data)
81+{
82+ destroy_data_t * destroy_data = (destroy_data_t *)user_data;
83+ g_hash_table_steal(destroy_data->iapp->destruction_timers, GUINT_TO_POINTER(destroy_data->xid));
84+ unregister_window(destroy_data->iapp, destroy_data->xid);
85+ return FALSE; /* free's data through source deregistration */
86+}
87+
88+/* Unregisters the source in the hash table when it gets removed. This ensure
89+ we don't leave any timeouts around */
90+static void
91+source_unregister (gpointer user_data)
92+{
93+ g_source_remove(GPOINTER_TO_UINT(user_data));
94+ return;
95+}
96+
97 /* When windows leave us, this function gets called */
98 static void
99 old_window (BamfMatcher * matcher, BamfView * view, gpointer user_data)
100@@ -790,25 +836,12 @@
101 BamfWindow * window = BAMF_WINDOW(view);
102 guint32 xid = bamf_window_get_xid(window);
103
104- /* See if it's in our list of desktop windows, if
105- so remove it from that list. */
106- if (bamf_window_get_window_type(window) == BAMF_WINDOW_DESKTOP) {
107- g_hash_table_remove(iapp->desktop_windows, GUINT_TO_POINTER(xid));
108- }
109-
110- /* Now let's see if we've got a WM object for it then
111- we need to mark it as destroyed and unreference to
112- actually destroy it. */
113- gpointer wm = g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(xid));
114- if (wm != NULL) {
115- GObject * wmo = G_OBJECT(wm);
116-
117- /* Using destroyed so that if the menus are shown
118- they'll be switch and the current window gets
119- updated as well. */
120- menus_destroyed(wmo, iapp);
121- g_object_unref(wmo);
122- }
123+ destroy_data_t * destroy_data = g_new0(destroy_data_t, 1);
124+ destroy_data->iapp = iapp;
125+ destroy_data->xid = xid;
126+
127+ guint source_id = g_timeout_add_seconds_full(G_PRIORITY_LOW, 5, destroy_window_timeout, destroy_data, g_free);
128+ g_hash_table_replace(iapp->destruction_timers, GUINT_TO_POINTER(xid), GUINT_TO_POINTER(source_id));
129
130 return;
131 }
132@@ -1272,6 +1305,9 @@
133 {
134 g_debug("Registering window ID %d with path %s from %s", windowid, objectpath, sender);
135
136+ /* Shouldn't do anything, but let's be sure */
137+ g_hash_table_remove(iapp->destruction_timers, GUINT_TO_POINTER(windowid));
138+
139 if (g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid)) == NULL && windowid != 0) {
140 WindowMenus * wm = window_menus_new(windowid, sender, objectpath);
141 g_return_val_if_fail(wm != NULL, FALSE);
142@@ -1294,7 +1330,19 @@
143 if (windowid == 0) {
144 g_warning("Can't build windows for a NULL window ID %d with path %s from %s", windowid, objectpath, sender);
145 } else {
146- g_warning("Already have a menu for window ID %d with path %s from %s", windowid, objectpath, sender);
147+ g_warning("Already have a menu for window ID %d with path %s from %s, unregistering that one", windowid, objectpath, sender);
148+ unregister_window(iapp, windowid);
149+
150+ /* NOTE: So we're doing a lookup here. That seems pretty useless
151+ now doesn't it. It's for a good reason. We're going recursive
152+ with a pretty complex set of functions we want to ensure that
153+ we're not going to end up infinitely recursive otherwise things
154+ could go really bad. */
155+ if (g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid)) == NULL) {
156+ return register_window(iapp, windowid, objectpath, sender);
157+ }
158+
159+ g_warning("Unable to unregister window!");
160 }
161 }
162
163@@ -1309,35 +1357,26 @@
164 g_return_val_if_fail(IS_INDICATOR_APPMENU(iapp), NULL);
165 g_return_val_if_fail(iapp->matcher != NULL, NULL);
166
167- /* Get the application that uses that XID */
168- BamfApplication * app = bamf_matcher_get_application_for_xid(iapp->matcher, windowid);
169- if (app == NULL) {
170- return NULL;
171- }
172- g_object_ref_sink(G_OBJECT(app));
173-
174- /* Figure out which window is associated with it */
175- GList * windows = bamf_application_get_windows(app);
176- GList * lwindow;
177- BamfWindow * window = NULL;
178- for (lwindow = windows; lwindow != NULL; lwindow = g_list_next(lwindow)) {
179- BamfWindow * thiswindow = BAMF_WINDOW(lwindow->data);
180- if (bamf_window_get_xid(thiswindow) == windowid) {
181- window = thiswindow;
182- break;
183- }
184- }
185- g_list_free(windows);
186-
187- if (window == NULL) {
188- g_object_unref(app);
189- return NULL;
190- }
191-
192- /* Call the old_window handler for that window */
193- old_window(iapp->matcher, BAMF_VIEW(window), iapp);
194-
195- g_object_unref(app);
196+ /* Make sure we don't destroy it later */
197+ g_hash_table_remove(iapp->destruction_timers, GUINT_TO_POINTER(windowid));
198+
199+ /* If it's a desktop window remove it from that table as well */
200+ g_hash_table_remove(iapp->desktop_windows, GUINT_TO_POINTER(windowid));
201+
202+ /* Now let's see if we've got a WM object for it then
203+ we need to mark it as destroyed and unreference to
204+ actually destroy it. */
205+ gpointer wm = g_hash_table_lookup(iapp->apps, GUINT_TO_POINTER(windowid));
206+ if (wm != NULL) {
207+ GObject * wmo = G_OBJECT(wm);
208+
209+ /* Using destroyed so that if the menus are shown
210+ they'll be switch and the current window gets
211+ updated as well. */
212+ menus_destroyed(wmo, iapp);
213+ g_object_unref(wmo);
214+ }
215+
216 return NULL;
217 }
218

Subscribers

People subscribed via source and target branches