3 /** glk_request_char_event:
4 * @win: A window to request char events from.
6 * Request input of a Latin-1 character or special key. A window cannot have
7 * requests for both character and line input at the same time. Nor can it have
8 * requests for character input of both types (Latin-1 and Unicode). It is
9 * illegal to call glk_request_char_event() if the window already has a pending
10 * request for either character or line input.
13 glk_request_char_event(winid_t win)
15 g_return_if_fail(win);
16 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
17 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
19 win->input_request_type = INPUT_REQUEST_CHARACTER;
20 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
23 /** glk_request_char_event_uni:
24 * @win: A window to request char events from.
26 * Request input of a Unicode character or special key. See
27 * glk_request_char_event().
30 glk_request_char_event_uni(winid_t win)
32 g_return_if_fail(win);
33 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
34 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
36 win->input_request_type = INPUT_REQUEST_CHARACTER_UNICODE;
37 g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler );
40 /* Internal function: Request either latin-1 or unicode line input, in a text grid window. */
42 text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
44 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
46 GtkTextMark *cursor = gtk_text_buffer_get_mark(buffer, "cursor_position");
47 GtkTextIter start_iter, end_iter;
48 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, cursor);
50 /* Determine the maximum length of the line input */
51 gint cursorpos = gtk_text_iter_get_line_offset(&start_iter);
52 gint input_length = MIN(win->width - cursorpos, win->line_input_buffer_max_len);
53 end_iter = start_iter;
54 gtk_text_iter_set_line_offset(&end_iter, cursorpos + input_length);
56 /* Insert pre-entered text if needed */
59 text = g_strnfill(input_length, ' ');
62 gchar *blanks = g_strnfill(input_length - g_utf8_strlen(inserttext, -1), ' ');
63 text = g_strconcat(inserttext, blanks, NULL);
67 /* Erase the text currently in the input field and replace it with blanks or the insert text, with the editable "input_field" tag */
68 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
69 gtk_text_buffer_insert_with_tags_by_name(buffer, &start_iter, text, -1, "input_field", NULL);
72 g_signal_handler_unblock(GTK_TEXT_VIEW(win->widget), win->insert_text_handler);
75 /* Internal function: Request either latin-1 or unicode line input, in a text buffer window. */
77 text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext)
79 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
81 /* Move the input_position mark to the end of the window_buffer */
82 GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position");
84 gtk_text_buffer_get_end_iter(buffer, &end_iter);
85 gtk_text_buffer_move_mark(buffer, input_position, &end_iter);
87 /* Set the entire contents of the window_buffer as uneditable
88 * (so input can only be entered at the end) */
89 GtkTextIter start_iter;
90 gtk_text_buffer_get_start_iter(buffer, &start_iter);
91 gtk_text_buffer_remove_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
92 gtk_text_buffer_apply_tag_by_name(buffer, "uneditable", &start_iter, &end_iter);
94 /* Insert pre-entered text if needed */
96 gtk_text_buffer_insert(buffer, &end_iter, inserttext, -1);
98 /* Scroll to input point */
99 gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position);
101 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE);
102 g_signal_handler_unblock(buffer, win->insert_text_handler);
106 * glk_request_line_event:
107 * @win: A text buffer or text grid window to request line input on.
108 * @buf: A buffer of at least @maxlen bytes.
109 * @maxlen: Length of the buffer.
110 * @initlen: The number of characters in @buf to pre-enter.
112 * Requests input of a line of Latin-1 characters. A window cannot have requests
113 * for both character and line input at the same time. Nor can it have requests
114 * for line input of both types (Latin-1 and Unicode). It is illegal to call
115 * glk_request_line_event() if the window already has a pending request for
116 * either character or line input.
118 * The @buf argument is a pointer to space where the line input will be stored.
119 * (This may not be %NULL.) @maxlen is the length of this space, in bytes; the
120 * library will not accept more characters than this. If @initlen is nonzero,
121 * then the first @initlen bytes of @buf will be entered as pre-existing input
122 * -- just as if the player had typed them himself. (The player can continue
123 * composing after this pre-entered input, or delete it or edit as usual.)
125 * The contents of the buffer are undefined until the input is completed (either
126 * by a line input event, or glk_cancel_line_event(). The library may or may not
127 * fill in the buffer as the player composes, while the input is still pending;
128 * it is illegal to change the contents of the buffer yourself.
131 glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen)
133 g_return_if_fail(win);
134 g_return_if_fail(buf);
135 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
136 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
137 g_return_if_fail(initlen <= maxlen);
139 win->input_request_type = INPUT_REQUEST_LINE;
140 win->line_input_buffer = buf;
141 win->line_input_buffer_max_len = maxlen;
143 gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup("");
146 case wintype_TextBuffer:
147 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
149 case wintype_TextGrid:
150 text_grid_request_line_event_common(win, maxlen, (initlen > 0), inserttext);
153 g_assert_not_reached();
159 * glk_request_line_event_uni:
160 * @win: A text buffer or text grid window to request line input on.
161 * @buf: A buffer of at least @maxlen characters.
162 * @maxlen: Length of the buffer.
163 * @initlen: The number of characters in @buf to pre-enter.
165 * Request input of a line of Unicode characters. This works the same as
166 * glk_request_line_event(), except the result is stored in an array of
167 * <type>glui32</type> values instead of an array of characters, and the values
168 * may be any valid Unicode code points.
170 * The result will be in Unicode Normalization Form C. This basically means that
171 * composite characters will be single characters where possible, instead of
172 * sequences of base and combining marks. See
173 * <ulink url="http://www.unicode.org/reports/tr15/">Unicode Standard Annex #15
174 * </ulink> for the details.
177 glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen)
179 g_return_if_fail(win);
180 g_return_if_fail(buf);
181 g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE);
182 g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
184 win->input_request_type = INPUT_REQUEST_LINE_UNICODE;
185 win->line_input_buffer_unicode = buf;
186 win->line_input_buffer_max_len = maxlen;
190 GError *error = NULL;
191 utf8 = g_ucs4_to_utf8(buf, initlen, NULL, NULL, &error);
195 g_warning("Error during unicode->utf8 conversion: %s", error->message);
204 case wintype_TextBuffer:
205 text_buffer_request_line_event_common(win, maxlen, (initlen > 0), utf8);
207 case wintype_TextGrid:
208 text_grid_request_line_event_common(win, maxlen, (initlen > 0), utf8);
211 g_assert_not_reached();
216 /* Internal function: Callback for signal key-press-event on a text buffer or text grid window. */
218 on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
220 if(win->input_request_type != INPUT_REQUEST_CHARACTER &&
221 win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE)
226 switch(event->keyval) {
228 case GDK_KP_Up: keycode = keycode_Up; break;
230 case GDK_KP_Down: keycode = keycode_Down; break;
232 case GDK_KP_Left: keycode = keycode_Left; break;
234 case GDK_KP_Right: keycode = keycode_Right; break;
237 case GDK_KP_Enter: keycode = keycode_Return; break;
240 case GDK_KP_Delete: keycode = keycode_Delete; break;
241 case GDK_Escape: keycode = keycode_Escape; break;
243 case GDK_KP_Tab: keycode = keycode_Tab; break;
245 case GDK_KP_Page_Up: keycode = keycode_PageUp; break;
247 case GDK_KP_Page_Down: keycode = keycode_PageDown; break;
249 case GDK_KP_Home: keycode = keycode_Home; break;
251 case GDK_KP_End: keycode = keycode_End; break;
253 case GDK_KP_F1: keycode = keycode_Func1; break;
255 case GDK_KP_F2: keycode = keycode_Func2; break;
257 case GDK_KP_F3: keycode = keycode_Func3; break;
259 case GDK_KP_F4: keycode = keycode_Func4; break;
260 case GDK_F5: keycode = keycode_Func5; break;
261 case GDK_F6: keycode = keycode_Func6; break;
262 case GDK_F7: keycode = keycode_Func7; break;
263 case GDK_F8: keycode = keycode_Func8; break;
264 case GDK_F9: keycode = keycode_Func9; break;
265 case GDK_F10: keycode = keycode_Func10; break;
266 case GDK_F11: keycode = keycode_Func11; break;
267 case GDK_F12: keycode = keycode_Func12; break;
269 keycode = gdk_keyval_to_unicode(event->keyval);
270 /* If keycode is 0, then keyval was not recognized; also return
271 unknown if Latin-1 input was requested and the character is not in
273 if(keycode == 0 || (win->input_request_type == INPUT_REQUEST_CHARACTER && keycode > 255))
274 keycode = keycode_Unknown;
277 event_throw(evtype_CharInput, win, keycode, 0);
279 /* Only one keypress will be handled */
280 win->input_request_type = INPUT_REQUEST_NONE;
281 g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler );
286 /* Internal function: finish handling a line input request, for both text grid and text buffer windows. */
288 end_line_input_request(winid_t win, gchar *inserted_text)
290 /* Convert the string from UTF-8 to Latin-1 or Unicode */
291 if(win->input_request_type == INPUT_REQUEST_LINE)
293 GError *error = NULL;
296 latin1 = g_convert_with_fallback(inserted_text, -1, "ISO-8859-1", "UTF-8", "?", NULL, &bytes_written, &error);
300 g_warning("Error during utf8->latin1 conversion: %s", error->message);
301 event_throw(evtype_LineInput, win, 0, 0);
305 /* Place input in the echo stream */
306 if(win->echo_stream != NULL)
307 glk_put_string_stream(win->echo_stream, latin1);
309 /* Copy the string (but not the NULL at the end) */
310 int copycount = MIN(win->line_input_buffer_max_len, bytes_written - 1);
311 memcpy(win->line_input_buffer, latin1, copycount);
313 event_throw(evtype_LineInput, win, copycount, 0);
315 else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
319 unicode = g_utf8_to_ucs4_fast(inserted_text, -1, &items_written);
323 g_warning("Error during utf8->unicode conversion");
324 event_throw(evtype_LineInput, win, 0, 0);
328 /* Place input in the echo stream */
330 if(win->echo_stream != NULL)
331 glk_put_string_stream_uni(window->echo_stream, unicode);*/
333 /* Copy the string (but not the NULL at the end) */
334 int copycount = MIN(win->line_input_buffer_max_len, items_written);
335 memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar));
337 event_throw(evtype_LineInput, win, copycount, 0);
340 g_warning("%s: Wrong input request type.", __func__);
342 win->input_request_type = INPUT_REQUEST_NONE;
345 /* Internal function: calculate the bounds of the line input field of a text grid window. Fill them into start_iter and end_iter */
347 text_grid_get_line_input_bounds(winid_t win, GtkTextIter *start_iter, GtkTextIter *end_iter)
349 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
350 /* "cursor_position" is the Glk output cursor position, which is also the position of the start of the line input buffer */
351 GtkTextMark *input_start = gtk_text_buffer_get_mark(buffer, "cursor_position");
352 gtk_text_buffer_get_iter_at_mark(buffer, start_iter, input_start);
353 gint startpos = gtk_text_iter_get_line_offset(start_iter);
354 gint input_length = MIN(win->width - startpos, win->line_input_buffer_max_len);
355 *end_iter = *start_iter;
356 gtk_text_iter_set_line_offset(end_iter, startpos + input_length);
359 /* Internal function: Retag the input field bounds */
361 text_grid_retag_line_input_bounds(winid_t win)
363 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
364 GtkTextIter start, end, input_start, input_end;
365 text_grid_get_line_input_bounds(win, &input_start, &input_end);
366 gtk_text_buffer_get_start_iter(buffer, &start);
367 gtk_text_buffer_get_end_iter(buffer, &end);
368 gtk_text_buffer_remove_tag_by_name(buffer, "input_field", &start, &end);
369 gtk_text_buffer_apply_tag_by_name(buffer, "input_field", &input_start, &input_end);
372 /* Internal function: Callback for signal key-press-event on a text grid window. Only unblocked during line input, so that we can catch all the keys that have different functions in text grid line input, before they mess up the window. */
374 on_text_grid_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win)
376 g_return_val_if_fail(win->type == wintype_TextGrid, FALSE);
377 g_return_val_if_fail(win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE, FALSE);
379 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
381 switch(event->keyval) {
382 /* Backspace deletes the character before the cursor, moves the characters in the rest of the input field back one, and inserts a space at the end */
385 GtkTextIter insert, bound, input_start, input_end;
386 /* Backspace acts like Delete if there is text selected */
387 if( gtk_text_buffer_get_selection_bounds(buffer, &insert, &bound) )
389 text_grid_get_line_input_bounds(win, &input_start, &input_end);
391 /* Determine whether part of the selected range is in the input field, and if so, what part */
392 gint insert_where = gtk_text_iter_compare(&insert, &input_start);
393 gint bound_where = gtk_text_iter_compare(&bound, &input_start);
394 if(insert_where < 0 && bound_where <= 0)
397 insert = input_start;
399 insert_where = gtk_text_iter_compare(&insert, &input_end);
400 bound_where = gtk_text_iter_compare(&bound, &input_end);
401 if(insert_where >= 0 && bound_where > 0)
406 /* insert and bound are now within the input field */
407 gsize num_spaces = gtk_text_iter_get_offset(&bound) - gtk_text_iter_get_offset(&insert);
408 gchar *spaces = g_strnfill(num_spaces, ' ');
409 /* Create a temporary mark with right gravity at the end of the input field */
410 GtkTextMark *input_end_mark = gtk_text_buffer_create_mark(buffer, NULL, &input_end, FALSE);
411 gtk_text_buffer_delete(buffer, &insert, &bound);
412 gtk_text_buffer_get_iter_at_mark(buffer, &input_end, input_end_mark);
413 gtk_text_buffer_delete_mark(buffer, input_end_mark);
414 gtk_text_buffer_insert(buffer, &input_end, spaces, -1);
416 text_grid_retag_line_input_bounds(win);
421 text_grid_get_line_input_bounds(win, &input_start, &input_end);
422 /* if cursor is outside input field, continue as default */
423 if( !gtk_text_iter_in_range(&insert, &input_start, &input_end) )
425 /* if cursor is at start of field, do nothing */
426 if( gtk_text_iter_equal(&insert, &input_start) )
428 gtk_text_iter_backward_cursor_position(&insert);
429 /* Create a temporary mark with right gravity at the end of the input field */
430 GtkTextMark *input_end_mark = gtk_text_buffer_create_mark(buffer, NULL, &input_end, FALSE);
431 gtk_text_buffer_delete(buffer, &insert, &bound);
432 /* Revalidate end iterator */
433 gtk_text_buffer_get_iter_at_mark(buffer, &input_end, input_end_mark);
434 gtk_text_buffer_delete_mark(buffer, input_end_mark);
435 gtk_text_buffer_insert(buffer, &input_end, " ", -1);
436 text_grid_retag_line_input_bounds(win);
440 /* Various forms of Enter */
445 GtkTextIter input_start, input_end, start, end;
446 g_signal_handler_block(GTK_TEXT_VIEW(win->widget), win->insert_text_handler);
448 /* Get the input text */
449 text_grid_get_line_input_bounds(win, &input_start, &input_end);
450 gtk_text_iter_forward_char(&input_end);
451 gchar *inserted_text = g_strchomp( gtk_text_buffer_get_text(buffer, &input_start, &input_end, FALSE) );
453 /* Make the buffer uneditable again and remove the special tags */
454 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
455 gtk_text_buffer_get_start_iter(buffer, &start);
456 gtk_text_buffer_get_end_iter(buffer, &end);
457 gtk_text_buffer_remove_tag_by_name(buffer, "input_field", &start, &end);
459 end_line_input_request(win, inserted_text);
460 g_free(inserted_text);
462 /* Block the enter key */
469 /* Internal function: Callback for signal insert-text on a text buffer window.
470 Runs after the default handler has already inserted the text.*/
472 after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win)
474 if( strchr(text, '\n') != NULL )
476 /* Remove signal handlers */
477 GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
478 g_signal_handler_block(window_buffer, win->insert_text_handler);
480 /* Make the window uneditable again and retrieve the text that was input */
481 gchar *inserted_text;
482 GtkTextIter start_iter, end_iter;
484 gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE);
485 GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position");
486 gtk_text_buffer_get_iter_at_mark(window_buffer, &start_iter, input_position);
487 gtk_text_buffer_get_end_iter(window_buffer, &end_iter);
489 inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE);
491 end_line_input_request(win, inserted_text);
492 g_free(inserted_text);