2 #include "chimara-glk-private.h"
4 extern ChimaraGlkPrivate *glk_data;
8 * @win: A window, or %NULL.
9 * @rockptr: Return location for the next window's rock, or %NULL.
11 * Iterates over the list of windows; if @win is %NULL, it returns the first
12 * window, otherwise the next window after @win. If there are no more, it
13 * returns #NULL. The window's rock is stored in @rockptr. If you don't want
14 * the rocks to be returned, you may set @rockptr to %NULL.
16 * The order in which windows are returned is arbitrary. The root window is
17 * not necessarily first, nor is it necessarily last. The order may change
18 * every time you create or destroy a window, invalidating the iteration.
20 * Returns: the next window, or %NULL if there are no more.
23 glk_window_iterate(winid_t win, glui32 *rockptr)
28 retnode = glk_data->root_window;
31 GNode *node = win->window_node;
32 if( G_NODE_IS_LEAF(node) )
34 while(node && node->next == NULL)
42 retnode = g_node_first_child(node);
44 winid_t retval = retnode? (winid_t)retnode->data : NULL;
46 /* Store the window's rock in rockptr */
48 *rockptr = glk_window_get_rock(retval);
54 * glk_window_get_rock:
57 * Returns @win's rock value. Pair windows always have rock 0; all other windows
58 * have the rock value you created them with.
60 * Returns: A rock value.
63 glk_window_get_rock(winid_t win)
65 g_return_val_if_fail(win != NULL, 0);
70 * glk_window_get_type:
73 * Returns @win's type, one of #wintype_Blank, #wintype_Pair,
74 * #wintype_TextBuffer, #wintype_TextGrid, or #wintype_Graphics.
76 * Returns: The window's type.
79 glk_window_get_type(winid_t win)
81 g_return_val_if_fail(win != NULL, 0);
86 * glk_window_get_parent:
89 * Returns the window @win's parent window. If @win is the root window, this
90 * returns %NULL, since the root window has no parent. Remember that the parent
91 * of every window is a pair window; other window types are always childless.
96 glk_window_get_parent(winid_t win)
98 g_return_val_if_fail(win != NULL, NULL);
99 /* Value will also be NULL if win is the root window */
100 return (winid_t)win->window_node->parent->data;
104 * glk_window_get_sibling:
107 * Returns the other child of the window @win's parent. If @win is the
108 * root window, this returns %NULL.
110 * Returns: A window, or %NULL.
113 glk_window_get_sibling(winid_t win)
115 g_return_val_if_fail(win != NULL, NULL);
117 if(G_NODE_IS_ROOT(win->window_node))
119 if(win->window_node->next)
120 return (winid_t)win->window_node->next;
121 return (winid_t)win->window_node->prev;
125 * glk_window_get_root:
127 * Returns the root window. If there are no windows, this returns #NULL.
129 * Returns: A window, or #NULL.
132 glk_window_get_root()
134 if(glk_data->root_window == NULL)
136 return (winid_t)glk_data->root_window->data;
141 * @split: The window to split to create the new window. Must be 0 if there
142 * are no windows yet.
143 * @method: Position of the new window and method of size computation. One of
144 * #winmethod_Above, #winmethod_Below, #winmethod_Left, or #winmethod_Right
145 * OR'ed with #winmethod_Fixed or #winmethod_Proportional. If @wintype is
146 * #wintype_Blank, then #winmethod_Fixed is not allowed.
147 * @size: Size of the new window, in percentage points if @method is
148 * #winmethod_Proportional, otherwise in characters if @wintype is
149 * #wintype_TextBuffer or #wintype_TextGrid, or pixels if @wintype is
151 * @wintype: Type of the new window. One of #wintype_Blank, #wintype_TextGrid,
152 * #wintype_TextBuffer, or #wintype_Graphics.
153 * @rock: The new window's rock value.
155 * If there are no windows, create a new root window. @split must be 0, and
156 * @method and @size are ignored. Otherwise, split window @split into two, with
157 * position, size, and type specified by @method, @size, and @wintype. See the
158 * Glk documentation for the window placement algorithm.
160 * Returns: the new window.
163 glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype,
168 g_warning("glk_window_open: splitting of windows not implemented");
172 if(glk_data->root_window != NULL)
174 g_warning("glk_window_open: there is already a window");
180 /* We only create one window and don't support any more than that */
181 winid_t win = g_new0(struct glk_window_struct, 1);
182 glk_data->root_window = g_node_new(win);
186 win->window_node = glk_data->root_window;
192 /* A blank window will be a label without any text */
193 GtkWidget *label = gtk_label_new("");
194 gtk_widget_show(label);
198 /* You can print to a blank window's stream, but it does nothing */
199 win->window_stream = window_stream_new(win);
200 win->echo_stream = NULL;
204 case wintype_TextBuffer:
206 GtkWidget *scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
207 GtkWidget *textview = gtk_text_view_new();
208 GtkTextBuffer *textbuffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(textview) );
210 gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR );
211 gtk_text_view_set_editable( GTK_TEXT_VIEW(textview), FALSE );
213 gtk_container_add( GTK_CONTAINER(scrolledwindow), textview );
214 gtk_widget_show_all(scrolledwindow);
216 win->widget = textview;
217 win->frame = scrolledwindow;
218 win->window_stream = window_stream_new(win);
219 win->echo_stream = NULL;
220 win->input_request_type = INPUT_REQUEST_NONE;
221 win->line_input_buffer = NULL;
222 win->line_input_buffer_unicode = NULL;
224 /* Connect signal handlers */
225 win->keypress_handler = g_signal_connect( G_OBJECT(textview), "key-press-event", G_CALLBACK(on_window_key_press_event), win );
226 g_signal_handler_block( G_OBJECT(textview), win->keypress_handler );
228 win->insert_text_handler = g_signal_connect_after( G_OBJECT(textbuffer), "insert-text", G_CALLBACK(after_window_insert_text), win );
229 g_signal_handler_block( G_OBJECT(textbuffer), win->insert_text_handler );
231 /* Create an editable tag to indicate uneditable parts of the window
233 gtk_text_buffer_create_tag(textbuffer, "uneditable", "editable", FALSE, "editable-set", TRUE, NULL);
235 /* Mark the position where the user will input text */
237 gtk_text_buffer_get_end_iter(textbuffer, &end);
238 gtk_text_buffer_create_mark(textbuffer, "input_position", &end, TRUE);
244 g_warning("%s: unsupported window type", __func__);
249 /* Put the frame widget into our container */
250 gtk_widget_set_parent(win->frame, GTK_WIDGET(glk_data->self));
251 gtk_widget_queue_resize(GTK_WIDGET(glk_data->self));
260 * @win: Window to close.
261 * @result: Pointer to a #stream_result_t in which to store the write count.
263 * Closes @win, which is pretty much exactly the opposite of opening a window.
264 * It is legal to close all your windows, or to close the root window (which is
267 * The @result argument is filled with the output character count of the window
271 glk_window_close(winid_t win, stream_result_t *result)
273 g_return_if_fail(win != NULL);
277 case wintype_TextBuffer:
278 gtk_widget_destroy( gtk_widget_get_parent(win->widget) );
279 /* TODO: Cancel all input requests */
283 gtk_widget_destroy(win->widget);
287 stream_close_common(win->window_stream, result);
289 g_node_destroy(win->window_node);
290 /* TODO: iterate over child windows, closing them */
299 * Erases the window @win. The meaning of this depends on the window type.
303 * Text buffer: This may do any number of things, such as delete all text in
304 * the window, or print enough blank lines to scroll all text beyond
305 * visibility, or insert a page-break marker which is treated specially by the
306 * display part of the library.
309 * Text grid: This will clear the window, filling all positions with blanks.
310 * The window cursor is moved to the top left corner (position 0,0).
313 * Graphics: Clears the entire window to its current background color.
316 * Other window types: No effect.
320 * It is illegal to erase a window which has line input pending.
323 glk_window_clear(winid_t win)
325 g_return_if_fail(win != NULL);
326 g_return_if_fail(win->input_request_type != INPUT_REQUEST_LINE && win->input_request_type != INPUT_REQUEST_LINE_UNICODE);
334 case wintype_TextBuffer:
335 /* delete all text in the window */
339 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
340 GtkTextIter start, end;
341 gtk_text_buffer_get_bounds(buffer, &start, &end);
342 gtk_text_buffer_delete(buffer, &start, &end);
349 g_warning("glk_window_clear: unsupported window type");
357 * Sets the current stream to @win's window stream. It is exactly equivalent to
358 * <informalexample><programlisting>
359 * glk_stream_set_current(glk_window_get_stream(win))
360 * </programlisting></informalexample>
363 glk_set_window(winid_t win)
365 glk_stream_set_current( glk_window_get_stream(win) );
369 * glk_window_get_stream:
372 * Returns the stream which is associated with @win. Every window has a stream
373 * which can be printed to, but this may not be useful, depending on the window
374 * type. (For example, printing to a blank window's stream has no effect.)
376 * Returns: The window stream.
378 strid_t glk_window_get_stream(winid_t win)
380 g_return_val_if_fail(win != NULL, NULL);
381 return win->window_stream;
385 * glk_window_set_echo_stream:
387 * @str: A stream to attach to the window, or %NULL.
389 * Attaches the stream @str to @win as a second stream. Any text printed to the
390 * window is also echoed to this second stream, which is called the window's
393 * Effectively, any call to glk_put_char() (or the other output commands) which
394 * is directed to @win's window stream, is replicated to @win's echo stream.
395 * This also goes for the style commands such as glk_set_style().
397 * Note that the echoing is one-way. You can still print text directly to the
398 * echo stream, and it will go wherever the stream is bound, but it does not
399 * back up and appear in the window.
401 * It is illegal to set a window's echo stream to be its own window stream,
402 * which would create an infinite loop. It is similarly illegal to create a
403 * longer loop (two or more windows echoing to each other.)
405 * You can reset a window to stop echoing by setting @str to %NULL.
408 glk_window_set_echo_stream(winid_t win, strid_t str)
410 g_return_if_fail(win != NULL);
412 /* Test for an infinite loop */
414 for(; next && next->type == STREAM_TYPE_WINDOW; next = next->window->echo_stream)
416 if(next == win->window_stream)
418 g_warning("%s: Infinite loop detected", __func__);
419 win->echo_stream = NULL;
424 win->echo_stream = str;
428 * glk_window_get_echo_stream:
431 * Returns the echo stream of window @win. If the window has no echo stream (as
432 * is initially the case) then this returns %NULL.
434 * Returns: A stream, or %NULL.
437 glk_window_get_echo_stream(winid_t win)
439 g_return_val_if_fail(win != NULL, NULL);
440 return win->echo_stream;
444 * glk_window_get_size:
446 * @widthptr: Pointer to a location to store the window's width, or %NULL.
447 * @heightptr: Pointer to a location to store the window's height, or %NULL.
449 * Simply returns the actual size of the window, in its measurement system.
450 * Either @widthptr or @heightptr can be %NULL, if you only want one
451 * measurement. (Or, in fact, both, if you want to waste time.)
454 glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr)
456 g_return_if_fail(win != NULL);
458 /* TODO: Write this function */
459 /* For a text buffer window: Return the number of rows and columns which
460 would be available _if_ the window was filled with "0" (zero) characters in
461 the "normal" font. */
462 if(widthptr != NULL) {
466 if(heightptr != NULL) {
472 * glk_window_move_cursor:
473 * @win: A text grid window.
474 * @xpos: Horizontal cursor position.
475 * @ypos: Vertical cursor position.
477 * Sets the cursor position. If you move the cursor right past the end of a
478 * line, it wraps; the next character which is printed will appear at the
479 * beginning of the next line.
481 * If you move the cursor below the last line, or when the cursor reaches the
482 * end of the last line, it goes "off the screen" and further output has no
483 * effect. You must call glk_window_move_cursor() or glk_window_clear() to move
484 * the cursor back into the visible region.
487 * Note that the arguments of glk_window_move_cursor() are <type>unsigned
488 * int</type>s. This is okay, since there are no negative positions. If you try
489 * to pass a negative value, Glk will interpret it as a huge positive value,
490 * and it will wrap or go off the last line.
494 * Also note that the output cursor is not necessarily visible. In particular,
495 * when you are requesting line or character input in a grid window, you cannot
496 * rely on the cursor position to prompt the player where input is indicated.
497 * You should print some character prompt at that spot -- a ">" character, for
502 glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos)
504 g_return_if_fail(win != NULL);
505 g_return_if_fail(win->type == wintype_TextGrid);
506 /* TODO: write this function */