Special Portal commit
[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_char_input_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
389 {
390         glui32 keycode = keyval_to_glk_keycode(event->keyval, win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE);
391
392         ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(widget, CHIMARA_TYPE_GLK));
393         g_assert(glk);
394         event_throw(glk, evtype_CharInput, win, keycode, 0);
395         g_signal_emit_by_name(glk, "char-input", win->rock, event->keyval);
396
397         /* Only one keypress will be handled */
398         win->input_request_type = INPUT_REQUEST_NONE;
399         g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
400
401         return TRUE;
402 }
403
404 gboolean
405 on_line_input_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
406 {
407         switch(win->type)
408         {
409                 case wintype_TextBuffer:
410                         if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
411                         {
412                                 
413                         }
414                         else if(event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
415                         {
416                         
417                         }
418                         break;
419
420                 /* If this is a text grid window, then redirect the key press to the line input GtkEntry */
421                 case wintype_TextGrid:
422                 {
423                         if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
424                                 || event->keyval == GDK_Down || event->keyval == GDK_KP_Down
425                                 || event->keyval == GDK_Left || event->keyval == GDK_KP_Left
426                                 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
427                                 || event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab
428                                 || event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up
429                                 || event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down
430                                 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
431                                 || event->keyval == GDK_End || event->keyval == GDK_KP_End)
432                                 return FALSE; /* Don't redirect these keys */
433                         gtk_widget_grab_focus(win->input_entry);
434                         gtk_editable_set_position(GTK_EDITABLE(win->input_entry), -1);
435                         gboolean retval = TRUE;
436                         g_signal_emit_by_name(win->input_entry, "key-press-event", event, &retval);
437                         return retval; /* Block this key event if the entry handled it */
438                 }
439         }
440 }
441
442 /* Internal function: finish handling a line input request, for both text grid and text buffer windows. */
443 static int
444 write_to_window_buffer(winid_t win, const gchar *inserted_text)
445 {
446         int copycount = 0;
447
448     /* Convert the string from UTF-8 to Latin-1 or Unicode */
449     if(win->input_request_type == INPUT_REQUEST_LINE) 
450     {
451         gsize bytes_written;
452         gchar *latin1 = convert_utf8_to_latin1(inserted_text, &bytes_written);
453         
454         if(latin1 == NULL)
455             return 0;
456
457         /* Place input in the echo stream */
458         if(win->echo_stream != NULL) 
459             glk_put_string_stream(win->echo_stream, latin1);
460
461         /* Copy the string (bytes_written does not include the NULL at the end) */
462         copycount = MIN(win->line_input_buffer_max_len, bytes_written);
463         memcpy(win->line_input_buffer, latin1, copycount);
464         g_free(latin1);
465     }
466     else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE) 
467     {
468         glong items_written;
469         gunichar *unicode = convert_utf8_to_ucs4(inserted_text, &items_written);
470         
471         if(unicode == NULL)
472             return 0;
473
474         /* Place input in the echo stream */
475         if(win->echo_stream != NULL) 
476             glk_put_string_stream_uni(win->echo_stream, unicode);
477
478         /* Copy the string (but not the NULL at the end) */
479         copycount = MIN(win->line_input_buffer_max_len, items_written);
480         memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
481         g_free(unicode);
482     }
483     else 
484         WARNING("Wrong input request type");
485
486     win->input_request_type = INPUT_REQUEST_NONE;
487         return copycount;
488 }
489
490 /* Internal function: Retrieves the input of a TextBuffer window and stores it in the window buffer.
491  * Returns the number of characters written, suitable for inclusion in a line input event. */
492 static int
493 finish_text_buffer_line_input(winid_t win, gboolean emit_signal)
494 {
495         VALID_WINDOW(win, return 0);
496         g_return_val_if_fail(win->type == wintype_TextBuffer, 0);
497
498         GtkTextIter start_iter, end_iter, last_character;
499
500         GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
501         GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
502         gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position);
503         gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
504         gtk_text_buffer_get_end_iter(window_buffer, &last_character);
505         gtk_text_iter_backward_cursor_position(&last_character);
506
507         gchar* last_char = gtk_text_buffer_get_text(window_buffer, &last_character, &end_iter, FALSE);
508
509         if( strchr(last_char, '\n') != NULL )
510                 gtk_text_iter_backward_cursor_position(&end_iter);
511
512         gchar* inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
513
514         int chars_written = write_to_window_buffer(win, inserted_text);
515         if(emit_signal)
516         {
517                 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
518                 g_assert(glk);
519                 g_signal_emit_by_name(glk, "line-input", win->rock, inserted_text);
520         }
521         
522         /* Add the text to the window input history */
523         win->history = g_list_prepend(win->history, g_strdup(inserted_text));
524         win->history_pos = win->history;
525         
526         g_free(inserted_text);
527
528         return chars_written;
529 }
530
531 /* Internal function: Retrieves the input of a TextGrid window and stores it in the window buffer.
532  * Returns the number of characters written, suitable for inclusion in a line input event. */
533 static int
534 finish_text_grid_line_input(winid_t win, gboolean emit_signal)
535 {
536         VALID_WINDOW(win, return 0);
537         g_return_val_if_fail(win->type == wintype_TextGrid, 0);
538
539         GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
540         
541         gchar *text = g_strdup( gtk_entry_get_text(GTK_ENTRY(win->input_entry)) );
542         /* Move the focus back into the text view */
543         gtk_widget_grab_focus(win->widget);
544         /* Remove entry widget from text view */
545         /* Should be ok even though this is the widget's own signal handler */
546         gtk_container_remove( GTK_CONTAINER(win->widget), GTK_WIDGET(win->input_entry) );
547         win->input_entry = NULL;
548         /* Delete the child anchor */
549         GtkTextIter start, end;
550         gtk_text_buffer_get_iter_at_child_anchor(buffer, &start, win->input_anchor);
551         end = start;
552         gtk_text_iter_forward_char(&end); /* Point after the child anchor */
553         gtk_text_buffer_delete(buffer, &start, &end);
554         win->input_anchor = NULL;
555         
556     gchar *spaces = g_strnfill(win->input_length - g_utf8_strlen(text, -1), ' ');
557     gchar *text_to_insert = g_strconcat(text, spaces, NULL);
558         g_free(spaces);
559     gtk_text_buffer_insert(buffer, &start, text_to_insert, -1);
560     g_free(text_to_insert);
561     
562     int chars_written = write_to_window_buffer(win, text);
563     if(emit_signal)
564     {
565                 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
566                 g_assert(glk);
567                 g_signal_emit_by_name(glk, "line-input", win->rock, text);
568     }
569         g_free(text);
570
571         return chars_written;
572 }
573
574 /* Internal function: Callback for signal insert-text on a text buffer window.
575 Runs after the default handler has already inserted the text.
576 FIXME: This function assumes that newline was the last character typed into the
577 window. That assumption is wrong if, for example, text containing a newline was
578 pasted into the window. */
579 void
580 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win) 
581 {
582         if( strchr(text, '\n') != NULL ) 
583         {
584                 /* Remove signal handlers */
585                 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
586                 g_signal_handler_block(window_buffer, win->insert_text_handler);
587                 
588                 /* Make the window uneditable again and retrieve the text that was input */
589         gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
590
591         int chars_written = finish_text_buffer_line_input(win, TRUE);
592         ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
593                 event_throw(glk, evtype_LineInput, win, chars_written, 0);
594         }
595 }
596
597 /* Internal function: Callback for signal activate on the line input GtkEntry
598 in a text grid window. */
599 void
600 on_input_entry_activate(GtkEntry *input_entry, winid_t win)
601 {
602         g_signal_handler_block(win->widget, win->keypress_handler);
603
604         int chars_written = finish_text_grid_line_input(win, TRUE);
605         ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
606         event_throw(glk, evtype_LineInput, win, chars_written, 0);
607 }
608
609 glui32
610 keyval_to_glk_keycode(guint keyval, gboolean unicode)
611 {
612         glui32 keycode;
613         switch(keyval) {
614                 case GDK_Up:
615                 case GDK_KP_Up: return keycode_Up;
616                 case GDK_Down: 
617                 case GDK_KP_Down: return keycode_Down;
618                 case GDK_Left:
619                 case GDK_KP_Left: return keycode_Left; 
620                 case GDK_Right:
621                 case GDK_KP_Right: return keycode_Right; 
622                 case GDK_Linefeed:
623                 case GDK_Return:
624                 case GDK_KP_Enter: return keycode_Return; 
625                 case GDK_Delete:
626                 case GDK_BackSpace:
627                 case GDK_KP_Delete: return keycode_Delete; 
628                 case GDK_Escape: return keycode_Escape; 
629                 case GDK_Tab: 
630                 case GDK_KP_Tab: return keycode_Tab; 
631                 case GDK_Page_Up:
632                 case GDK_KP_Page_Up: return keycode_PageUp; 
633                 case GDK_Page_Down:
634                 case GDK_KP_Page_Down: return keycode_PageDown; 
635                 case GDK_Home:
636                 case GDK_KP_Home: return keycode_Home; 
637                 case GDK_End:
638                 case GDK_KP_End: return keycode_End; 
639                 case GDK_F1: 
640                 case GDK_KP_F1: return keycode_Func1; 
641                 case GDK_F2: 
642                 case GDK_KP_F2: return keycode_Func2; 
643                 case GDK_F3: 
644                 case GDK_KP_F3: return keycode_Func3; 
645                 case GDK_F4: 
646                 case GDK_KP_F4: return keycode_Func4; 
647                 case GDK_F5: return keycode_Func5; 
648                 case GDK_F6: return keycode_Func6; 
649                 case GDK_F7: return keycode_Func7; 
650                 case GDK_F8: return keycode_Func8; 
651                 case GDK_F9: return keycode_Func9; 
652                 case GDK_F10: return keycode_Func10; 
653                 case GDK_F11: return keycode_Func11; 
654                 case GDK_F12: return keycode_Func12; 
655                 default:
656                         keycode = gdk_keyval_to_unicode(keyval);
657                         /* If keycode is 0, then keyval was not recognized; also return
658                         unknown if Latin-1 input was requested and the character is not in
659                         Latin-1 */
660                         if(keycode == 0 || (!unicode && keycode > 255))
661                                 return keycode_Unknown;
662                         return keycode;
663         }
664 }
665
666 void
667 force_char_input_from_queue(winid_t win, event_t *event)
668 {
669         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
670         guint keyval = GPOINTER_TO_UINT(g_async_queue_pop(glk_data->char_input_queue));
671         
672         glk_cancel_char_event(win);
673         
674         gdk_threads_enter();
675         ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
676         g_assert(glk);
677         g_signal_emit_by_name(glk, "char-input", win->rock, keyval);
678         gdk_threads_leave();
679         
680         event->type = evtype_CharInput;
681         event->win = win;
682         event->val1 = keyval_to_glk_keycode(keyval, win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE);
683         event->val2 = 0;
684 }
685
686 void
687 force_line_input_from_queue(winid_t win, event_t *event)
688 {
689         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
690         const gchar *text = g_async_queue_pop(glk_data->line_input_queue);
691         glui32 chars_written = 0;
692         
693         gdk_threads_enter();
694         if(win->type == wintype_TextBuffer)
695         {
696                 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
697                 GtkTextIter start, end;
698                 
699                 /* Remove signal handlers so the line input doesn't get picked up again */
700                 g_signal_handler_block(buffer, win->insert_text_handler);
701                 
702                 /* Erase any text that was already typed */
703                 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
704                 gtk_text_buffer_get_iter_at_mark(buffer, &start, input_position);
705                 gtk_text_buffer_get_end_iter(buffer, &end);
706                 gtk_text_buffer_delete(buffer, &start, &end);
707                 
708                 /* Make the window uneditable again */
709                 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
710                 
711                 /* Insert the forced input into the window */
712                 gtk_text_buffer_get_end_iter(buffer, &end);
713                 gchar *text_to_insert = g_strconcat(text, "\n", NULL);
714                 gtk_text_buffer_insert_with_tags_by_name(buffer, &end, text_to_insert, -1, "input", NULL);
715                 chars_written = finish_text_buffer_line_input(win, TRUE);               
716         }
717         else if(win->type == wintype_TextGrid)
718         {
719                 /* Remove signal handlers so the line input doesn't get picked up again */
720                 g_signal_handler_block(win->widget, win->keypress_handler);
721                 
722                 /* Insert the forced input into the window */
723                 gtk_entry_set_text(GTK_ENTRY(win->input_entry), text);
724                 chars_written = finish_text_grid_line_input(win, TRUE);
725         }
726         gdk_threads_leave();
727         
728         event->type = evtype_LineInput;
729         event->win = win;
730         event->val1 = chars_written;
731         event->val2 = 0;
732 }