8 #include "chimara-glk.h"
9 #include "chimara-glk-private.h"
11 extern GPrivate glk_data_key;
13 #define EVENT_TIMEOUT_MICROSECONDS (3000000)
15 /* Internal function: push an event onto the event queue. If the event queue is
16 full, wait for max three seconds and then drop the event. If the event queue is
17 NULL, i.e. freed, then fail silently. */
19 event_throw(ChimaraGlk *glk, glui32 type, winid_t win, glui32 val1, glui32 val2)
21 ChimaraGlkPrivate *priv = CHIMARA_GLK_PRIVATE(glk);
23 if(!priv->event_queue)
26 gint64 timeout = g_get_monotonic_time() + EVENT_TIMEOUT_MICROSECONDS;
28 g_mutex_lock(&priv->event_lock);
30 /* Wait for room in the event queue */
31 while( g_queue_get_length(priv->event_queue) >= EVENT_QUEUE_MAX_LENGTH )
32 if( !g_cond_wait_until(&priv->event_queue_not_full, &priv->event_lock, timeout) )
34 /* Drop the event if the event queue is still not emptying */
35 g_mutex_unlock(&priv->event_lock);
39 event_t *event = g_new0(event_t, 1);
44 g_queue_push_head(priv->event_queue, event);
46 /* Signal that there is an event */
47 g_cond_signal(&priv->event_queue_not_empty);
49 g_mutex_unlock(&priv->event_lock);
52 /* Helper function: Wait for an event in the event queue. If it is a forced
53 * input event, but no windows have an input request of that type, then wait
54 * for the next event and put the forced input event back on top of the queue.
57 get_appropriate_event(event_t *event)
59 ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
61 g_mutex_lock(&glk_data->event_lock);
63 event_t *retrieved_event = NULL;
65 /* Wait for an event */
66 if( g_queue_is_empty(glk_data->event_queue) )
67 g_cond_wait(&glk_data->event_queue_not_empty, &glk_data->event_lock);
69 retrieved_event = g_queue_pop_tail(glk_data->event_queue);
71 /* Signal that the event queue is no longer full */
72 g_cond_signal(&glk_data->event_queue_not_full);
74 g_mutex_unlock(&glk_data->event_lock);
76 if(retrieved_event->type == evtype_ForcedCharInput)
78 /* Check for forced character input in the queue */
80 for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL))
81 if(win->input_request_type == INPUT_REQUEST_CHARACTER || win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE)
85 force_char_input_from_queue(win, event);
86 g_free(retrieved_event);
90 get_appropriate_event(event);
91 g_mutex_lock(&glk_data->event_lock);
92 g_queue_push_tail(glk_data->event_queue, retrieved_event);
93 g_cond_signal(&glk_data->event_queue_not_empty);
94 g_mutex_unlock(&glk_data->event_lock);
97 else if(retrieved_event->type == evtype_ForcedLineInput)
99 /* Check for forced line input in the queue */
101 for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL))
102 if(win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
106 force_line_input_from_queue(win, event);
107 g_free(retrieved_event);
111 get_appropriate_event(event);
112 g_mutex_lock(&glk_data->event_lock);
113 g_queue_push_tail(glk_data->event_queue, retrieved_event);
114 g_cond_signal(&glk_data->event_queue_not_empty);
115 g_mutex_unlock(&glk_data->event_lock);
120 if(retrieved_event == NULL)
122 WARNING("Retrieved NULL event from non-empty event queue");
125 memcpy(event, retrieved_event, sizeof(event_t));
126 g_free(retrieved_event);
132 * @event: Pointer to an #event_t.
134 * Causes the program to wait for an event, and then store it in the structure
135 * pointed to by @event. Unlike most Glk functions that take pointers, the
136 * argument of glk_select() may not be %NULL.
138 * Most of the time, you only get the events that you request. However, there
139 * are some events which can arrive at any time. This is why you must always
140 * call glk_select() in a loop, and continue the loop until you get the event
144 glk_select(event_t *event)
146 g_return_if_fail(event != NULL);
148 /* Flush all window buffers */
150 for(win = glk_window_iterate(NULL, NULL); win != NULL; win = glk_window_iterate(win, NULL)) {
151 if(win->type == wintype_TextBuffer || win->type == wintype_TextGrid)
152 flush_window_buffer(win);
155 ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
157 get_appropriate_event(event);
159 /* Check for interrupt */
162 /* If the event was a line input event, the library must release the buffer */
163 if(event->type == evtype_LineInput && glk_data->unregister_arr)
165 if(event->win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
166 (*glk_data->unregister_arr)(event->win->line_input_buffer_unicode, event->win->line_input_buffer_max_len, "&+#!Iu", event->win->buffer_rock);
168 (*glk_data->unregister_arr)(event->win->line_input_buffer, event->win->line_input_buffer_max_len, "&+#!Cn", event->win->buffer_rock);
171 /* If an abort event was generated, the thread should have exited by now */
172 g_assert(event->type != evtype_Abort);
177 * @event: Return location for an event.
179 * You can also inquire if an event is available, without stopping to wait for
182 * This checks if an internally-spawned event is available. If so, it stores it
183 * in the structure pointed to by @event. If not, it sets
184 * <code>@event->type</code> to %evtype_None. Either way, it returns almost
187 * The first question you now ask is, what is an internally-spawned event?
188 * glk_select_poll() does not check for or return %evtype_CharInput,
189 * %evtype_LineInput, %evtype_MouseInput, or %evtype_Hyperlink events. It is
190 * intended for you to test conditions which may have occurred while you are
191 * computing, and not interfacing with the player. For example, time may pass
192 * during slow computations; you can use glk_select_poll() to see if a
193 * %evtype_Timer event has occurred. (See <link
194 * linkend="chimara-Timer-Events">Timer Events</link>.)
196 * At the moment, glk_select_poll() checks for %evtype_Timer, %evtype_Arrange,
197 * %evtype_Redraw and %evtype_SoundNotify events. But see <link
198 * linkend="chimara-Other-Events">Other Events</link>.
200 * The second question is, what does it mean that glk_select_poll() returns
201 * <quote>almost immediately</quote>? In some Glk libraries, text that you send
202 * to a window is buffered; it does not actually appear until you request player
203 * input with glk_select(). glk_select_poll() attends to this buffer-flushing
204 * task in the same way. (Although it does not do the <quote><computeroutput>Hit
205 * any key to scroll down</computeroutput></quote> waiting which may be done in
206 * glk_select(); that's a player-input task.)
208 * Similarly, on multitasking platforms, glk_select() may yield time to other
209 * processes; and glk_select_poll() does this as well.
211 * The upshot of this is that you should not call glk_select_poll() very often.
212 * If you are not doing much work between player inputs, you should not need to
216 * For example, in a virtual machine interpreter, you should not call
217 * glk_select_poll() after every opcode.
220 * However, if you are doing intense computation, you may wish to call
221 * glk_select_poll() every so often to yield time to other processes. And if you
222 * are printing intermediate results during this computation, you should
223 * glk_select_poll() every so often, so that you can be certain your output will
224 * be displayed before the next glk_select().
227 * However, you should call glk_tick() often — once per opcode in a VM
228 * interpreter. See <link linkend="chimara-The-Tick-Thing">The Tick
233 glk_select_poll(event_t *event)
235 g_return_if_fail(event != NULL);
237 ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
239 event->type = evtype_None;
244 g_mutex_lock(&glk_data->event_lock);
246 if( !g_queue_is_empty(glk_data->event_queue) )
250 for(count = 0; (link = g_queue_peek_nth_link(glk_data->event_queue, count)) != NULL; count++)
252 glui32 type = ((event_t *)link->data)->type;
253 if(type != evtype_CharInput && type != evtype_LineInput && type != evtype_MouseInput && type != evtype_Hyperlink)
255 memcpy(event, link->data, sizeof(event_t));
257 g_queue_delete_link(glk_data->event_queue, link);
258 g_cond_signal(&glk_data->event_queue_not_full);
264 g_mutex_unlock(&glk_data->event_lock);
266 /* Check for interrupt */
269 /* If an abort event was generated, the thread should have exited by now */
270 g_assert(event->type != evtype_Abort);