1 /*
2
3 Copyright (c) 2001-2007 Michael Terry
4 Copyright (c) 2013-2014 Arthur Borsboom
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 */
21
22 #include "../config.h"
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <glib/gi18n.h>
27
28 #include "xpad-periodic.h"
29
30 #ifdef SHOW_DEBUG
31 # define G_PRINT_DBG g_print
32 #else
33 # define G_PRINT_DBG gprint_ignore
34 #endif
35
36 #define TIMEOUT_SECONDS 4
37
38 struct sigref_ {
39 const char * signame;
40 XpadPeriodicFunc func_ptr;
41 gpointer data;
42 };
43
44 typedef struct sigref_ Xpadsigref;
45
46 typedef struct {
47 /************************
48 count = a clock tick count
49 after_id = the timeout id
50 ************************/
51 int count;
52 int after_id;
53
54 /************************
55 template = a list of signal names and function pointers
56 template_len = the length of 'template'
57 sigs = a list of signal names, function pointers and data
58 sigs_len = the length of 'sigs'
59 ************************/
60 Xpadsigref * template;
61 int template_len;
62 Xpadsigref * sigs;
63 int sigs_len;
64 } XpadPeriodic;
65
66
67 /* prototypes */
68 static gint xppd_intercept (gpointer);
69 static gint gprint_ignore(const char *, ...);
70 static void xpad_periodic_signal (const char * cbname, void * xpad_pad);
71 static void xpad_periodic_error_exit (const char *, ...);
72
73 static gboolean str_equal (const char *, const char *);
74
75 /* global variables */
76 static XpadPeriodic xpptr [1];
77 static gboolean is_activated;
78
79 /* Functions start here */
80
xpad_periodic_init(void)81 gboolean xpad_periodic_init (void)
82 {
83 memset(xpptr, 0, sizeof(*xpptr));
84 xpptr->after_id = (gint) g_timeout_add_seconds(TIMEOUT_SECONDS, xppd_intercept, xpptr);
85
86 /* Allocate space for the signal references. */
87 int tlen = xpptr->template_len = 5;
88 int slen = xpptr->sigs_len = 200;
89 xpptr->template = g_malloc0((gsize) tlen * sizeof(Xpadsigref));
90 xpptr->sigs = g_malloc0((gsize) slen * sizeof(Xpadsigref));
91 is_activated = TRUE;
92
93 return is_activated;
94 }
95
xpad_periodic_close(void)96 void xpad_periodic_close (void)
97 {
98 is_activated = FALSE;
99 if (xpptr->after_id) { g_source_remove((guint) xpptr->after_id); }
100 /* Free the signal references memory. */
101 g_free(xpptr->template);
102 g_free(xpptr->sigs);
103 /* Signal that this structure is now cleared. */
104 memset(xpptr, 0, sizeof(*xpptr));
105 }
106
107 /************************
108 xppd_intercept - intercepts a timer tick
109
110 This function intercepts a timer tick and iterates
111 over the signal references. Any signal references that
112 are fully stocked with signal names, function pointers
113 and data pointers are invoked.
114
115 IOW (In other words), the function pointer is called with the
116 right data pointer.
117 ************************/
118
xppd_intercept(gpointer cdata)119 gint xppd_intercept (gpointer cdata)
120 {
121 /* A dirty way to silence the compiler for these unused variables. */
122 (void) cdata;
123
124 int cnt=0;
125 XpadPeriodicFunc fnptr=0;
126 xpptr->count++; /* increment tick count */
127
128 G_PRINT_DBG("xppd tick: %4d\n", xpptr->count);
129
130 for (cnt = 0; cnt < xpptr->sigs_len; ++cnt) {
131 Xpadsigref * sig_item = xpptr->sigs + cnt;
132 if (sig_item->signame && sig_item->func_ptr && sig_item->data) {
133 fnptr = sig_item->func_ptr;
134 (*fnptr)(sig_item->data);
135 G_PRINT_DBG("invoked %s : %p : %p\n", sig_item->signame,
136 sig_item->func_ptr, sig_item->data);
137 memset(sig_item, 0, sizeof(*sig_item));
138 }
139 }
140
141 return TRUE;
142 }
143
144 /************************
145 Xpad_periodic_set_callback():
146 This function prepares a callback function to be invoked
147 for an event name such as "save-content" or "save-info".
148
149 cbname : event name (or callback function name)
150 func : function address
151
152 Returns true if a callback was registered.
153 ************************/
154
xpad_periodic_set_callback(const char * cbname,XpadPeriodicFunc func)155 gboolean xpad_periodic_set_callback (const char * cbname, XpadPeriodicFunc func)
156 {
157 int index = 0;
158 gboolean isdone=FALSE;
159 if (0 == func) { return FALSE; }
160 if (0 == cbname || 0==*cbname) { return FALSE; }
161
162 /* Find an open slot for signal (callback) references and
163 insert this one. */
164 for (index = 0; index < xpptr->template_len; ++index) {
165 /* Store a pointer to the current signal item. */
166 Xpadsigref * sig_item = xpptr->template + index;
167
168 /* If it's empty, set it. */
169 if (0 == sig_item->signame) {
170 sig_item->signame = cbname;
171 sig_item->func_ptr = func;
172 isdone = TRUE;
173 break;
174 }
175 }
176
177 if (! isdone) {
178 g_printerr("Failed to install signal callback: %s\n", cbname);
179 exit(1);
180 }
181
182 return isdone;
183 }
184
xpad_periodic_save_info_delayed(void * xpad_pad)185 void xpad_periodic_save_info_delayed (void * xpad_pad)
186 {
187 if (is_activated) {
188 xpad_periodic_signal("save-info", xpad_pad);
189 }
190 }
191
xpad_periodic_save_content_delayed(void * xpad_pad)192 void xpad_periodic_save_content_delayed (void * xpad_pad)
193 {
194 if (is_activated) {
195 xpad_periodic_signal("save-content", xpad_pad);
196 }
197 }
198
xpad_periodic_signal(const char * cbname,void * xpad_pad)199 static void xpad_periodic_signal (const char * cbname, void * xpad_pad)
200 {
201 gboolean isdone = FALSE;
202 int tnx = 0;
203 int snx = 0;
204 XpadPeriodicFunc func_ptr = 0;
205 Xpadsigref * sig_item = 0;
206
207 if (0 == cbname || 0 == *cbname) {
208 return;
209 }
210
211 if (0 == xpad_pad) {
212 return;
213 }
214
215 /* Get the callback function address */
216 for (tnx = 0; tnx < xpptr->template_len; ++tnx) {
217 if (str_equal(xpptr->template[tnx].signame, cbname)) {
218 func_ptr = xpptr->template[tnx].func_ptr;
219 break;
220 }
221 }
222
223 /* If there is no callback address, we can't continue. */
224 if (! func_ptr) {
225 xpad_periodic_error_exit ("Can't find signal function address: %s\n", cbname);
226 }
227
228 /* Check that this event is not already present.
229 If it is present, don't do anything more. */
230 for (snx = 0; snx < xpptr->sigs_len; ++snx) {
231 sig_item = xpptr->sigs + snx;
232
233 if (str_equal(sig_item->signame, cbname) && xpad_pad == sig_item->data) {
234 G_PRINT_DBG("Already got signal for this pad: %s\n", cbname);
235 return;
236 }
237 }
238
239 /* Find a suitable slot for the signal reference and set it. */
240 for (snx = 0; snx < xpptr->sigs_len; ++snx) {
241 gint doadd = 0;
242 sig_item = xpptr->sigs + snx;
243
244 doadd += (str_equal(sig_item->signame, cbname) && xpad_pad == sig_item->data);
245 doadd += (0 == sig_item->signame);
246
247 if (doadd) {
248 sig_item->signame = cbname;
249 sig_item->func_ptr = func_ptr;
250 sig_item->data = xpad_pad;
251 isdone = TRUE;
252 break;
253 }
254 }
255
256 if (! isdone) {
257 xpad_periodic_error_exit("Could not schedule event: %s\n", cbname);
258 }
259 }
260
str_equal(const char * s1,const char * s2)261 gboolean str_equal (const char * s1, const char * s2)
262 {
263 if (0 == s1 || 0==s2) { return FALSE; }
264 if (s1 == s2) { return TRUE; }
265 return (0 == strcmp(s1, s2));
266 }
267
gprint_ignore(const char * fmt,...)268 gint gprint_ignore (const char * fmt, ...)
269 {
270 /* A dirty way to silence the compiler for these unused variables. */
271 (void) fmt;
272
273 return 0;
274 }
275
xpad_periodic_error_exit(const char * fmt,...)276 static void xpad_periodic_error_exit (const char * fmt, ...)
277 {
278 va_list app;
279 va_start(app, fmt);
280 g_printerr(fmt, app);
281 va_end(app);
282 exit(1);
283 }
284