4 #include "chimara-glk-private.h"
6 extern GPrivate *glk_data_key;
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);
12 /* Internal function: code common to both flavors of char event request */
14 request_char_event_common(winid_t win, gboolean unicode)
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);
20 flush_window_buffer(win);
22 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
24 win->input_request_type = unicode? INPUT_REQUEST_CHARACTER_UNICODE : INPUT_REQUEST_CHARACTER;
25 g_signal_handler_unblock( G_OBJECT(win->widget), win->char_input_keypress_handler );
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)
34 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
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?? */
42 gtk_widget_grab_focus( GTK_WIDGET(win->widget) );
45 /* Emit the "waiting" signal to let listeners know we are ready for input */
46 g_signal_emit_by_name(glk_data->self, "waiting");
50 * glk_request_char_event:
51 * @win: A window to request char events from.
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.
60 glk_request_char_event(winid_t win)
62 request_char_event_common(win, FALSE);
66 * glk_request_char_event_uni:
67 * @win: A window to request char events from.
69 * Request input of a Unicode character or special key. See
70 * glk_request_char_event().
73 glk_request_char_event_uni(winid_t win)
75 request_char_event_common(win, TRUE);
79 * glk_cancel_char_event:
80 * @win: A window to cancel the latest char event request on.
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
88 glk_cancel_char_event(winid_t win)
90 VALID_WINDOW(win, return);
91 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
93 if(win->input_request_type == INPUT_REQUEST_CHARACTER || win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE)
95 win->input_request_type = INPUT_REQUEST_NONE;
96 g_signal_handler_block( G_OBJECT(win->widget), win->char_input_keypress_handler );
100 /* Internal function: Request either latin-1 or unicode line input, in a text grid window. */
102 text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
106 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
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);
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);
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) )
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);
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);
146 /* Insert pre-entered text if needed */
148 gtk_entry_set_text(GTK_ENTRY(win->input_entry), inserttext);
150 /* Set background color of entry (TODO: implement as property) */
152 gdk_color_parse("grey", &background);
153 gtk_widget_modify_base(win->input_entry, GTK_STATE_NORMAL, &background);
155 g_signal_connect(win->input_entry, "activate", G_CALLBACK(on_input_entry_activate), win);
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);
160 g_signal_handler_unblock( G_OBJECT(win->widget), win->char_input_keypress_handler );
162 gtk_widget_grab_focus(win->input_entry);
167 /* Internal function: Request either latin-1 or unicode line input, in a text buffer window. */
169 text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
171 flush_window_buffer(win);
175 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
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);
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);
190 /* Insert pre-entered text if needed */
192 gtk_text_buffer_insert(buffer, &end_iter, inserttext, -1);
194 /* Scroll to input point */
195 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position);
197 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE);
198 g_signal_handler_unblock(buffer, win->insert_text_handler);
200 gtk_widget_grab_focus(win->widget);
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.
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.
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.)
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.
231 glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen)
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);
239 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
241 /* Register the buffer */
242 if(glk_data->register_arr)
243 win->buffer_rock = (*glk_data->register_arr)(buf, maxlen, "&+#!Cn");
245 win->input_request_type = INPUT_REQUEST_LINE;
246 win->line_input_buffer = buf;
247 win->line_input_buffer_max_len = maxlen;
249 gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup("");
252 case wintype_TextBuffer:
253 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
255 case wintype_TextGrid:
256 text_grid_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
261 /* Emit the "waiting" signal to let listeners know we are ready for input */
262 g_signal_emit_by_name(glk_data->self, "waiting");
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.
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.
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.
284 glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
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);
292 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
294 /* Register the buffer */
295 if(glk_data->register_arr)
296 win->buffer_rock = (*glk_data->register_arr)(buf, maxlen, "&+#!Iu");
298 win->input_request_type = INPUT_REQUEST_LINE_UNICODE;
299 win->line_input_buffer_unicode = buf;
300 win->line_input_buffer_max_len = maxlen;
304 utf8 = convert_ucs4_to_utf8(buf, initlen);
313 case wintype_TextBuffer:
314 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), utf8);
316 case wintype_TextGrid:
317 text_grid_request_line_event_common(win, maxlen, (initlen > 0), utf8);
322 /* Emit the "waiting" signal to let listeners know we are ready for input */
323 g_signal_emit_by_name(glk_data->self, "waiting");
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.
331 * This cancels a pending request for line input. (Either Latin-1 or Unicode.)
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.)
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.
343 glk_cancel_line_event(winid_t win, event_t *event)
345 VALID_WINDOW(win, return);
346 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
349 event->type = evtype_None;
355 if(win->input_request_type != INPUT_REQUEST_LINE && win->input_request_type != INPUT_REQUEST_LINE_UNICODE)
358 g_signal_handler_block( G_OBJECT(win->widget), win->char_input_keypress_handler );
360 int chars_written = 0;
362 if(win->type == wintype_TextGrid) {
363 g_signal_handler_block( G_OBJECT(win->widget), win->char_input_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);
371 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
372 if(glk_data->unregister_arr)
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);
377 (*glk_data->unregister_arr)(win->line_input_buffer, win->line_input_buffer_max_len, "&+#!Cn", win->buffer_rock);
380 if(event != NULL && chars_written > 0) {
381 event->type = evtype_LineInput;
382 event->val1 = chars_written;
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. */
388 on_char_input_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
390 glui32 keycode = keyval_to_glk_keycode(event->keyval, win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE);
392 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(widget, CHIMARA_TYPE_GLK));
394 event_throw(glk, evtype_CharInput, win, keycode, 0);
395 g_signal_emit_by_name(glk, "char-input", win->rock, event->keyval);
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->char_input_keypress_handler );
405 on_line_input_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
409 case wintype_TextBuffer:
410 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
411 || event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
413 /* Prevent falling off the end of the history list */
414 if( (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
415 && win->history_pos && win->history_pos->next == NULL)
417 if( (event->keyval == GDK_Down || event->keyval == GDK_KP_Down)
418 && (win->history_pos == NULL || win->history_pos->prev == NULL) )
421 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(win->widget));
422 GtkTextIter start, end;
423 /* Erase any text that was already typed */
424 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
425 gtk_text_buffer_get_iter_at_mark(buffer, &start, input_position);
426 gtk_text_buffer_get_end_iter(buffer, &end);
428 if(win->history_pos == NULL) {
429 gchar *current_input = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
430 win->history = g_list_prepend(win->history, current_input);
431 win->history_pos = win->history;
434 gtk_text_buffer_delete(buffer, &start, &end);
436 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
439 win->history_pos = g_list_next(win->history_pos);
441 win->history_pos = win->history;
444 win->history_pos = g_list_previous(win->history_pos);
446 /* Insert the history item into the window */
447 gtk_text_buffer_get_end_iter(buffer, &end);
449 g_signal_handler_block(buffer, win->insert_text_handler);
450 gtk_text_buffer_insert_with_tags_by_name(buffer, &end, win->history_pos->data, -1, "input", NULL);
451 g_signal_handler_unblock(buffer, win->insert_text_handler);
456 /* If this is a text grid window, then redirect the key press to the line input GtkEntry */
457 case wintype_TextGrid:
459 if(event->keyval == GDK_Up || event->keyval == GDK_KP_Up
460 || event->keyval == GDK_Down || event->keyval == GDK_KP_Down
461 || event->keyval == GDK_Left || event->keyval == GDK_KP_Left
462 || event->keyval == GDK_Right || event->keyval == GDK_KP_Right
463 || event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab
464 || event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up
465 || event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down
466 || event->keyval == GDK_Home || event->keyval == GDK_KP_Home
467 || event->keyval == GDK_End || event->keyval == GDK_KP_End)
468 return FALSE; /* Don't redirect these keys */
469 gtk_widget_grab_focus(win->input_entry);
470 gtk_editable_set_position(GTK_EDITABLE(win->input_entry), -1);
471 gboolean retval = TRUE;
472 g_signal_emit_by_name(win->input_entry, "key-press-event", event, &retval);
473 return retval; /* Block this key event if the entry handled it */
479 /* Internal function: finish handling a line input request, for both text grid and text buffer windows. */
481 write_to_window_buffer(winid_t win, const gchar *inserted_text)
485 /* Convert the string from UTF-8 to Latin-1 or Unicode */
486 if(win->input_request_type == INPUT_REQUEST_LINE)
489 gchar *latin1 = convert_utf8_to_latin1(inserted_text, &bytes_written);
494 /* Place input in the echo stream */
495 if(win->echo_stream != NULL)
496 glk_put_string_stream(win->echo_stream, latin1);
498 /* Copy the string (bytes_written does not include the NULL at the end) */
499 copycount = MIN(win->line_input_buffer_max_len, bytes_written);
500 memcpy(win->line_input_buffer, latin1, copycount);
503 else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
506 gunichar *unicode = convert_utf8_to_ucs4(inserted_text, &items_written);
511 /* Place input in the echo stream */
512 if(win->echo_stream != NULL)
513 glk_put_string_stream_uni(win->echo_stream, unicode);
515 /* Copy the string (but not the NULL at the end) */
516 copycount = MIN(win->line_input_buffer_max_len, items_written);
517 memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
521 WARNING("Wrong input request type");
523 win->input_request_type = INPUT_REQUEST_NONE;
527 /* Internal function: Retrieves the input of a TextBuffer window and stores it in the window buffer.
528 * Returns the number of characters written, suitable for inclusion in a line input event. */
530 finish_text_buffer_line_input(winid_t win, gboolean emit_signal)
532 VALID_WINDOW(win, return 0);
533 g_return_val_if_fail(win->type == wintype_TextBuffer, 0);
535 GtkTextIter start_iter, end_iter, last_character;
537 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
538 GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
539 gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position);
540 gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
541 gtk_text_buffer_get_end_iter(window_buffer, &last_character);
542 gtk_text_iter_backward_cursor_position(&last_character);
544 gchar* last_char = gtk_text_buffer_get_text(window_buffer, &last_character, &end_iter, FALSE);
546 if( strchr(last_char, '\n') != NULL )
547 gtk_text_iter_backward_cursor_position(&end_iter);
549 gchar* inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
551 int chars_written = write_to_window_buffer(win, inserted_text);
554 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
556 g_signal_emit_by_name(glk, "line-input", win->rock, inserted_text);
559 /* Add the text to the window input history */
560 if(win->history_pos != NULL)
562 g_free(win->history->data);
563 win->history = g_list_delete_link(win->history, win->history);
565 win->history = g_list_prepend(win->history, g_strdup(inserted_text));
566 win->history_pos = NULL;
568 g_free(inserted_text);
570 return chars_written;
573 /* Internal function: Retrieves the input of a TextGrid window and stores it in the window buffer.
574 * Returns the number of characters written, suitable for inclusion in a line input event. */
576 finish_text_grid_line_input(winid_t win, gboolean emit_signal)
578 VALID_WINDOW(win, return 0);
579 g_return_val_if_fail(win->type == wintype_TextGrid, 0);
581 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
583 gchar *text = g_strdup( gtk_entry_get_text(GTK_ENTRY(win->input_entry)) );
584 /* Move the focus back into the text view */
585 gtk_widget_grab_focus(win->widget);
586 /* Remove entry widget from text view */
587 /* Should be ok even though this is the widget's own signal handler */
588 gtk_container_remove( GTK_CONTAINER(win->widget), GTK_WIDGET(win->input_entry) );
589 win->input_entry = NULL;
590 /* Delete the child anchor */
591 GtkTextIter start, end;
592 gtk_text_buffer_get_iter_at_child_anchor(buffer, &start, win->input_anchor);
594 gtk_text_iter_forward_char(&end); /* Point after the child anchor */
595 gtk_text_buffer_delete(buffer, &start, &end);
596 win->input_anchor = NULL;
598 gchar *spaces = g_strnfill(win->input_length - g_utf8_strlen(text, -1), ' ');
599 gchar *text_to_insert = g_strconcat(text, spaces, NULL);
601 gtk_text_buffer_insert(buffer, &start, text_to_insert, -1);
602 g_free(text_to_insert);
604 int chars_written = write_to_window_buffer(win, text);
607 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
609 g_signal_emit_by_name(glk, "line-input", win->rock, text);
613 return chars_written;
616 /* Internal function: Callback for signal insert-text on a text buffer window.
617 Runs after the default handler has already inserted the text.
618 FIXME: This function assumes that newline was the last character typed into the
619 window. That assumption is wrong if, for example, text containing a newline was
620 pasted into the window. */
622 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win)
624 /* Set the history position to NULL and erase the text we were already editing */
625 if(win->history_pos != NULL)
627 g_free(win->history->data);
628 win->history = g_list_delete_link(win->history, win->history);
629 win->history_pos = NULL;
631 if( strchr(text, '\n') != NULL )
633 /* Remove signal handlers */
634 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
635 g_signal_handler_block(window_buffer, win->insert_text_handler);
637 /* Make the window uneditable again and retrieve the text that was input */
638 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
640 int chars_written = finish_text_buffer_line_input(win, TRUE);
641 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
642 event_throw(glk, evtype_LineInput, win, chars_written, 0);
646 /* Internal function: Callback for signal activate on the line input GtkEntry
647 in a text grid window. */
649 on_input_entry_activate(GtkEntry *input_entry, winid_t win)
651 g_signal_handler_block(win->widget, win->char_input_keypress_handler);
653 int chars_written = finish_text_grid_line_input(win, TRUE);
654 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
655 event_throw(glk, evtype_LineInput, win, chars_written, 0);
659 keyval_to_glk_keycode(guint keyval, gboolean unicode)
664 case GDK_KP_Up: return keycode_Up;
666 case GDK_KP_Down: return keycode_Down;
668 case GDK_KP_Left: return keycode_Left;
670 case GDK_KP_Right: return keycode_Right;
673 case GDK_KP_Enter: return keycode_Return;
676 case GDK_KP_Delete: return keycode_Delete;
677 case GDK_Escape: return keycode_Escape;
679 case GDK_KP_Tab: return keycode_Tab;
681 case GDK_KP_Page_Up: return keycode_PageUp;
683 case GDK_KP_Page_Down: return keycode_PageDown;
685 case GDK_KP_Home: return keycode_Home;
687 case GDK_KP_End: return keycode_End;
689 case GDK_KP_F1: return keycode_Func1;
691 case GDK_KP_F2: return keycode_Func2;
693 case GDK_KP_F3: return keycode_Func3;
695 case GDK_KP_F4: return keycode_Func4;
696 case GDK_F5: return keycode_Func5;
697 case GDK_F6: return keycode_Func6;
698 case GDK_F7: return keycode_Func7;
699 case GDK_F8: return keycode_Func8;
700 case GDK_F9: return keycode_Func9;
701 case GDK_F10: return keycode_Func10;
702 case GDK_F11: return keycode_Func11;
703 case GDK_F12: return keycode_Func12;
705 keycode = gdk_keyval_to_unicode(keyval);
706 /* If keycode is 0, then keyval was not recognized; also return
707 unknown if Latin-1 input was requested and the character is not in
709 if(keycode == 0 || (!unicode && keycode > 255))
710 return keycode_Unknown;
716 force_char_input_from_queue(winid_t win, event_t *event)
718 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
719 guint keyval = GPOINTER_TO_UINT(g_async_queue_pop(glk_data->char_input_queue));
721 glk_cancel_char_event(win);
724 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
726 g_signal_emit_by_name(glk, "char-input", win->rock, keyval);
729 event->type = evtype_CharInput;
731 event->val1 = keyval_to_glk_keycode(keyval, win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE);
736 force_line_input_from_queue(winid_t win, event_t *event)
738 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
739 const gchar *text = g_async_queue_pop(glk_data->line_input_queue);
740 glui32 chars_written = 0;
743 if(win->type == wintype_TextBuffer)
745 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
746 GtkTextIter start, end;
748 /* Remove signal handlers so the line input doesn't get picked up again */
749 g_signal_handler_block(buffer, win->insert_text_handler);
751 /* Erase any text that was already typed */
752 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
753 gtk_text_buffer_get_iter_at_mark(buffer, &start, input_position);
754 gtk_text_buffer_get_end_iter(buffer, &end);
755 gtk_text_buffer_delete(buffer, &start, &end);
757 /* Make the window uneditable again */
758 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
760 /* Insert the forced input into the window */
761 gtk_text_buffer_get_end_iter(buffer, &end);
762 gchar *text_to_insert = g_strconcat(text, "\n", NULL);
763 gtk_text_buffer_insert_with_tags_by_name(buffer, &end, text_to_insert, -1, "input", NULL);
764 chars_written = finish_text_buffer_line_input(win, TRUE);
766 else if(win->type == wintype_TextGrid)
768 /* Remove signal handlers so the line input doesn't get picked up again */
769 g_signal_handler_block(win->widget, win->char_input_keypress_handler);
771 /* Insert the forced input into the window */
772 gtk_entry_set_text(GTK_ENTRY(win->input_entry), text);
773 chars_written = finish_text_grid_line_input(win, TRUE);
777 event->type = evtype_LineInput;
779 event->val1 = chars_written;