Merge lp:~dandrader/geis/lp967605 into lp:geis

Proposed by Daniel d'Andrada
Status: Merged
Merged at revision: 240
Proposed branch: lp:~dandrader/geis/lp967605
Merge into: lp:geis
Diff against target: 510 lines (+331/-78)
4 files modified
libutouch-geis/backend/grail/Makefile.am (+1/-0)
libutouch-geis/backend/grail/geis_grail_backend.c (+15/-78)
libutouch-geis/backend/grail/geis_grail_xsync.c (+226/-0)
libutouch-geis/backend/grail/geis_grail_xsync.h (+89/-0)
To merge this branch: bzr merge lp:~dandrader/geis/lp967605
Reviewer Review Type Date Requested Status
Chase Douglas (community) Approve
Stephen M. Webb Pending
Review via email: mp+100194@code.launchpad.net

This proposal supersedes a proposal from 2012-03-29.

Description of the change

Fixes bug #967605.

I wonder if I should make a unit test for this guy (mocking XSync*).

Updates:
 * FIxed coding style
 * Fixed issue with querying server time
 * Changed header boilerplate to mention LGPL instead of GPL

To post a comment you must log in.
Revision history for this message
Chase Douglas (chasedouglas) wrote : Posted in a previous version of this proposal

* Style:

XSyncIntsToValue(&alarm_attributes.trigger.wait_value, timeout & 0xffffffff,
    timeout & 0xffffffff00000000);

The second line should be indented to match the open parens. The same for the XSyncCreateAlarm() call.

* geis_grail_xsync_query_server_time(GeisGrailXSync self) undoes the fix for bug 962705: https://code.launchpad.net/~chasedouglas/utouch-geis/fix-tap/+merge/98953. Instead of querying the time, we need to use the counter value from the alarm event.

Otherwise, this looks like a great fix for the problem. I don't think there's a test we can come up with that will be determinate, so I'm fine with not having an integration test. A unit test would be nice, but I will leave the call on that up to Stephen. Usually, unit tests are added for these types of additions.

review: Needs Fixing
Revision history for this message
Stephen M. Webb (bregma) wrote : Posted in a previous version of this proposal

I am not going to comment on the XSync code itself, I'll assume Daniel and Chase know more than I on that topic. The overall structure of the code and use of GeisBag is all OK.

I'm approving this with a single condition: the license terms of libutouch-geis is LGPL-3. Please modify the boilerplate in the new source files to refer to the Lesser General Public License.

review: Approve
Revision history for this message
Stephen M. Webb (bregma) wrote : Posted in a previous version of this proposal

I don't think any unit test is practical since the functionality is dependent on a working X server (unless we use a mock X lib, and I think it's too late in the game to start adding that sort of thing).

Any reliable integration test would require some carefully handcrafted event files, and even then would be unreliable because of effects such as machine load. I think we'll just have to forego automated testing of this fix.

A manual test case description might be warranted.

Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

> [...]
> I'm approving this with a single condition: the license terms of libutouch-
> geis is LGPL-3. Please modify the boilerplate in the new source files to
> refer to the Lesser General Public License.

Will do.
I copy-pasted that from geis_grail_backend.c, just changed the copyright year. Looks like several files in utouch-geis are still referring to GPL instead of LGPL.

Revision history for this message
Chase Douglas (chasedouglas) wrote :

Looks good to me :).

review: Approve
Revision history for this message
Chase Douglas (chasedouglas) wrote :

I missed this: the X connection needs to be flushed after creating the alarm. Otherwise, the request might not reach the server for some time. Make sure to add it in when merging.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> I missed this: the X connection needs to be flushed after creating the alarm. Otherwise, the request might not reach the server for some time. Make sure to add it in when merging.

I already merged it. Thus I pushed this change now as a separate, subsequent commit.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'libutouch-geis/backend/grail/Makefile.am'
2--- libutouch-geis/backend/grail/Makefile.am 2012-01-31 20:46:42 +0000
3+++ libutouch-geis/backend/grail/Makefile.am 2012-03-30 16:56:28 +0000
4@@ -22,6 +22,7 @@
5 geis_grail_token.h geis_grail_token.c \
6 geis_grail_backend.h geis_grail_backend.c \
7 geis_grail_window_grab.h geis_grail_window_grab.c \
8+ geis_grail_xsync.h geis_grail_xsync.c \
9 geis_ugsubscription_store.h geis_ugsubscription_store.c
10
11 libutouch_geis_grail_backend_la_CPPFLAGS = \
12
13=== modified file 'libutouch-geis/backend/grail/geis_grail_backend.c'
14--- libutouch-geis/backend/grail/geis_grail_backend.c 2012-03-29 19:41:33 +0000
15+++ libutouch-geis/backend/grail/geis_grail_backend.c 2012-03-30 16:56:28 +0000
16@@ -29,6 +29,7 @@
17 #include "geis_event.h"
18 #include "geis_grail_token.h"
19 #include "geis_grail_window_grab.h"
20+#include "geis_grail_xsync.h"
21 #include "geis_group.h"
22 #include "geis_logging.h"
23 #include "geis_private.h"
24@@ -41,7 +42,6 @@
25 #include <string.h>
26 #include <utouch/frame_x11.h>
27 #include <utouch/grail.h>
28-#include <X11/extensions/sync.h>
29 #include <X11/extensions/XInput2.h>
30 #include <X11/Xlib.h>
31
32@@ -84,7 +84,7 @@
33 Geis geis; /**< The parent GEIS instance. */
34 Display *display; /**< The X11 connection. */
35 Window root_window; /**< The X11 root window. */
36- XSyncCounter x11_timer; /**< The X11 timer. */
37+ GeisGrailXSync xsync; /**< XSync handler for setting timers. */
38 UFHandle frame; /**< A utouch-frame instance. */
39 UGHandle grail; /**< A utouch-grail instance. */
40 GeisBag devices; /**< The list of known devices. */
41@@ -373,22 +373,7 @@
42 /* Set a timeout on the X server, if necessary. */
43 uint64_t timeout = grail_next_timeout(gbe->grail);
44 if (timeout)
45- {
46- XSyncWaitCondition condition;
47-
48- condition.trigger.counter = gbe->x11_timer;
49- condition.trigger.value_type = XSyncAbsolute;
50- XSyncIntsToValue(&condition.trigger.wait_value,
51- timeout & 0x00000000ffffffff,
52- timeout & 0xffffffff00000000);
53- condition.trigger.test_type = XSyncPositiveComparison;
54- XSyncIntToValue(&condition.event_threshold, 0);
55- if (!XSyncAwait(gbe->display, &condition, 1))
56- {
57- geis_warning("failed to set wait trigger");
58- }
59- XFlush(gbe->display);
60- }
61+ geis_grail_xsync_set_timeout(gbe->xsync, timeout);
62 }
63
64 static void
65@@ -929,14 +914,11 @@
66 {
67 XNextEvent(gbe->display, &event);
68
69- /* Non-Generic events should be timer events. */
70- if (event.type != GenericEvent)
71+ if (geis_grail_xsync_is_timeout(gbe->xsync, &event))
72 {
73- XSyncCounterNotifyEvent *sync_event = (XSyncCounterNotifyEvent*)&event;
74- XSyncValue value = sync_event->counter_value;
75- uint64_t grail_time = (uint64_t)value.hi << 32 | value.lo;
76-
77- grail_update_time(gbe->grail, grail_time);
78+ uint64_t server_time = geis_grail_xsync_get_server_time(&event);
79+ if (server_time)
80+ grail_update_time(gbe->grail, server_time);
81 continue;
82 }
83
84@@ -996,57 +978,6 @@
85 return has_xi2;
86 }
87
88-
89-/**
90- * Gets a synchronization timer from the X11 server (if one is available).
91- *
92- * @param[in] gbe The grail back end
93- *
94- * @returns a valid syncronization timer from the X11 server is an appropriate
95- * one is available, otherwise returns a value < 0.
96- */
97-static XSyncCounter
98-_geis_grail_x11_get_timer(GeisGrailBackend gbe)
99-{
100- XSyncCounter x11_timer = -1;
101-
102- /* check for the presence of the XSync extension */
103- int event;
104- int error;
105- if (XSyncQueryExtension(gbe->display, &event, &error) != True)
106- {
107- geis_warning("XSync extension is not available");
108- goto final_exit;
109- }
110-
111- /* initialize the XSync extension */
112- int major;
113- int minor;
114- if (XSyncInitialize(gbe->display, &major, &minor) != True)
115- {
116- geis_warning("failed to initialize XSync extension");
117- goto final_exit;
118- }
119-
120- /* find the server-time counter */
121- int num_system_counters;
122- XSyncSystemCounter *counters;
123- counters = XSyncListSystemCounters(gbe->display, &num_system_counters);
124- for (int i = 0; i < num_system_counters; ++i)
125- {
126- if (0 == strcmp(counters[i].name, "SERVERTIME"))
127- {
128- x11_timer = counters[i].counter;
129- break;
130- }
131- }
132- XSyncFreeSystemCounterList(counters);
133-
134-final_exit:
135- return x11_timer;
136-}
137-
138-
139 /**
140 * Connects to the X11 server and sets up processing on X11 events.
141 *
142@@ -1074,7 +1005,6 @@
143 }
144
145 gbe->root_window = DefaultRootWindow(gbe->display);
146- gbe->x11_timer = _geis_grail_x11_get_timer(gbe);
147
148 /* install an X11 event callback */
149 geis_multiplex_fd(gbe->geis,
150@@ -1222,13 +1152,17 @@
151
152 if (_geis_grail_open_x11_connection(gbe))
153 {
154+ gbe->xsync = geis_grail_xsync_new(gbe->display);
155+ if (!gbe->xsync)
156+ goto unwind_x11;
157+
158 _geis_grail_subscribe_x11_device_events(gbe);
159
160 UFStatus frame_status = frame_x11_new(gbe->display, &gbe->frame);
161 if (frame_status != UFStatusSuccess)
162 {
163 geis_error("failed to create utouch frame instance");
164- goto unwind_x11;
165+ goto unwind_xsync;
166 }
167 geis_multiplex_fd(gbe->geis,
168 frame_get_fd(gbe->frame),
169@@ -1300,6 +1234,8 @@
170 unwind_frame:
171 geis_demultiplex_fd(gbe->geis, frame_get_fd(gbe->frame));
172 frame_x11_delete(gbe->frame);
173+unwind_xsync:
174+ geis_grail_xsync_delete(gbe->xsync);
175 unwind_x11:
176 geis_demultiplex_fd(gbe->geis, ConnectionNumber(gbe->display));
177 XCloseDisplay(gbe->display);
178@@ -1338,6 +1274,7 @@
179 grail_delete_v3(gbe->grail);
180 geis_demultiplex_fd(gbe->geis, frame_get_fd(gbe->frame));
181 frame_x11_delete(gbe->frame);
182+ geis_grail_xsync_delete(gbe->xsync);
183 geis_demultiplex_fd(gbe->geis, ConnectionNumber(gbe->display));
184
185 XErrorHandler old_x_handler = XSetErrorHandler(_grail_be_x_error_handler);
186
187=== added file 'libutouch-geis/backend/grail/geis_grail_xsync.c'
188--- libutouch-geis/backend/grail/geis_grail_xsync.c 1970-01-01 00:00:00 +0000
189+++ libutouch-geis/backend/grail/geis_grail_xsync.c 2012-03-30 16:56:28 +0000
190@@ -0,0 +1,226 @@
191+/**
192+ * @file geis_grail_xsync.c
193+ * @brief Handles XSync lib usage for the GEIS grail backend
194+ */
195+/*
196+ * Copyright 2012 Canonical Ltd.
197+ *
198+ * This file is part of UTouch GEIS.
199+ *
200+ * UTouch GEIS is free software: you can redistribute it and/or modify it
201+ * under the terms of the GNU Lesser General Public License as published by the
202+ * Free Software Foundation, either version 3 of the License, or (at your
203+ * option) any later version.
204+ *
205+ * UTouch GEIS is distributed in the hope that it will be useful, but WITHOUT
206+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
207+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
208+ * details.
209+ *
210+ * You should have received a copy of the GNU Lesser General Public License
211+ * along with UTouch GEIS. If not, see <http://www.gnu.org/licenses/>.
212+ */
213+
214+#include "geis_grail_xsync.h"
215+
216+#include "geis_bag.h"
217+#include "geis_logging.h"
218+#include "geis/geis.h"
219+#include <X11/extensions/sync.h>
220+#include <string.h>
221+
222+struct GeisGrailAlarm {
223+ XSyncAlarm xsync_alarm;
224+ uint64_t timeout;
225+};
226+
227+struct GeisGrailXSync
228+{
229+ Display *display;
230+ XSyncCounter server_time_counter;
231+ int xsync_event_base;
232+ GeisBag alarms; /* List of GeisGrailAlarm, describing existing alarms */
233+};
234+
235+GeisGrailXSync
236+geis_grail_xsync_new(Display *display)
237+{
238+ GeisGrailXSync self = malloc(sizeof(struct GeisGrailXSync));
239+ if (!self)
240+ {
241+ geis_error("failed to allocate new GeisGrailXSync");
242+ goto return_failure;
243+ }
244+
245+ self->display = display;
246+ self->xsync_event_base = -1;
247+
248+ self->alarms = geis_bag_new(sizeof(struct GeisGrailAlarm), 4, 4.0f);
249+ if (!self->alarms)
250+ {
251+ geis_error("failed to create GeisGrailXSync.alarms bag");
252+ goto return_failure;
253+ }
254+
255+ /* check for the presence of the XSync extension */
256+ int error_base;
257+ if (XSyncQueryExtension(self->display, &self->xsync_event_base, &error_base) != True)
258+ {
259+ geis_warning("XSync extension is not available");
260+ goto return_failure;
261+ }
262+
263+ /* initialize the XSync extension */
264+ int major;
265+ int minor;
266+ if (XSyncInitialize(self->display, &major, &minor) != True)
267+ {
268+ geis_warning("failed to initialize XSync extension");
269+ goto return_failure;
270+ }
271+
272+ /* find the server-time counter */
273+ int num_system_counters;
274+ XSyncSystemCounter *counters;
275+ counters = XSyncListSystemCounters(self->display, &num_system_counters);
276+ GeisBoolean found_counter = GEIS_FALSE;
277+ for (int i = 0; i < num_system_counters; ++i)
278+ {
279+ if (0 == strcmp(counters[i].name, "SERVERTIME"))
280+ {
281+ self->server_time_counter = counters[i].counter;
282+ found_counter = GEIS_TRUE;
283+ break;
284+ }
285+ }
286+ XSyncFreeSystemCounterList(counters);
287+ if (!found_counter)
288+ geis_warning("couldn't find SERVERTIME XSyncCounter");
289+
290+ return self;
291+
292+return_failure:
293+ if (self->alarms)
294+ geis_bag_delete(self->alarms);
295+
296+ if (self)
297+ free(self);
298+
299+ return NULL;
300+}
301+
302+void
303+geis_grail_xsync_delete(GeisGrailXSync self)
304+{
305+ free(self);
306+}
307+
308+/**
309+ * Creates and returns an XSyncAlarm configured with the given timeout or
310+ * None on failure
311+ */
312+static XSyncAlarm
313+_geis_grail_xsync_create_alarm(GeisGrailXSync self, uint64_t timeout)
314+{
315+ XSyncAlarmAttributes alarm_attributes;
316+ alarm_attributes.trigger.counter = self->server_time_counter;
317+ alarm_attributes.trigger.value_type = XSyncAbsolute;
318+ XSyncIntsToValue(&alarm_attributes.trigger.wait_value, timeout & 0xffffffff,
319+ timeout & 0xffffffff00000000);
320+ alarm_attributes.trigger.test_type = XSyncPositiveComparison;
321+ alarm_attributes.events = True;
322+
323+ return XSyncCreateAlarm(self->display,
324+ XSyncCACounter | XSyncCAValueType | XSyncCAValue
325+ | XSyncCATestType | XSyncCAEvents,
326+ &alarm_attributes);
327+}
328+
329+/**
330+ * Returns GEIS_TRUE if an XSyncAlarm exists for the given timeout or
331+ * GEIS_FALSE otherwise
332+ */
333+static GeisBoolean
334+_geis_grail_xsync_alarm_exists_for_timeout(GeisGrailXSync self, uint64_t timeout)
335+{
336+ for (GeisSize i = 0, alarms_count = geis_bag_count(self->alarms);
337+ i < alarms_count; ++i)
338+ {
339+ struct GeisGrailAlarm *alarm = geis_bag_at(self->alarms, i);
340+ if (alarm->timeout == timeout)
341+ return GEIS_TRUE;
342+ }
343+ return GEIS_FALSE;
344+}
345+
346+void
347+geis_grail_xsync_set_timeout(GeisGrailXSync self, uint64_t timeout)
348+{
349+ if (_geis_grail_xsync_alarm_exists_for_timeout(self, timeout))
350+ return;
351+
352+ struct GeisGrailAlarm alarm;
353+
354+ alarm.xsync_alarm = _geis_grail_xsync_create_alarm(self, timeout);
355+ if (alarm.xsync_alarm == None)
356+ {
357+ geis_error("failed to create an XSync alarm.");
358+ return;
359+ }
360+
361+ alarm.timeout = timeout;
362+
363+ geis_bag_append(self->alarms, &alarm);
364+}
365+
366+/**
367+ * If the given XSyncAlarm exists, it's destroyed, removed from the
368+ * list of alarms and the function returns GEIS_TRUE.
369+ *
370+ * If the given XSyncAlarm doesn't exist, the function returns GEIS_FALSE.
371+ */
372+static GeisBoolean
373+_geis_grail_xsync_destroy_alarm(GeisGrailXSync self, XSyncAlarm xsync_alarm)
374+{
375+ for (GeisSize i = 0, alarms_count = geis_bag_count(self->alarms);
376+ i < alarms_count; ++i)
377+ {
378+ struct GeisGrailAlarm *alarm = geis_bag_at(self->alarms, i);
379+ if (alarm->xsync_alarm == xsync_alarm)
380+ {
381+ if (XSyncDestroyAlarm(self->display, xsync_alarm) == False)
382+ geis_error("failed to destroy XSync alarm");
383+
384+ geis_bag_remove(self->alarms, i);
385+ return GEIS_TRUE;
386+ }
387+ }
388+ return GEIS_FALSE;
389+}
390+
391+GeisBoolean
392+geis_grail_xsync_is_timeout(GeisGrailXSync self, const XEvent *event)
393+{
394+ if (event->type != (self->xsync_event_base + XSyncAlarmNotify))
395+ return GEIS_FALSE;
396+
397+ const XSyncAlarmNotifyEvent *alarm_notify =
398+ (const XSyncAlarmNotifyEvent *) event;
399+
400+ /* If the destruction isn't successful it means that this alarm has already
401+ been destroyed due to a previous XSyncAlarmNotifyEvent. Therefore its
402+ corresponding timeout has already happened and we shouldn't confirm it
403+ again. */
404+ return _geis_grail_xsync_destroy_alarm(self, alarm_notify->alarm);
405+}
406+
407+uint64_t
408+geis_grail_xsync_get_server_time(const XEvent *event)
409+{
410+ const XSyncAlarmNotifyEvent *alarm_notify =
411+ (const XSyncAlarmNotifyEvent *) event;
412+
413+ XSyncValue time = alarm_notify->counter_value;
414+
415+ return (uint64_t)XSyncValueHigh32(time) << 32 | XSyncValueLow32(time);
416+}
417
418=== added file 'libutouch-geis/backend/grail/geis_grail_xsync.h'
419--- libutouch-geis/backend/grail/geis_grail_xsync.h 1970-01-01 00:00:00 +0000
420+++ libutouch-geis/backend/grail/geis_grail_xsync.h 2012-03-30 16:56:28 +0000
421@@ -0,0 +1,89 @@
422+/**
423+ * @file geis_grail_xsync.h
424+ * @brief Handles XSync lib usage for the GEIS grail backend
425+ */
426+/*
427+ * Copyright 2012 Canonical Ltd.
428+ *
429+ * This file is part of UTouch GEIS.
430+ *
431+ * UTouch GEIS is free software: you can redistribute it and/or modify it
432+ * under the terms of the GNU Lesser General Public License as published by the
433+ * Free Software Foundation, either version 3 of the License, or (at your
434+ * option) any later version.
435+ *
436+ * UTouch GEIS is distributed in the hope that it will be useful, but WITHOUT
437+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
438+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
439+ * details.
440+ *
441+ * You should have received a copy of the GNU Lesser General Public License
442+ * along with UTouch GEIS. If not, see <http://www.gnu.org/licenses/>.
443+ */
444+
445+#ifndef GEIS_BACKEND_GRAIL_XSYNC_H_
446+#define GEIS_BACKEND_GRAIL_XSYNC_H_
447+
448+#include <X11/Xlib.h> // For Display and XEvent
449+#include <stdint.h> // For uint64_t
450+#include "geis/geisimpl.h" // For GeisBoolean
451+
452+/**
453+ * The opaque "X Synchronization Extension Library" handler
454+ *
455+ * It facilitates and encapsulates the use of the XSync extension. Specially
456+ * by managing the XSyncAlarm instances needed to implement timeouts.
457+ */
458+typedef struct GeisGrailXSync *GeisGrailXSync;
459+
460+/**
461+ * Constructs a new GeisGrailXSync instance.
462+ *
463+ * It returns NULL if the initialization wasn't successful.
464+ *
465+ * OBS: Having multiple GeisGrailXSync instances is not supported and doesn't
466+ * make sense.
467+ */
468+GeisGrailXSync
469+geis_grail_xsync_new(Display *display);
470+
471+/**
472+ * Destroys the given GeisGrailXSync
473+ */
474+void
475+geis_grail_xsync_delete(GeisGrailXSync xsync);
476+
477+
478+/**
479+ * Creates an XSyncAlarm with the given timeout
480+ *
481+ * If there's already an XSyncAlarm with that timeout,
482+ * nothing is done.
483+ *
484+ * An XSyncAlarmNotifyEvent will be sent once that timeout
485+ * is reached on the server.
486+ */
487+void
488+geis_grail_xsync_set_timeout(GeisGrailXSync xsync, uint64_t timeout);
489+
490+/**
491+ * Returns true if the given XEvent is an XSyncAlarmNotifyEvent for
492+ * one of the existing XSyncAlarm instances and false otherwise.
493+ *
494+ * It also takes care of destroying the XSyncAlarm that had its
495+ * timeout reached.
496+ */
497+GeisBoolean
498+geis_grail_xsync_is_timeout(GeisGrailXSync xsync, const XEvent *event);
499+
500+/**
501+ * Gets the server time contained in the given event.
502+ *
503+ * That event must be an XSyncAlarmNotifyEvent. It will be the case when
504+ * geis_grail_xsync_is_timeout() returns GeisTrue for it.
505+ *
506+ */
507+uint64_t
508+geis_grail_xsync_get_server_time(const XEvent *event);
509+
510+#endif // GEIS_BACKEND_GRAIL_XSYNC_H_

Subscribers

People subscribed via source and target branches