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