2
* @file geis_grail_xsync.c
3
* @brief Handles XSync lib usage for the GEIS grail backend
6
* Copyright 2012 Canonical Ltd.
8
* This file is part of GEIS.
10
* GEIS is free software: you can redistribute it and/or modify it
11
* under the terms of the GNU Lesser General Public License as published by the
12
* Free Software Foundation, either version 3 of the License, or (at your
13
* option) any later version.
15
* GEIS is distributed in the hope that it will be useful, but WITHOUT
16
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
20
* You should have received a copy of the GNU Lesser General Public License
21
* along with GEIS. If not, see <http://www.gnu.org/licenses/>.
24
#include "geis_grail_xsync.h"
27
#include "geis_logging.h"
28
#include "geis/geis.h"
29
#include <X11/extensions/sync.h>
32
struct GeisGrailAlarm {
33
XSyncAlarm xsync_alarm;
40
XSyncCounter server_time_counter;
42
GeisBag alarms; /* List of GeisGrailAlarm, describing existing alarms */
46
geis_grail_xsync_new(Display *display)
48
GeisGrailXSync self = malloc(sizeof(struct GeisGrailXSync));
51
geis_error("failed to allocate new GeisGrailXSync");
55
self->display = display;
56
self->xsync_event_base = -1;
58
self->alarms = geis_bag_new(sizeof(struct GeisGrailAlarm), 4, 4.0f);
61
geis_error("failed to create GeisGrailXSync.alarms bag");
65
/* check for the presence of the XSync extension */
67
if (XSyncQueryExtension(self->display, &self->xsync_event_base, &error_base) != True)
69
geis_warning("XSync extension is not available");
73
/* initialize the XSync extension */
76
if (XSyncInitialize(self->display, &major, &minor) != True)
78
geis_warning("failed to initialize XSync extension");
82
/* find the server-time counter */
83
int num_system_counters;
84
XSyncSystemCounter *counters;
85
counters = XSyncListSystemCounters(self->display, &num_system_counters);
86
GeisBoolean found_counter = GEIS_FALSE;
87
for (int i = 0; i < num_system_counters; ++i)
89
if (0 == strcmp(counters[i].name, "SERVERTIME"))
91
self->server_time_counter = counters[i].counter;
92
found_counter = GEIS_TRUE;
96
XSyncFreeSystemCounterList(counters);
98
geis_warning("couldn't find SERVERTIME XSyncCounter");
104
geis_bag_delete(self->alarms);
113
geis_grail_xsync_delete(GeisGrailXSync self)
119
* Creates and returns an XSyncAlarm configured with the given timeout or
123
_geis_grail_xsync_create_alarm(GeisGrailXSync self, uint64_t timeout)
125
XSyncAlarmAttributes alarm_attributes;
126
alarm_attributes.trigger.counter = self->server_time_counter;
127
alarm_attributes.trigger.value_type = XSyncAbsolute;
128
XSyncIntsToValue(&alarm_attributes.trigger.wait_value, timeout & 0xffffffff,
129
timeout & 0xffffffff00000000);
130
alarm_attributes.trigger.test_type = XSyncPositiveComparison;
131
alarm_attributes.events = True;
133
return XSyncCreateAlarm(self->display,
134
XSyncCACounter | XSyncCAValueType | XSyncCAValue
135
| XSyncCATestType | XSyncCAEvents,
140
* Returns GEIS_TRUE if an XSyncAlarm exists for the given timeout or
141
* GEIS_FALSE otherwise
144
_geis_grail_xsync_alarm_exists_for_timeout(GeisGrailXSync self, uint64_t timeout)
146
for (GeisSize i = 0, alarms_count = geis_bag_count(self->alarms);
147
i < alarms_count; ++i)
149
struct GeisGrailAlarm *alarm = geis_bag_at(self->alarms, i);
150
if (alarm->timeout == timeout)
157
geis_grail_xsync_set_timeout(GeisGrailXSync self, uint64_t timeout)
159
if (_geis_grail_xsync_alarm_exists_for_timeout(self, timeout))
162
struct GeisGrailAlarm alarm;
164
alarm.xsync_alarm = _geis_grail_xsync_create_alarm(self, timeout);
165
if (alarm.xsync_alarm == None)
167
geis_error("failed to create an XSync alarm.");
170
/* make sure xserver creates the alarm now, otherwise it could in theory
171
happen only after the desired timeout period */
172
XFlush(self->display);
174
alarm.timeout = timeout;
176
geis_bag_append(self->alarms, &alarm);
180
* If the given XSyncAlarm exists, it's destroyed, removed from the
181
* list of alarms and the function returns GEIS_TRUE.
183
* If the given XSyncAlarm doesn't exist, the function returns GEIS_FALSE.
186
_geis_grail_xsync_destroy_alarm(GeisGrailXSync self, XSyncAlarm xsync_alarm)
188
for (GeisSize i = 0, alarms_count = geis_bag_count(self->alarms);
189
i < alarms_count; ++i)
191
struct GeisGrailAlarm *alarm = geis_bag_at(self->alarms, i);
192
if (alarm->xsync_alarm == xsync_alarm)
194
if (XSyncDestroyAlarm(self->display, xsync_alarm) == False)
195
geis_error("failed to destroy XSync alarm");
197
geis_bag_remove(self->alarms, i);
205
geis_grail_xsync_is_timeout(GeisGrailXSync self, const XEvent *event)
207
if (event->type != (self->xsync_event_base + XSyncAlarmNotify))
210
const XSyncAlarmNotifyEvent *alarm_notify =
211
(const XSyncAlarmNotifyEvent *) event;
213
/* If the destruction isn't successful it means that this alarm has already
214
been destroyed due to a previous XSyncAlarmNotifyEvent. Therefore its
215
corresponding timeout has already happened and we shouldn't confirm it
217
return _geis_grail_xsync_destroy_alarm(self, alarm_notify->alarm);
221
geis_grail_xsync_get_server_time(const XEvent *event)
223
const XSyncAlarmNotifyEvent *alarm_notify =
224
(const XSyncAlarmNotifyEvent *) event;
226
XSyncValue time = alarm_notify->counter_value;
228
return (uint64_t)XSyncValueHigh32(time) << 32 | XSyncValueLow32(time);