Merge lp:~agateau/unity-2d/dont-disconnect-from-dead-windows into lp:unity-2d/3.0

Proposed by Aurélien Gâteau
Status: Merged
Approved by: Florian Boucault
Approved revision: 624
Merged at revision: 631
Proposed branch: lp:~agateau/unity-2d/dont-disconnect-from-dead-windows
Merge into: lp:unity-2d/3.0
Diff against target: 328 lines (+252/-4)
6 files modified
libunity-2d-private/src/CMakeLists.txt (+1/-0)
libunity-2d-private/src/gconnector.cpp (+81/-0)
libunity-2d-private/src/gconnector.h (+67/-0)
libunity-2d-private/tests/CMakeLists.txt (+2/-0)
libunity-2d-private/tests/gconnectortest.cpp (+96/-0)
panel/applets/appname/windowhelper.cpp (+5/-4)
To merge this branch: bzr merge lp:~agateau/unity-2d/dont-disconnect-from-dead-windows
Reviewer Review Type Date Requested Status
Florian Boucault (community) Approve
Review via email: mp+67701@code.launchpad.net

Commit message

[libunity-2d] Introduce GConnector, a class to track connections to GObject signals.

Use GConnector in WindowHelper, avoiding some GLib critical messages when the active window is closed. Those messages were caused by us trying to disconnect from a dead wnck window handle.

Description of the change

This branch has been extracted from the unity-core branch. It does two things:

- Introduces GConnector, a class to track connections to GObject signals.

- Uses GConnector in WindowHelper, avoiding some GLib critical messages when the active window is closed. Those messages were caused by us trying to disconnect from a dead wnck window handle.

To post a comment you must log in.
Revision history for this message
Florian Boucault (fboucault) wrote :

Works as advertised, code is clean. Thank you.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'libunity-2d-private/src/CMakeLists.txt'
2--- libunity-2d-private/src/CMakeLists.txt 2011-06-22 08:22:14 +0000
3+++ libunity-2d-private/src/CMakeLists.txt 2011-07-12 13:50:20 +0000
4@@ -4,6 +4,7 @@
5
6 # Sources
7 set(libunity-2d-private_SRCS
8+ gconnector.cpp
9 gnomesessionclient.cpp
10 keyboardmodifiersmonitor.cpp
11 hotkeymonitor.cpp
12
13=== added file 'libunity-2d-private/src/gconnector.cpp'
14--- libunity-2d-private/src/gconnector.cpp 1970-01-01 00:00:00 +0000
15+++ libunity-2d-private/src/gconnector.cpp 2011-07-12 13:50:20 +0000
16@@ -0,0 +1,81 @@
17+/*
18+ * This file is part of unity-2d
19+ *
20+ * Copyright 2011 Canonical Ltd.
21+ *
22+ * Authors:
23+ * - Aurélien Gâteau <aurelien.gateau@canonical.com>
24+ *
25+ * This program is free software; you can redistribute it and/or modify
26+ * it under the terms of the GNU General Public License as published by
27+ * the Free Software Foundation; version 3.
28+ *
29+ * This program is distributed in the hope that it will be useful,
30+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
31+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32+ * GNU General Public License for more details.
33+ *
34+ * You should have received a copy of the GNU General Public License
35+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
36+ */
37+// Self
38+#include "gconnector.h"
39+
40+// Local
41+#include <debug_p.h>
42+
43+// Qt
44+
45+GConnector::GConnector()
46+{
47+}
48+
49+GConnector::~GConnector()
50+{
51+ disconnectAll();
52+}
53+
54+void GConnector::connect(gpointer instance, const char* signal, GCallback handler, gpointer data)
55+{
56+ gulong id = g_signal_connect(instance, signal, handler, data);
57+ auto it = m_connections.find(instance);
58+ if (it == m_connections.end()) {
59+ g_object_weak_ref(G_OBJECT(instance),
60+ GWeakNotify(weakNotifyCB),
61+ reinterpret_cast<gpointer>(this));
62+ m_connections.insert(instance, ConnectionIdList() << id);
63+ } else {
64+ it.value() << id;
65+ }
66+}
67+
68+void GConnector::disconnect(gpointer instance)
69+{
70+ auto it = m_connections.find(instance);
71+ UQ_RETURN_IF_FAIL(it != m_connections.end());
72+ disconnect(it);
73+}
74+
75+void GConnector::disconnectAll()
76+{
77+ while (!m_connections.isEmpty()) {
78+ disconnect(m_connections.begin());
79+ }
80+}
81+
82+void GConnector::disconnect(Connections::Iterator it)
83+{
84+ gpointer instance = it.key();
85+ Q_FOREACH(gulong id, it.value()) {
86+ g_signal_handler_disconnect(instance, id);
87+ }
88+ g_object_weak_unref(G_OBJECT(instance),
89+ GWeakNotify(weakNotifyCB),
90+ reinterpret_cast<gpointer>(this));
91+ m_connections.erase(it);
92+}
93+
94+void GConnector::weakNotifyCB(GConnector* that, GObject* instance)
95+{
96+ that->m_connections.remove(instance);
97+}
98
99=== added file 'libunity-2d-private/src/gconnector.h'
100--- libunity-2d-private/src/gconnector.h 1970-01-01 00:00:00 +0000
101+++ libunity-2d-private/src/gconnector.h 2011-07-12 13:50:20 +0000
102@@ -0,0 +1,67 @@
103+/*
104+ * This file is part of unity-2d
105+ *
106+ * Copyright 2011 Canonical Ltd.
107+ *
108+ * Authors:
109+ * - Aurélien Gâteau <aurelien.gateau@canonical.com>
110+ *
111+ * This program is free software; you can redistribute it and/or modify
112+ * it under the terms of the GNU General Public License as published by
113+ * the Free Software Foundation; version 3.
114+ *
115+ * This program is distributed in the hope that it will be useful,
116+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
117+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
118+ * GNU General Public License for more details.
119+ *
120+ * You should have received a copy of the GNU General Public License
121+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
122+ */
123+#ifndef GCONNECTOR_H
124+#define GCONNECTOR_H
125+
126+// Local
127+
128+// Qt
129+#include <QHash>
130+#include <QList>
131+
132+// GLib
133+#include <glib-object.h>
134+
135+/**
136+ * An helper class to ensure GObject signals are correctly disconnected.
137+ */
138+class GConnector
139+{
140+public:
141+ GConnector();
142+ ~GConnector();
143+
144+ /**
145+ * Connect a signal to a callback
146+ */
147+ void connect(gpointer instance, const char* signal, GCallback handler, gpointer data);
148+
149+ /**
150+ * Disconnect from all signals of instance
151+ */
152+ void disconnect(gpointer instance);
153+
154+ /**
155+ * Disconnect from all signals we connected to
156+ */
157+ void disconnectAll();
158+
159+private:
160+ typedef QList<gulong> ConnectionIdList;
161+ typedef QHash<gpointer, ConnectionIdList> Connections;
162+ Connections m_connections;
163+
164+ void disconnect(Connections::Iterator);
165+
166+ static void weakNotifyCB(GConnector*, GObject*);
167+};
168+
169+#endif /* GCONNECTOR_H */
170
171=== modified file 'libunity-2d-private/tests/CMakeLists.txt'
172--- libunity-2d-private/tests/CMakeLists.txt 2011-06-13 09:32:04 +0000
173+++ libunity-2d-private/tests/CMakeLists.txt 2011-07-12 13:50:20 +0000
174@@ -5,6 +5,7 @@
175 ${libunity-2d-private_SOURCE_DIR}/src
176 ${libunity-2d-private_SOURCE_DIR}/Unity2d
177 ${CMAKE_CURRENT_BINARY_DIR}
178+ ${GLIB_INCLUDE_DIRS}
179 ${QT_QTTEST_INCLUDE_DIR}
180 )
181
182@@ -33,6 +34,7 @@
183 endmacro(libunity_2d_tests)
184
185 libunity_2d_tests(
186+ gconnectortest
187 keyboardmodifiersmonitortest
188 paneltest
189 unity2dtrtest
190
191=== added file 'libunity-2d-private/tests/gconnectortest.cpp'
192--- libunity-2d-private/tests/gconnectortest.cpp 1970-01-01 00:00:00 +0000
193+++ libunity-2d-private/tests/gconnectortest.cpp 2011-07-12 13:50:20 +0000
194@@ -0,0 +1,96 @@
195+/*
196+ * This file is part of unity-2d
197+ *
198+ * Copyright 2011 Canonical Ltd.
199+ *
200+ * Authors:
201+ * - Aurélien Gâteau <aurelien.gateau@canonical.com>
202+ *
203+ * This program is free software; you can redistribute it and/or modify
204+ * it under the terms of the GNU General Public License as published by
205+ * the Free Software Foundation; version 3.
206+ *
207+ * This program is distributed in the hope that it will be useful,
208+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
209+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
210+ * GNU General Public License for more details.
211+ *
212+ * You should have received a copy of the GNU General Public License
213+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
214+ */
215+
216+// Local
217+#include <debug_p.h>
218+#include <gconnector.h>
219+
220+// Qt
221+#include <QtTest>
222+
223+// GLib
224+#include <gio/gio.h>
225+
226+class CallbackSpy {
227+public:
228+ CallbackSpy()
229+ : count(0)
230+ {}
231+
232+ static void increase(GCancellable*, gpointer data)
233+ {
234+ CallbackSpy* spy = reinterpret_cast<CallbackSpy*>(data);
235+ ++spy->count;
236+ }
237+
238+ int count;
239+};
240+
241+class GConnectorTest : public QObject
242+{
243+ Q_OBJECT
244+private Q_SLOTS:
245+ void init()
246+ {
247+ g_type_init();
248+ }
249+
250+ void testDisconnectAll()
251+ {
252+ // Create a cancellable object as it is easy to get it to emit a
253+ // signal.
254+ GCancellable* object = g_cancellable_new();
255+
256+ CallbackSpy spy;
257+ GConnector connector;
258+ connector.connect(object, "cancelled", G_CALLBACK(CallbackSpy::increase), &spy);
259+
260+ // Emit signal, check callback gets called
261+ g_cancellable_cancel(object);
262+ QCOMPARE(spy.count, 1);
263+
264+ // Disconnect and emit signal, check callback does *not* get called
265+ connector.disconnectAll();
266+ g_cancellable_reset(object);
267+ g_cancellable_cancel(object);
268+ QCOMPARE(spy.count, 1);
269+
270+ g_object_unref(object);
271+ }
272+
273+ void testDeletedInstance()
274+ {
275+ GCancellable* object = g_cancellable_new();
276+
277+ // Create a dummy connection
278+ CallbackSpy spy;
279+ GConnector connector;
280+ connector.connect(object, "cancelled", G_CALLBACK(CallbackSpy::increase), &spy);
281+ g_object_unref(object);
282+
283+ // Should not crash because connection should have been removed
284+ connector.disconnectAll();
285+ }
286+};
287+
288+QTEST_MAIN(GConnectorTest)
289+
290+#include "gconnectortest.moc"
291
292=== modified file 'panel/applets/appname/windowhelper.cpp'
293--- panel/applets/appname/windowhelper.cpp 2011-06-22 14:49:34 +0000
294+++ panel/applets/appname/windowhelper.cpp 2011-07-12 13:50:20 +0000
295@@ -26,6 +26,7 @@
296
297 // unity-2d
298 #include <debug_p.h>
299+#include <gconnector.h>
300
301 // Bamf
302 #include <bamf-matcher.h>
303@@ -45,6 +46,7 @@
304 struct WindowHelperPrivate
305 {
306 WnckWindow* m_window;
307+ GConnector m_connector;
308 };
309
310 WindowHelper::WindowHelper(QObject* parent)
311@@ -96,14 +98,13 @@
312 uint xid = bamfWindow ? bamfWindow->xid() : 0;
313
314 if (d->m_window) {
315- g_signal_handlers_disconnect_by_func(d->m_window, gpointer(stateChangedCB), this);
316- g_signal_handlers_disconnect_by_func(d->m_window, gpointer(nameChangedCB), this);
317+ d->m_connector.disconnectAll();
318 d->m_window = 0;
319 }
320 if (xid != 0) {
321 d->m_window = wnck_window_get(xid);
322- g_signal_connect(G_OBJECT(d->m_window), "name-changed", G_CALLBACK(nameChangedCB), this);
323- g_signal_connect(G_OBJECT(d->m_window), "state-changed", G_CALLBACK(stateChangedCB), this);
324+ d->m_connector.connect(G_OBJECT(d->m_window), "name-changed", G_CALLBACK(nameChangedCB), this);
325+ d->m_connector.connect(G_OBJECT(d->m_window), "state-changed", G_CALLBACK(stateChangedCB), this);
326 }
327 stateChanged();
328 nameChanged();

Subscribers

People subscribed via source and target branches