76f82409ad5bcddf4201fa2e17b60b23c20c7796
[rodin/chimara.git] / src / event.c
1 #include "event.h"
2 #include <string.h>
3
4 static GQueue *event_queue = NULL;
5 static GMutex *event_lock = NULL;
6 static GCond *event_queue_not_empty = NULL;
7 static GCond *event_queue_not_full = NULL;
8
9 /* Internal function: initialize the event_queue, locking and signalling 
10 objects. */
11 void
12 events_init()
13 {
14         event_queue = g_queue_new();
15         event_lock = g_mutex_new();
16         event_queue_not_empty = g_cond_new();
17         event_queue_not_full = g_cond_new();
18 }
19
20 /* Internal function: free the event queue and all the objects allocated in
21 events_init(). */
22 void
23 events_free()
24 {
25         g_queue_foreach(event_queue, (GFunc)g_free, NULL);
26         g_queue_free(event_queue);
27         g_mutex_free(event_lock);
28         g_cond_free(event_queue_not_empty);
29         g_cond_free(event_queue_not_full);
30 }
31
32 /* Internal function: push an event onto the event queue. If the event queue is
33 full, wait for max three seconds and then drop the event. */
34 void
35 event_throw(glui32 type, winid_t win, glui32 val1, glui32 val2)
36 {
37         GTimeVal timeout;
38         g_get_current_time(&timeout);
39         g_time_val_add(&timeout, 3000000); /* 3 Seconds */
40
41         g_mutex_lock(event_lock);
42
43         /* Wait for room in the event queue */
44         if( g_queue_get_length(event_queue) >= EVENT_QUEUE_MAX_LENGTH )
45                 if( !g_cond_timed_wait(event_queue_not_full, event_lock, &timeout) ) 
46                 {
47                         /* Drop the event after 3 seconds */
48                         g_mutex_unlock(event_lock);
49                         return;
50                 }
51
52         event_t *event = g_new0(event_t, 1);
53         event->type = type;
54         event->win = win;
55         event->val1 = val1;
56         event->val2 = val2;
57         g_queue_push_head(event_queue, event);
58
59         /* Signal that there is an event */
60         g_cond_signal(event_queue_not_empty);
61
62         g_mutex_unlock(event_lock);
63 }
64
65 /**
66  * glk_select:
67  * @event: Pointer to an #event_t.
68  *
69  * Causes the program to wait for an event, and then store it in the structure
70  * pointed to by @event. Unlike most Glk functions that take pointers, the
71  * argument of glk_select() may not be %NULL.
72  *
73  * Most of the time, you only get the events that you request. However, there
74  * are some events which can arrive at any time. This is why you must always
75  * call glk_select() in a loop, and continue the loop until you get the event
76  * you really want.
77  */
78 void
79 glk_select(event_t *event)
80 {
81         g_return_if_fail(event != NULL);
82
83         g_mutex_lock(event_lock);
84
85         /* Wait for an event */
86         if( g_queue_is_empty(event_queue) )
87                 g_cond_wait(event_queue_not_empty, event_lock);
88
89         event_t *retrieved_event = g_queue_pop_tail(event_queue);
90         if(retrieved_event == NULL)
91         {
92                 g_mutex_unlock(event_lock);
93                 g_warning("%s: Retrieved NULL event from non-empty event queue", __func__);
94                 return;
95         }
96         memcpy(event, retrieved_event, sizeof(event_t));
97         g_free(retrieved_event);
98
99         /* Signal that the event queue is no longer full */
100         g_cond_signal(event_queue_not_full);
101
102         g_mutex_unlock(event_lock);
103
104         /* Implementation-defined events */
105         switch(event->type) {
106                 case EVENT_TYPE_QUIT:
107                         g_thread_exit(NULL);
108         }
109 }