Implemented output buffering.
[rodin/chimara.git] / libchimara / input.c
1 #include "charset.h"
2 #include "magic.h"
3 #include "input.h"
4 #include "chimara-glk-private.h"
5
6 extern GPrivate *glk_data_key;
7
8 /* Forward declarations */
9 static int finish_text_buffer_line_input(winid_t win, gboolean emit_signal);
10 static int finish_text_grid_line_input(winid_t win, gboolean emit_signal);
11
12 /* Internal function: code common to both flavors of char event request */
13 void
14 request_char_event_common(winid_t win, gboolean unicode)
15 {
16         VALID_WINDOW(win, return);
17         g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
18         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
19
20         flush_window_buffer(win);
21         
22         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
23
24         win->input_request_type = unicode? INPUT_REQUEST_CHARACTER_UNICODE : INPUT_REQUEST_CHARACTER;
25         g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
26
27         gdk_threads_enter();
28         
29         /* If the request is in a text buffer window, scroll to the end of the
30          text buffer. TODO: This may scroll text off the top of the window that the
31          user hasn't read yet. We need to implement a paging mechanism. */
32         if(win->type == wintype_TextBuffer)
33         {
34                 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
35                 GtkTextIter iter;
36                 gtk_text_buffer_get_end_iter(buffer, &iter);
37                 gtk_text_buffer_place_cursor(buffer, &iter);
38                 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), gtk_text_buffer_get_insert(buffer));
39                 /* Why doesn't this always work?? */
40         }
41         
42         gtk_widget_grab_focus( GTK_WIDGET(win->widget) );
43         gdk_threads_leave();
44         
45         /* Emit the "waiting" signal to let listeners know we are ready for input */
46         g_signal_emit_by_name(glk_data->self, "waiting");
47 }
48
49 /** 
50  * glk_request_char_event:
51  * @win: A window to request char events from.
52  *
53  * Request input of a Latin-1 character or special key. A window cannot have 
54  * requests for both character and line input at the same time. Nor can it have
55  * requests for character input of both types (Latin-1 and Unicode). It is
56  * illegal to call glk_request_char_event() if the window already has a pending
57  * request for either character or line input. 
58  */
59 void
60 glk_request_char_event(winid_t win)
61 {
62         request_char_event_common(win, FALSE);
63 }
64
65 /** 
66  * glk_request_char_event_uni:
67  * @win: A window to request char events from.
68  *
69  * Request input of a Unicode character or special key. See 
70  * glk_request_char_event().
71  */
72 void
73 glk_request_char_event_uni(winid_t win)
74 {
75         request_char_event_common(win, TRUE);
76 }
77
78 /**
79  * glk_cancel_char_event:
80  * @win: A window to cancel the latest char event request on.
81  *
82  * This cancels a pending request for character input. (Either Latin-1 or
83  * Unicode.) For convenience, it is legal to call glk_cancel_char_event() even
84  * if there is no charcter input request on that window. Glk will ignore the
85  * call in this case.
86  */
87 void
88 glk_cancel_char_event(winid_t win)
89 {
90         VALID_WINDOW(win, return);
91         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
92         
93         if(win->input_request_type == INPUT_REQUEST_CHARACTER || win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE)
94         {
95                 win->input_request_type = INPUT_REQUEST_NONE;
96                 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
97         }
98 }
99
100 /* Internal function: Request either latin-1 or unicode line input, in a text grid window. */
101 static void
102 text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
103 {
104         gdk_threads_enter();
105         
106         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
107
108     GtkTextMark *cursor = gtk_text_buffer_get_mark(buffer, "cursor_position");
109     GtkTextIter start_iter, end_iter;
110     gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, cursor);
111     
112     /* Determine the maximum length of the line input */
113     gint cursorpos = gtk_text_iter_get_line_offset(&start_iter);
114     /* Odd; the Glk spec says the maximum input length is
115     windowwidth - 1 - cursorposition. I say no, because if cursorposition is
116     zero, then the input should fill the whole line. FIXME??? */
117     win->input_length = MIN(win->width - cursorpos, win->line_input_buffer_max_len);
118     end_iter = start_iter;
119     gtk_text_iter_set_line_offset(&end_iter, cursorpos + win->input_length);
120     
121         /* If the buffer currently has a selection with one bound in the middle of
122         the input field, then deselect it. Otherwise the input field gets trashed */
123         GtkTextIter start_sel, end_sel;
124         if( gtk_text_buffer_get_selection_bounds(buffer, &start_sel, &end_sel) )
125         {
126                 if( gtk_text_iter_in_range(&start_sel, &start_iter, &end_iter) )
127                         gtk_text_buffer_place_cursor(buffer, &end_sel);
128                 if( gtk_text_iter_in_range(&end_sel, &start_iter, &end_iter) )
129                         gtk_text_buffer_place_cursor(buffer, &start_sel);
130         }
131         
132     /* Erase the text currently in the input field and replace it with a GtkEntry */
133     gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
134     win->input_anchor = gtk_text_buffer_create_child_anchor(buffer, &start_iter);
135     win->input_entry = gtk_entry_new();
136         /* Set the entry's font to match that of the window */
137     GtkRcStyle *style = gtk_widget_get_modifier_style(win->widget);     /* Don't free */
138         gtk_widget_modify_font(win->input_entry, style->font_desc);
139         /* Make the entry as small as possible to fit with the text */
140         gtk_entry_set_has_frame(GTK_ENTRY(win->input_entry), FALSE);
141         GtkBorder border = { 0, 0, 0, 0 };
142         gtk_entry_set_inner_border(GTK_ENTRY(win->input_entry), &border);
143     gtk_entry_set_max_length(GTK_ENTRY(win->input_entry), win->input_length);
144     gtk_entry_set_width_chars(GTK_ENTRY(win->input_entry), win->input_length);
145     
146     /* Insert pre-entered text if needed */
147     if(insert)
148         gtk_entry_set_text(GTK_ENTRY(win->input_entry), inserttext);
149     
150     /* Set background color of entry (TODO: implement as property) */
151     GdkColor background;
152         gdk_color_parse("grey", &background);
153     gtk_widget_modify_base(win->input_entry, GTK_STATE_NORMAL, &background);
154     
155     g_signal_connect(win->input_entry, "activate", G_CALLBACK(on_input_entry_activate), win);
156     
157     gtk_widget_show(win->input_entry);
158     gtk_text_view_add_child_at_anchor(GTK_TEXT_VIEW(win->widget), win->input_entry, win->input_anchor);
159         
160         g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
161
162         gtk_widget_grab_focus(win->input_entry);
163         
164         gdk_threads_leave();
165 }
166     
167 /* Internal function: Request either latin-1 or unicode line input, in a text buffer window. */
168 static void
169 text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
170 {
171         flush_window_buffer(win);
172
173         gdk_threads_enter();
174         
175         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
176
177     /* Move the input_position mark to the end of the window_buffer */
178     GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
179     GtkTextIter end_iter;
180     gtk_text_buffer_get_end_iter(buffer, &end_iter);
181     gtk_text_buffer_move_mark(buffer, input_position, &end_iter);
182
183     /* Set the entire contents of the window_buffer as uneditable
184      * (so input can only be entered at the end) */
185     GtkTextIter start_iter;
186     gtk_text_buffer_get_start_iter(buffer, &start_iter);
187     gtk_text_buffer_remove_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
188     gtk_text_buffer_apply_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
189     
190     /* Insert pre-entered text if needed */
191     if(insert)
192         gtk_text_buffer_insert(buffer, &end_iter, inserttext, -1);
193     
194     /* Scroll to input point */
195     gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position);
196     
197     gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE);
198     g_signal_handler_unblock(buffer, win->insert_text_handler);
199
200         gtk_widget_grab_focus(win->widget);
201         
202         gdk_threads_leave();
203 }
204
205 /**
206  * glk_request_line_event:
207  * @win: A text buffer or text grid window to request line input on.
208  * @buf: A buffer of at least @maxlen bytes.
209  * @maxlen: Length of the buffer.
210  * @initlen: The number of characters in @buf to pre-enter.
211  *
212  * Requests input of a line of Latin-1 characters. A window cannot have requests
213  * for both character and line input at the same time. Nor can it have requests
214  * for line input of both types (Latin-1 and Unicode). It is illegal to call
215  * glk_request_line_event() if the window already has a pending request for
216  * either character or line input.
217  * 
218  * The @buf argument is a pointer to space where the line input will be stored.
219  * (This may not be %NULL.) @maxlen is the length of this space, in bytes; the
220  * library will not accept more characters than this. If @initlen is nonzero,
221  * then the first @initlen bytes of @buf will be entered as pre-existing input
222  * — just as if the player had typed them himself. (The player can continue
223  * composing after this pre-entered input, or delete it or edit as usual.)
224  * 
225  * The contents of the buffer are undefined until the input is completed (either
226  * by a line input event, or glk_cancel_line_event(). The library may or may not
227  * fill in the buffer as the player composes, while the input is still pending;
228  * it is illegal to change the contents of the buffer yourself. 
229  */
230 void
231 glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen)
232 {
233         VALID_WINDOW(win, return);
234         g_return_if_fail(buf);
235         g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
236         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
237         g_return_if_fail(initlen <= maxlen);
238
239         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
240         
241         /* Register the buffer */
242         if(glk_data->register_arr)
243         win->buffer_rock = (*glk_data->register_arr)(buf, maxlen, "&+#!Cn");
244         
245         win->input_request_type = INPUT_REQUEST_LINE;
246         win->line_input_buffer = buf;
247         win->line_input_buffer_max_len = maxlen;
248
249         gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup("");
250         switch(win->type)
251         {
252             case wintype_TextBuffer:
253                 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
254                 break;
255             case wintype_TextGrid:
256                 text_grid_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
257                 break;
258     }
259         g_free(inserttext);
260         
261         /* Emit the "waiting" signal to let listeners know we are ready for input */
262         g_signal_emit_by_name(glk_data->self, "waiting");
263 }
264
265 /**
266  * glk_request_line_event_uni:
267  * @win: A text buffer or text grid window to request line input on.
268  * @buf: A buffer of at least @maxlen characters.
269  * @maxlen: Length of the buffer.
270  * @initlen: The number of characters in @buf to pre-enter.
271  *
272  * Request input of a line of Unicode characters. This works the same as
273  * glk_request_line_event(), except the result is stored in an array of
274  * <type>glui32</type> values instead of an array of characters, and the values
275  * may be any valid Unicode code points.
276  * 
277  * The result will be in Unicode Normalization Form C. This basically means that
278  * composite characters will be single characters where possible, instead of
279  * sequences of base and combining marks. See 
280  * <ulink url="http://www.unicode.org/reports/tr15/">Unicode Standard Annex 
281  * #15</ulink> for the details.
282  */
283 void
284 glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
285 {
286         VALID_WINDOW(win, return);
287         g_return_if_fail(buf);
288         g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
289         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
290         g_return_if_fail(initlen <= maxlen);
291
292         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
293         
294         /* Register the buffer */
295         if(glk_data->register_arr)
296         win->buffer_rock = (*glk_data->register_arr)(buf, maxlen, "&+#!Iu");
297
298         win->input_request_type = INPUT_REQUEST_LINE_UNICODE;
299         win->line_input_buffer_unicode = buf;
300         win->line_input_buffer_max_len = maxlen;
301
302         gchar *utf8;
303         if(initlen > 0) {
304                 utf8 = convert_ucs4_to_utf8(buf, initlen);
305                 if(utf8 == NULL)
306                         return;
307         }
308         else
309                 utf8 = g_strdup("");
310
311     switch(win->type)
312         {
313             case wintype_TextBuffer:
314                 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), utf8);
315                 break;
316             case wintype_TextGrid:
317                 text_grid_request_line_event_common(win, maxlen, (initlen > 0), utf8);
318                 break;
319     }           
320         g_free(utf8);
321         
322         /* Emit the "waiting" signal to let listeners know we are ready for input */
323         g_signal_emit_by_name(glk_data->self, "waiting");
324 }
325
326 /**
327  * glk_cancel_line_event:
328  * @win: A text buffer or text grid window to cancel line input on.
329  * @event: Will be filled in if the user had already input something.
330  *
331  * This cancels a pending request for line input. (Either Latin-1 or Unicode.)
332  *
333  * The event pointed to by the event argument will be filled in as if the
334  * player had hit <keycap>enter</keycap>, and the input composed so far will be 
335  * stored in the buffer; see below. If you do not care about this information, 
336  * pass %NULL as the @event argument. (The buffer will still be filled.) 
337  *
338  * For convenience, it is legal to call glk_cancel_line_event() even if there
339  * is no line input request on that window. The event type will be set to
340  * %evtype_None in this case.
341  */
342 void
343 glk_cancel_line_event(winid_t win, event_t *event)
344 {
345         VALID_WINDOW(win, return);
346         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
347
348         if(event != NULL) {
349                 event->type = evtype_None;
350                 event->win = win;
351                 event->val1 = 0;
352                 event->val2 = 0;
353         }
354
355         if(win->input_request_type != INPUT_REQUEST_LINE && win->input_request_type != INPUT_REQUEST_LINE_UNICODE)
356                 return;
357
358         g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
359
360         int chars_written = 0;
361
362         if(win->type == wintype_TextGrid) {
363                 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
364                 chars_written = finish_text_grid_line_input(win, FALSE);
365         } else if(win->type == wintype_TextBuffer) {
366                 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
367                 g_signal_handler_block(window_buffer, win->insert_text_handler);
368                 chars_written = finish_text_buffer_line_input(win, FALSE);
369         }
370
371         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
372         if(glk_data->unregister_arr) 
373         {
374                 if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
375                         (*glk_data->unregister_arr)(win->line_input_buffer_unicode, win->line_input_buffer_max_len, "&+#!Iu", win->buffer_rock);
376                 else
377                 (*glk_data->unregister_arr)(win->line_input_buffer, win->line_input_buffer_max_len, "&+#!Cn", win->buffer_rock);
378     }
379         
380         if(event != NULL && chars_written > 0) {
381                 event->type = evtype_LineInput;
382                 event->val1 = chars_written;
383         }
384 }
385
386 /* Internal function: General callback for signal key-press-event on a text buffer or text grid window. Used in character input on both text buffers and grids, and also in line input on grids, to redirect keystrokes to the line input field. Blocked when not in use. */
387 gboolean
388 on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
389 {
390         /* If this is a text grid window, and line input is active, then redirect the key press to the line input GtkEntry */
391         if( win->type == wintype_TextGrid && (win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE) )
392         {
393                 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
394                     || event->keyval == GDK_Down || event->keyval == GDK_KP_Down
395                     || event->keyval == GDK_Left || event->keyval == GDK_KP_Left
396                     || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
397                     || event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab
398                     || event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up
399                     || event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down
400                     || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
401                     || event->keyval == GDK_End || event->keyval == GDK_KP_End)
402                         return FALSE; /* Don't redirect these keys */
403                 gtk_widget_grab_focus(win->input_entry);
404                 gtk_editable_set_position(GTK_EDITABLE(win->input_entry), -1);
405                 gboolean retval = TRUE;
406                 g_signal_emit_by_name(win->input_entry, "key-press-event", event, &retval);
407                 return retval; /* Block this key event if the entry handled it */
408         }
409         if(win->input_request_type != INPUT_REQUEST_CHARACTER && 
410                 win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE)
411                 return FALSE;
412
413         glui32 keycode = keyval_to_glk_keycode(event->keyval, win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE);
414
415         ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(widget, CHIMARA_TYPE_GLK));
416         g_assert(glk);
417         event_throw(glk, evtype_CharInput, win, keycode, 0);
418         g_signal_emit_by_name(glk, "char-input", win->rock, event->keyval);
419
420         /* Only one keypress will be handled */
421         win->input_request_type = INPUT_REQUEST_NONE;
422         g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
423
424         return TRUE;
425 }
426
427 /* Internal function: finish handling a line input request, for both text grid and text buffer windows. */
428 static int
429 write_to_window_buffer(winid_t win, const gchar *inserted_text)
430 {
431         int copycount = 0;
432
433     /* Convert the string from UTF-8 to Latin-1 or Unicode */
434     if(win->input_request_type == INPUT_REQUEST_LINE) 
435     {
436         gsize bytes_written;
437         gchar *latin1 = convert_utf8_to_latin1(inserted_text, &bytes_written);
438         
439         if(latin1 == NULL)
440             return 0;
441
442         /* Place input in the echo stream */
443         if(win->echo_stream != NULL) 
444             glk_put_string_stream(win->echo_stream, latin1);
445
446         /* Copy the string (bytes_written does not include the NULL at the end) */
447         copycount = MIN(win->line_input_buffer_max_len, bytes_written);
448         memcpy(win->line_input_buffer, latin1, copycount);
449         g_free(latin1);
450     }
451     else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE) 
452     {
453         glong items_written;
454         gunichar *unicode = convert_utf8_to_ucs4(inserted_text, &items_written);
455         
456         if(unicode == NULL)
457             return 0;
458
459         /* Place input in the echo stream */
460         if(win->echo_stream != NULL) 
461             glk_put_string_stream_uni(win->echo_stream, unicode);
462
463         /* Copy the string (but not the NULL at the end) */
464         copycount = MIN(win->line_input_buffer_max_len, items_written);
465         memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
466         g_free(unicode);
467     }
468     else 
469         WARNING("Wrong input request type");
470
471     win->input_request_type = INPUT_REQUEST_NONE;
472         return copycount;
473 }
474
475 /* Internal function: Retrieves the input of a TextBuffer window and stores it in the window buffer.
476  * Returns the number of characters written, suitable for inclusion in a line input event. */
477 static int
478 finish_text_buffer_line_input(winid_t win, gboolean emit_signal)
479 {
480         VALID_WINDOW(win, return 0);
481         g_return_val_if_fail(win->type == wintype_TextBuffer, 0);
482
483         GtkTextIter start_iter, end_iter, last_character;
484
485         GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
486         GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
487         gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position);
488         gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
489         gtk_text_buffer_get_end_iter(window_buffer, &last_character);
490         gtk_text_iter_backward_cursor_position(&last_character);
491
492         gchar* last_char = gtk_text_buffer_get_text(window_buffer, &last_character, &end_iter, FALSE);
493
494         if( strchr(last_char, '\n') != NULL )
495                 gtk_text_iter_backward_cursor_position(&end_iter);
496
497         gchar* inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
498
499         int chars_written = write_to_window_buffer(win, inserted_text);
500         if(emit_signal)
501         {
502                 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
503                 g_assert(glk);
504                 g_signal_emit_by_name(glk, "line-input", win->rock, inserted_text);
505         }
506         g_free(inserted_text);
507
508         return chars_written;
509 }
510
511 /* Internal function: Retrieves the input of a TextGrid window and stores it in the window buffer.
512  * Returns the number of characters written, suitable for inclusion in a line input event. */
513 static int
514 finish_text_grid_line_input(winid_t win, gboolean emit_signal)
515 {
516         VALID_WINDOW(win, return 0);
517         g_return_val_if_fail(win->type == wintype_TextGrid, 0);
518
519         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
520         
521         gchar *text = g_strdup( gtk_entry_get_text(GTK_ENTRY(win->input_entry)) );
522         /* Move the focus back into the text view */
523         gtk_widget_grab_focus(win->widget);
524         /* Remove entry widget from text view */
525         /* Should be ok even though this is the widget's own signal handler */
526         gtk_container_remove( GTK_CONTAINER(win->widget), GTK_WIDGET(win->input_entry) );
527         win->input_entry = NULL;
528         /* Delete the child anchor */
529         GtkTextIter start, end;
530         gtk_text_buffer_get_iter_at_child_anchor(buffer, &start, win->input_anchor);
531         end = start;
532         gtk_text_iter_forward_char(&end); /* Point after the child anchor */
533         gtk_text_buffer_delete(buffer, &start, &end);
534         win->input_anchor = NULL;
535         
536     gchar *spaces = g_strnfill(win->input_length - g_utf8_strlen(text, -1), ' ');
537     gchar *text_to_insert = g_strconcat(text, spaces, NULL);
538         g_free(spaces);
539     gtk_text_buffer_insert(buffer, &start, text_to_insert, -1);
540     g_free(text_to_insert);
541     
542     int chars_written = write_to_window_buffer(win, text);
543     if(emit_signal)
544     {
545                 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
546                 g_assert(glk);
547                 g_signal_emit_by_name(glk, "line-input", win->rock, text);
548     }
549         g_free(text);
550
551         return chars_written;
552 }
553
554 /* Internal function: Callback for signal insert-text on a text buffer window.
555 Runs after the default handler has already inserted the text.
556 FIXME: This function assumes that newline was the last character typed into the
557 window. That assumption is wrong if, for example, text containing a newline was
558 pasted into the window. */
559 void
560 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win) 
561 {
562         if( strchr(text, '\n') != NULL ) 
563         {
564                 /* Remove signal handlers */
565                 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
566                 g_signal_handler_block(window_buffer, win->insert_text_handler);
567                 
568                 /* Make the window uneditable again and retrieve the text that was input */
569         gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
570
571         int chars_written = finish_text_buffer_line_input(win, TRUE);
572         ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
573                 event_throw(glk, evtype_LineInput, win, chars_written, 0);
574         }
575 }
576
577 /* Internal function: Callback for signal activate on the line input GtkEntry
578 in a text grid window. */
579 void
580 on_input_entry_activate(GtkEntry *input_entry, winid_t win)
581 {
582         g_signal_handler_block(win->widget, win->keypress_handler);
583
584         int chars_written = finish_text_grid_line_input(win, TRUE);
585         ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
586         event_throw(glk, evtype_LineInput, win, chars_written, 0);
587 }
588
589 glui32
590 keyval_to_glk_keycode(guint keyval, gboolean unicode)
591 {
592         glui32 keycode;
593         switch(keyval) {
594                 case GDK_Up:
595                 case GDK_KP_Up: return keycode_Up;
596                 case GDK_Down: 
597                 case GDK_KP_Down: return keycode_Down;
598                 case GDK_Left:
599                 case GDK_KP_Left: return keycode_Left; 
600                 case GDK_Right:
601                 case GDK_KP_Right: return keycode_Right; 
602                 case GDK_Linefeed:
603                 case GDK_Return:
604                 case GDK_KP_Enter: return keycode_Return; 
605                 case GDK_Delete:
606                 case GDK_BackSpace:
607                 case GDK_KP_Delete: return keycode_Delete; 
608                 case GDK_Escape: return keycode_Escape; 
609                 case GDK_Tab: 
610                 case GDK_KP_Tab: return keycode_Tab; 
611                 case GDK_Page_Up:
612                 case GDK_KP_Page_Up: return keycode_PageUp; 
613                 case GDK_Page_Down:
614                 case GDK_KP_Page_Down: return keycode_PageDown; 
615                 case GDK_Home:
616                 case GDK_KP_Home: return keycode_Home; 
617                 case GDK_End:
618                 case GDK_KP_End: return keycode_End; 
619                 case GDK_F1: 
620                 case GDK_KP_F1: return keycode_Func1; 
621                 case GDK_F2: 
622                 case GDK_KP_F2: return keycode_Func2; 
623                 case GDK_F3: 
624                 case GDK_KP_F3: return keycode_Func3; 
625                 case GDK_F4: 
626                 case GDK_KP_F4: return keycode_Func4; 
627                 case GDK_F5: return keycode_Func5; 
628                 case GDK_F6: return keycode_Func6; 
629                 case GDK_F7: return keycode_Func7; 
630                 case GDK_F8: return keycode_Func8; 
631                 case GDK_F9: return keycode_Func9; 
632                 case GDK_F10: return keycode_Func10; 
633                 case GDK_F11: return keycode_Func11; 
634                 case GDK_F12: return keycode_Func12; 
635                 default:
636                         keycode = gdk_keyval_to_unicode(keyval);
637                         /* If keycode is 0, then keyval was not recognized; also return
638                         unknown if Latin-1 input was requested and the character is not in
639                         Latin-1 */
640                         if(keycode == 0 || (!unicode && keycode > 255))
641                                 return keycode_Unknown;
642                         return keycode;
643         }
644 }
645
646 void
647 force_char_input_from_queue(winid_t win, event_t *event)
648 {
649         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
650         guint keyval = GPOINTER_TO_UINT(g_async_queue_pop(glk_data->char_input_queue));
651         
652         glk_cancel_char_event(win);
653         
654         gdk_threads_enter();
655         ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
656         g_assert(glk);
657         g_signal_emit_by_name(glk, "char-input", win->rock, keyval);
658         gdk_threads_leave();
659         
660         event->type = evtype_CharInput;
661         event->win = win;
662         event->val1 = keyval_to_glk_keycode(keyval, win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE);
663         event->val2 = 0;
664 }
665
666 void
667 force_line_input_from_queue(winid_t win, event_t *event)
668 {
669         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
670         const gchar *text = g_async_queue_pop(glk_data->line_input_queue);
671         glui32 chars_written = 0;
672         
673         gdk_threads_enter();
674         if(win->type == wintype_TextBuffer)
675         {
676                 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
677                 GtkTextIter start, end;
678                 
679                 /* Remove signal handlers so the line input doesn't get picked up again */
680                 g_signal_handler_block(buffer, win->insert_text_handler);
681                 
682                 /* Erase any text that was already typed */
683                 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
684                 gtk_text_buffer_get_iter_at_mark(buffer, &start, input_position);
685                 gtk_text_buffer_get_end_iter(buffer, &end);
686                 gtk_text_buffer_delete(buffer, &start, &end);
687                 
688                 /* Make the window uneditable again */
689                 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
690                 
691                 /* Insert the forced input into the window */
692                 gtk_text_buffer_get_end_iter(buffer, &end);
693                 gchar *text_to_insert = g_strconcat(text, "\n", NULL);
694                 gtk_text_buffer_insert_with_tags_by_name(buffer, &end, text_to_insert, -1, "input", NULL);
695                 chars_written = finish_text_buffer_line_input(win, TRUE);               
696         }
697         else if(win->type == wintype_TextGrid)
698         {
699                 /* Remove signal handlers so the line input doesn't get picked up again */
700                 g_signal_handler_block(win->widget, win->keypress_handler);
701                 
702                 /* Insert the forced input into the window */
703                 gtk_entry_set_text(GTK_ENTRY(win->input_entry), text);
704                 chars_written = finish_text_grid_line_input(win, TRUE);
705         }
706         gdk_threads_leave();
707         
708         event->type = evtype_LineInput;
709         event->win = win;
710         event->val1 = chars_written;
711         event->val2 = 0;
712 }