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