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