aboutsummaryrefslogtreecommitdiffstats
path: root/timer_group.c
blob: c50343f0f885116426ea6a6d81f60740f2707e97 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
/* $Id: timer_group.c 1185 2012-03-26 13:24:37Z mjona $
 * $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/timer_group.c $
 *
 * Generic grouping of widgets that have been set to the same update
 * interval, thus allowing synchronized updates.
 *
 * Copyright (C) 2010 Martin Zuther <code@mzuther.de>
 * Copyright (C) 2010 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
 *
 * Based on "timer.c" which is
 * Copyright (C) 2003, 2004 Michael Reinelt <michael@reinelt.co.at>
 * Copyright (C) 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*
 * Exported functions:
 *
 * void timer_process_group(void *data)
 *
 *  Process all widgets of a timer group; if the timer group only
 *  contains one-shot timers, it will be deleted after processing.
 *
 *
 * void timer_exit_group(void)
 *
 *   Release all timer groups and widgets and free the associated
 *   memory blocks.
 *
 *
 * int timer_add_widget(void (*callback) (void *data), void *data,
 *     const int interval, const int one_shot)
 *
 *   Add widget to timer group of the specified update interval
 *   (also creates a new timer group if necessary).
 *
 *
 * int timer_remove_widget(void (*callback) (void *data), void *data)
 *
 *   Remove widget from the timer group with the specified update
 *   interval (also removes corresponding timer group if empty).
 *
 */


#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "debug.h"
#include "cfg.h"
#include "timer.h"
#include "timer_group.h"

#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif


/* structure for storing all relevant data of a single timer group */
typedef struct TIMER_GROUP {
    /* group's triggering interval in milliseconds;
       this will be used to identify a specific timer group and also
       as callback data for the underlying generic timer */
    int interval;

    /* marks timer group as being active (so it will get processed) or
       inactive (which means the timer group has been deleted and its
       allocated memory may be re-used) */
    int active;
} TIMER_GROUP;

/* number of allocated timer group slots */
int nTimerGroups = 0;

/* pointer to memory allocated for storing the timer group slots */
TIMER_GROUP *TimerGroups = NULL;


/* structure for storing all relevant timer data of a single widget */
typedef struct TIMER_GROUP_WIDGET {
    /* pointer to function of type void func(void *data) that will be
       called when the timer is processed; it will also be used to
       identify a specific widget */
    void (*callback) (void *data);

    /* pointer to data which will be passed to the callback function;
       it will also be used to identify a specific widget */
    void *data;

    /* specifies the timer's triggering interval in milliseconds; it
       will also be used to identify a specific widget */
    int interval;

    /* specifies whether the timer should trigger indefinitely until
       it is deleted (value of 0) or only once (all other values) */
    int one_shot;

    /* marks timer as being active (so it will get processed) or
       inactive (which means the timer has been deleted and its
       allocated memory may be re-used) */
    int active;
} TIMER_GROUP_WIDGET;

/* number of allocated widget slots */
int nTimerGroupWidgets = 0;

/* pointer to memory allocated for storing the widget slots */
TIMER_GROUP_WIDGET *TimerGroupWidgets = NULL;


int timer_group_exists(const int interval)
/*  Check whether a timer group for the specified interval exists.

    interval (integer): the sought-after triggering interval in
    milliseconds

	return value (integer): returns a value of 1 if timer group
	exists; otherwise returns a value of 0
*/
{
    int group;			/* current timer group's ID */

    /* loop through the timer group slots to search for one that
       matches the specified interval */
    for (group = 0; group < nTimerGroups; group++) {
	/* skip inactive (i.e. deleted) timer groups */
	if (TimerGroups[group].active == TIMER_INACTIVE)
	    continue;

	if (TimerGroups[group].interval == interval) {
	    /* matching timer group found, so signal success by returning
	       a value of 1 */
	    return 1;
	}
    }

    /* matching timer group not found, so signal failure by returning
       a value of 0 */
    return 0;
}


int timer_add_group(const int interval)
/*  Create a new timer group (unless it already exists) and link it to
	the timer queue.

	interval (integer): the new timer group's triggering interval in
	milliseconds

	return value (integer): returns a value of 0 on successful timer
	group creation; otherwise returns a value of -1
*/
{
    /* if timer group for update interval already exists, signal
       success by returning a value of 0 */
    if (timer_group_exists(interval))
	return 0;

    /* display an info message to inform the user that a new timer
       group is being created */
    info("Creating new timer group (%d ms)", interval);

    int group;			/* current timer group's ID */

    /* try to minimize memory usage by looping through timer group
       slots and looking for an inactive timer group */
    for (group = 0; group < nTimerGroups; group++) {
	if (TimerGroups[group].active == TIMER_INACTIVE) {
	    /* we've just found one, so let's reuse it ("group" holds its
	       ID) by breaking the loop */
	    break;
	}
    }

    /* no inactive timer groups (or none at all) found, so we have to
       add a new timer group slot */
    if (group == nTimerGroups) {
	TIMER_GROUP *tmp;

	if ((tmp = realloc(TimerGroups, (nTimerGroups + 1) * sizeof(*TimerGroups))) == NULL) {
	    /* signal unsuccessful timer group creation */
	    return -1;
	}
	TimerGroups = tmp;
	nTimerGroups++;
    }

    /* initialize timer group's interval */
    TimerGroups[group].interval = interval;

    /* set timer group to active so that it is processed and not
       overwritten by the memory optimization routine above */
    TimerGroups[group].active = TIMER_ACTIVE;

    /* finally, request a generic timer that calls this group and
       signal success or failure */
    return timer_add(timer_process_group, &TimerGroups[group].interval, interval, 0);
}


int timer_remove_group(const int interval)
/*  Remove a timer group and unlink it from the timer queue (also
	removes all remaining widget slots in this timer group).

	interval (integer): triggering interval in milliseconds; here, it
    will be used to identify the timer group

	return value (integer): returns a value of 0 on successful timer
	group removal; otherwise returns a value of -1
*/
{
    /* display an info message to inform the user that a timer group
       is being removed */
    info("Removing timer group (%d ms)", interval);

    int group;			/* current timer group's ID */
    int widget;			/* current widget's ID */

    /* loop through the widget slots to look for remaining widgets
       with the specified update interval */
    for (widget = 0; widget < nTimerGroupWidgets; widget++) {
	/* skip inactive (i.e. deleted) widget slots */
	if (TimerGroupWidgets[widget].active == TIMER_INACTIVE)
	    continue;

	if (TimerGroupWidgets[widget].interval == interval) {
	    /* we have found a matching widget slot, so mark it as being
	       inactive; we will not actually delete the slot, so its
	       allocated memory may be re-used */
	    TimerGroupWidgets[widget].active = TIMER_INACTIVE;
	}
    }

    /* loop through timer group slots and try to find the specified
       timer group slot by looking for its settings */
    for (group = 0; group < nTimerGroups; group++) {
	/* skip inactive (i.e. deleted) timer groups */
	if (TimerGroups[group].active == TIMER_INACTIVE)
	    continue;

	if (TimerGroups[group].interval == interval) {
	    /* we have found the timer group slot, so mark it as being
	       inactive; we will not actually delete the slot, so its
	       allocated memory may be re-used */
	    TimerGroups[group].active = TIMER_INACTIVE;

	    /* remove the generic timer that calls this group */
	    if (timer_remove(timer_process_group, &TimerGroups[group].interval)) {
		/* signal successful removal of timer group */
		return 0;
	    } else {
		/* an error occurred on generic timer removal, so signal
		   failure by returning a value of -1 */
		return -1;
	    }
	}
    }

    /* we have NOT found the timer group slot, so signal failure by
       returning a value of -1 */
    return -1;
}


int timer_remove_empty_group(const int interval)
/*  Remove timer group *only* if it contains no more widget slots.

	interval (integer): triggering interval in milliseconds; here, it
    will be used to identify the timer group

	return value (integer): returns a value of 0 on successful
	processing; otherwise returns a value of -1
*/
{
    int widget;			/* current widget's ID */

    /* loop through the widget slots to look for widgets with the
       specified update interval */
    for (widget = 0; widget < nTimerGroupWidgets; widget++) {
	/* skip inactive (i.e. deleted) widget slots */
	if (TimerGroupWidgets[widget].active == TIMER_INACTIVE)
	    continue;

	/* at least one other widget with specified update interval
	   exists, so signal success by returning a value of 0 */
	if (TimerGroupWidgets[widget].interval == interval)
	    return 0;
    }

    /* no other widgets with specified update interval exist, so
       remove corresponding timer group and signal success or
       failure */
    return timer_remove_group(interval);
}


void timer_process_group(void *data)
/*  Process all widgets of a timer group; if the timer group only
	contains one-shot timers, it will be deleted after processing.

	data (void pointer): points to an integer holding the triggering
    interval in milliseconds; here, it will be used to identify the
    timer group

	return value: void
*/
{
    int widget;			/* current widget's ID */

    /* convert callback data to integer (triggering interval in
       milliseconds) */
    int interval = *((int *) data);

    /* sanity check; by now, at least one timer group should be
       instantiated */
    if (nTimerGroups <= 0) {
	/* otherwise, print an error and return early */
	error("Huh? Not even a single timer group to process? Dazed and confused...");
	return;
    }

    /* sanity check; by now, at least one widget slot should be
       instantiated */
    if (nTimerGroupWidgets <= 0) {
	/* otherwise, print an error and return early */
	error("Huh? Not even a single widget slot to process? Dazed and confused...");
	return;
    }

    /* loop through widgets and search for those matching the timer
       group's update interval */
    for (widget = 0; widget < nTimerGroupWidgets; widget++) {
	/* skip inactive (i.e. deleted) widgets */
	if (TimerGroupWidgets[widget].active == TIMER_INACTIVE)
	    continue;

	/* the current widget belongs to the specified timer group */
	if (TimerGroupWidgets[widget].interval == interval) {
	    /* if the widget's callback function has been set, call it and
	       pass the corresponding data */
	    if (TimerGroupWidgets[widget].callback != NULL)
		TimerGroupWidgets[widget].callback(TimerGroupWidgets[widget].data);

	    /* mark one-shot widget as inactive (which means the it has
	       been deleted and its allocated memory may be re-used) */
	    if (TimerGroupWidgets[widget].one_shot) {
		TimerGroupWidgets[widget].active = TIMER_INACTIVE;

		/* also remove the corresponding timer group if it is empty */
		timer_remove_empty_group(interval);
	    }
	}
    }
}


int timer_add_widget(void (*callback) (void *data), void *data, const int interval, const int one_shot)
/*  Add widget to timer group of the specified update interval
    (also creates a new timer group if necessary).

    callback (void pointer): function of type void func(void *data)
	which will be called whenever the timer group triggers; this
	pointer will also be used to identify a specific widget

	data (void pointer): data which will be passed to the callback
	function; this pointer will also be used to identify a specific
	widget

	interval (integer): specifies the timer's triggering interval in
	milliseconds

	one_shot (integer): specifies whether the timer should trigger
	indefinitely until it is deleted (value of 0) or only once (all
	other values)

	return value (integer): returns a value of 0 on successful widget
	addition; otherwise returns a value of -1
*/
{
    int widget;			/* current widget's ID */

    /* if no timer group for update interval exists, create one */
    if (!timer_group_exists(interval)) {
	/* creation of new timer group failed, so signal failure by
	   returning a value of -1 */
	if (timer_add_group(interval) != 0)
	    return -1;
    }

    /* try to minimize memory usage by looping through the widget
       slots and looking for an inactive widget slot */
    for (widget = 0; widget < nTimerGroupWidgets; widget++) {
	if (TimerGroupWidgets[widget].active == TIMER_INACTIVE) {
	    /* we've just found one, so let's reuse it ("widget" holds its
	       ID) by breaking the loop */
	    break;
	}
    }

    /* no inactive widget slots (or none at all) found, so we have to
       add a new widget slot */
    if (widget == nTimerGroupWidgets) {
	TIMER_GROUP_WIDGET *tmp;

	if ((tmp = realloc(TimerGroupWidgets, (nTimerGroupWidgets + 1) * sizeof(*TimerGroupWidgets))) == NULL) {
	    /* signal unsuccessful creation of widget slot */
	    return -1;
	}
	TimerGroupWidgets = tmp;
	nTimerGroupWidgets++;
    }

    /* initialize widget slot */
    TimerGroupWidgets[widget].callback = callback;
    TimerGroupWidgets[widget].data = data;
    TimerGroupWidgets[widget].interval = interval;
    TimerGroupWidgets[widget].one_shot = one_shot;

    /* set widget slot to active so that it is processed and not
       overwritten by the memory optimization routine above */
    TimerGroupWidgets[widget].active = TIMER_ACTIVE;

    /* signal successful addition of widget slot */
    return 0;
}


int timer_remove_widget(void (*callback) (void *data), void *data)
/*  Remove widget from the timer group with the specified update
    interval (also removes corresponding timer group if empty).

    callback (void pointer): function of type void func(void *data);
	here, it will be used to identify a specific widget

	data (void pointer): data which will be passed to the callback
	function; here, it will be used to identify a specific widget

	return value (integer): returns a value of 0 on successful widget
	removal; otherwise returns a value of -1
*/
{
    int widget;			/* current widget's ID */
    int interval = -1;		/* specified widget's triggering interval in
				   milliseconds */

    /* loop through the widget slots and try to find the specified
       widget slot by looking for its settings */
    for (widget = 0; widget < nTimerGroupWidgets; widget++) {
	/* skip inactive (i.e. deleted) widget slots */
	if (TimerGroupWidgets[widget].active == TIMER_INACTIVE)
	    continue;

	if (TimerGroupWidgets[widget].callback == callback && TimerGroupWidgets[widget].data == data) {
	    /* we have found the widget slot, so mark it as being
	       inactive; we will not actually delete the slot, so its
	       allocated memory may be re-used */
	    TimerGroupWidgets[widget].active = TIMER_INACTIVE;

	    /* store the widget's triggering interval for later use and
	       break the loop */
	    interval = TimerGroupWidgets[widget].interval;
	    break;
	}
    }

    /* if no matching widget was found, signal an error by returning
       a value of -1 */
    if (interval < 0)
	return -1;

    /* if no other widgets with specified update interval exist,
       remove corresponding timer group and signal success or
       failure */
    return timer_remove_empty_group(interval);
}


void timer_exit_group(void)
/*  Release all timer groups and widgets and free the associated
	memory blocks.

	return value: void
*/
{
    int group;			/* current timer group's ID */

    /* loop through all timer groups and remove them one by one */
    for (group = 0; group < nTimerGroups; group++) {
	/* remove generic timer */
	timer_remove(timer_process_group, &TimerGroups[group].interval);
    }

    /* reset number of allocated timer groups */
    nTimerGroups = 0;

    /* free allocated memory containing the timer group slots */
    if (TimerGroups != NULL) {
	free(TimerGroups);
	TimerGroups = NULL;
    }

    /* reset number of allocated widget slots */
    nTimerGroupWidgets = 0;

    /* free allocated memory containing the widget slots */
    if (TimerGroupWidgets != NULL) {
	free(TimerGroupWidgets);
	TimerGroupWidgets = NULL;
    }
}