d7cb6596cbdd61611536bfe981c5116351b750a5
[rodin/chimara.git] / libchimara / event.c
1 #include "event.h"
2 #include "magic.h"
3 #include "glk.h"
4 #include <string.h>
5
6 #include "chimara-glk-private.h"
7
8 extern ChimaraGlkPrivate *glk_data;
9
10 #define EVENT_TIMEOUT_MICROSECONDS (3000000)
11
12 /* Internal function: push an event onto the event queue. If the event queue is
13 full, wait for max three seconds and then drop the event. If the event queue is
14 NULL, i.e. freed, then fail silently. */
15 void
16 event_throw(glui32 type, winid_t win, glui32 val1, glui32 val2)
17 {
18         if(!glk_data->event_queue)
19                 return;
20
21         GTimeVal timeout;
22         g_get_current_time(&timeout);
23         g_time_val_add(&timeout, EVENT_TIMEOUT_MICROSECONDS);
24
25         g_mutex_lock(glk_data->event_lock);
26
27         /* Wait for room in the event queue */
28         while( g_queue_get_length(glk_data->event_queue) >= EVENT_QUEUE_MAX_LENGTH )
29                 if( !g_cond_timed_wait(glk_data->event_queue_not_full, glk_data->event_lock, &timeout) ) 
30                 {
31                         /* Drop the event after 3 seconds */
32                         g_mutex_unlock(glk_data->event_lock);
33                         return;
34                 }
35
36         event_t *event = g_new0(event_t, 1);
37         event->type = type;
38         event->win = win;
39         event->val1 = val1;
40         event->val2 = val2;
41         g_queue_push_head(glk_data->event_queue, event);
42
43         /* Signal that there is an event */
44         g_cond_signal(glk_data->event_queue_not_empty);
45
46         g_mutex_unlock(glk_data->event_lock);
47 }
48
49 /**
50  * glk_select:
51  * @event: Pointer to an #event_t.
52  *
53  * Causes the program to wait for an event, and then store it in the structure
54  * pointed to by @event. Unlike most Glk functions that take pointers, the
55  * argument of glk_select() may not be %NULL.
56  *
57  * Most of the time, you only get the events that you request. However, there
58  * are some events which can arrive at any time. This is why you must always
59  * call glk_select() in a loop, and continue the loop until you get the event
60  * you really want.
61  */
62 void
63 glk_select(event_t *event)
64 {
65         g_return_if_fail(event != NULL);
66
67         g_mutex_lock(glk_data->event_lock);
68
69         /* Wait for an event */
70         while( g_queue_is_empty(glk_data->event_queue) )
71                 g_cond_wait(glk_data->event_queue_not_empty, glk_data->event_lock);
72
73         event_t *retrieved_event = g_queue_pop_tail(glk_data->event_queue);
74         if(retrieved_event == NULL)
75         {
76                 g_mutex_unlock(glk_data->event_lock);
77                 WARNING("Retrieved NULL event from non-empty event queue");
78                 return;
79         }
80         memcpy(event, retrieved_event, sizeof(event_t));
81         g_free(retrieved_event);
82
83         /* Signal that the event queue is no longer full */
84         g_cond_signal(glk_data->event_queue_not_full);
85
86         g_mutex_unlock(glk_data->event_lock);
87         
88         /* Check for interrupt */
89         glk_tick();
90         
91         /* If an abort event was generated, the thread should have exited by now */
92         g_assert(event->type != evtype_Abort);
93 }
94