From 2baac1a1aa285bc52da8feb92433144a1e50bcce Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Sun, 24 Aug 2008 00:37:04 +0000 Subject: [PATCH] Code opschonen, toevoegen documentatie git-svn-id: http://lassie.dyndns-server.com/svn/gargoyle-gtk@11 ddfedd41-794f-dd11-ae45-00112f111e67 --- src/case.c | 14 +-- src/event.c | 63 +++++++------ src/event.h | 5 +- src/gestalt.c | 49 +++++++++- src/glk.c | 24 ++--- src/input.c | 250 +++++++++++++++++++++++++++++--------------------- src/input.h | 4 +- src/stream.c | 143 ++++++++++++++++------------- src/stream.h | 2 +- src/strio.c | 75 ++++++--------- src/style.c | 12 +++ src/window.c | 216 ++++++++++++++++++++++++++++++------------- src/window.h | 2 +- 13 files changed, 519 insertions(+), 340 deletions(-) diff --git a/src/case.c b/src/case.c index ecb7862..9e73de1 100644 --- a/src/case.c +++ b/src/case.c @@ -35,8 +35,6 @@ glk_char_to_upper(unsigned char ch) return ch; } -#ifdef GLK_MODULE_UNICODE - /** * glk_buffer_to_lower_case_uni: * @buf: A character array in UCS-4. @@ -104,30 +102,28 @@ glk_buffer_to_upper_case_uni(glui32 *buf, glui32 len, glui32 numchars) * @buf: A character array in UCS-4. * @len: Available length of @buf. * @numchars: Number of characters in @buf. - * @lowerrest: #TRUE if the rest of @buf should be lowercased, #FALSE + * @lowerrest: %TRUE if the rest of @buf should be lowercased, %FALSE * otherwise. * * Converts the first character of @buf to uppercase, if there is such a thing. - * See glk_buffer_to_lower_case_uni(). If @lowerrest is #TRUE, then the + * See glk_buffer_to_lower_case_uni(). If @lowerrest is %TRUE, then the * remainder of @buf is lowercased. * * Returns: The number of characters after conversion. */ glui32 -glk_buffer_to_title_case_uni(glui32 *buf, glui32 len, glui32 numchars, - glui32 lowerrest) +glk_buffer_to_title_case_uni(glui32 *buf, glui32 len, glui32 numchars, glui32 lowerrest) { g_return_val_if_fail(buf != NULL && (len > 0 || numchars > 0), 0); g_return_val_if_fail(numchars <= len, 0); /* GLib has a function that converts _one_ UCS-4 character to _one_ - uppercase UCS-4 character; so apparently we don't have to worry about the + titlecase UCS-4 character; so apparently we don't have to worry about the string length changing... */ *buf = g_unichar_totitle(*buf); /* Call lowercase on the rest of the string */ if(lowerrest) - return glk_buffer_to_lower_case_uni(buf + 1, len - 1, numchars - 1) +1; + return glk_buffer_to_lower_case_uni(buf + 1, len - 1, numchars - 1) + 1; return numchars; } -#endif /* GLK_MODULE_UNICODE */ diff --git a/src/event.c b/src/event.c index 0334e39..76f8240 100644 --- a/src/event.c +++ b/src/event.c @@ -1,10 +1,13 @@ #include "event.h" +#include static GQueue *event_queue = NULL; static GMutex *event_lock = NULL; static GCond *event_queue_not_empty = NULL; static GCond *event_queue_not_full = NULL; +/* Internal function: initialize the event_queue, locking and signalling +objects. */ void events_init() { @@ -14,22 +17,20 @@ events_init() event_queue_not_full = g_cond_new(); } -static void -event_free(gpointer data, gpointer user_data) -{ - g_free(data); -} - +/* Internal function: free the event queue and all the objects allocated in +events_init(). */ void events_free() { - g_queue_foreach(event_queue, event_free, NULL); + g_queue_foreach(event_queue, (GFunc)g_free, NULL); g_queue_free(event_queue); g_mutex_free(event_lock); g_cond_free(event_queue_not_empty); g_cond_free(event_queue_not_full); } +/* Internal function: push an event onto the event queue. If the event queue is +full, wait for max three seconds and then drop the event. */ void event_throw(glui32 type, winid_t win, glui32 val1, glui32 val2) { @@ -37,22 +38,22 @@ event_throw(glui32 type, winid_t win, glui32 val1, glui32 val2) g_get_current_time(&timeout); g_time_val_add(&timeout, 3000000); /* 3 Seconds */ - /* Wait for room in the event queue */ g_mutex_lock(event_lock); - if( g_queue_get_length(event_queue) >= EVENT_QUEUE_MAX_LENGTH ) { - if( !g_cond_timed_wait(event_queue_not_full, event_lock, &timeout) ) { - /* Drop the event */ + + /* Wait for room in the event queue */ + if( g_queue_get_length(event_queue) >= EVENT_QUEUE_MAX_LENGTH ) + if( !g_cond_timed_wait(event_queue_not_full, event_lock, &timeout) ) + { + /* Drop the event after 3 seconds */ g_mutex_unlock(event_lock); return; } - } event_t *event = g_new0(event_t, 1); event->type = type; event->win = win; event->val1 = val1; event->val2 = val2; - g_queue_push_head(event_queue, event); /* Signal that there is an event */ @@ -61,28 +62,38 @@ event_throw(glui32 type, winid_t win, glui32 val1, glui32 val2) g_mutex_unlock(event_lock); } +/** + * glk_select: + * @event: Pointer to an #event_t. + * + * Causes the program to wait for an event, and then store it in the structure + * pointed to by @event. Unlike most Glk functions that take pointers, the + * argument of glk_select() may not be %NULL. + * + * Most of the time, you only get the events that you request. However, there + * are some events which can arrive at any time. This is why you must always + * call glk_select() in a loop, and continue the loop until you get the event + * you really want. + */ void glk_select(event_t *event) { - event_t *retrieved_event; - g_return_if_fail(event != NULL); g_mutex_lock(event_lock); /* Wait for an event */ - if( g_queue_is_empty(event_queue) ) { + if( g_queue_is_empty(event_queue) ) g_cond_wait(event_queue_not_empty, event_lock); - } - - retrieved_event = g_queue_pop_tail(event_queue); - g_return_if_fail(retrieved_event != NULL); - - event->type = retrieved_event->type; - event->win = retrieved_event->win; - event->val1 = retrieved_event->val1; - event->val2 = retrieved_event->val2; + event_t *retrieved_event = g_queue_pop_tail(event_queue); + if(retrieved_event == NULL) + { + g_mutex_unlock(event_lock); + g_warning("%s: Retrieved NULL event from non-empty event queue", __func__); + return; + } + memcpy(event, retrieved_event, sizeof(event_t)); g_free(retrieved_event); /* Signal that the event queue is no longer full */ @@ -90,7 +101,7 @@ glk_select(event_t *event) g_mutex_unlock(event_lock); - /* Implementation defined events */ + /* Implementation-defined events */ switch(event->type) { case EVENT_TYPE_QUIT: g_thread_exit(NULL); diff --git a/src/event.h b/src/event.h index 3acbdee..b17335c 100644 --- a/src/event.h +++ b/src/event.h @@ -4,9 +4,10 @@ #include #include "glk.h" -#define EVENT_QUEUE_MAX_LENGTH 100 +#define EVENT_QUEUE_MAX_LENGTH (100) -#define EVENT_TYPE_QUIT -1 +/* Implementation-defined events */ +#define EVENT_TYPE_QUIT (-1) void events_init(); void events_free(); diff --git a/src/gestalt.c b/src/gestalt.c index 1bb7f7e..4a586ce 100644 --- a/src/gestalt.c +++ b/src/gestalt.c @@ -27,11 +27,38 @@ glk_gestalt(glui32 sel, glui32 val) * @sel: A selector, representing which capability to request information * about. * @val: Extra information, depending on the value of @sel. - * @arr: Location of an array to store extra information in, or #NULL. - * @arrlen: Length of @arr, or 0 if @arr is #NULL. + * @arr: Location of an array to store extra information in, or %NULL. + * @arrlen: Length of @arr, or 0 if @arr is %NULL. * - * Calls the gestalt system to request information about selector @sel, - * possibly returning information in @arr. + * Calls the gestalt system to request information about the capabilities of the + * API. The selector @sel tells which capability you are requesting information + * about; the other three arguments are additional information, which may or may + * not be meaningful. The @arr and @arrlen arguments are always optional; you + * may always pass %NULL and 0, if you do not want whatever information they + * represent. glk_gestalt() is simply a shortcut for this; glk_gestalt(x, y) is + * exactly the same as glk_gestalt_ext(x, y, NULL, 0). + * + * The critical point is that if the Glk library has never heard of the selector + * sel, it will return 0. It is always safe to call glk_gestalt(x, y) (or + * glk_gestalt_ext(x, y, NULL, 0)). Even if you are using an old library, which + * was compiled before the given capability was imagined, you can test for the + * capability by calling glk_gestalt(); the library will correctly indicate that + * it does not support it, by returning 0. + * + * + * It is also safe to call glk_gestalt_ext(x, y, z, zlen) for an unknown + * selector x, where z is not %NULL, as long as z points at an array of at + * least zlen elements. The selector will be careful not to write beyond that + * point in the array, if it writes to the array at all. + * + * + * + * If a selector does not use the second argument, you should always pass 0; do + * not assume that the second argument is simply ignored. This is because the + * selector may be extended in the future. You will continue to get the current + * behavior if you pass 0 as the second argument, but other values may produce + * other behavior. + * * * Returns: an integer, depending on what selector was called. */ @@ -54,6 +81,20 @@ glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) return gestalt_CharOutput_CannotPrint; /* Can print all other Latin-1 characters */ return gestalt_CharOutput_ExactPrint; + + /* Which characters can the player type in line input? */ + case gestalt_LineInput: + /* Does not accept control chars */ + if( val < 32 || (val >= 127 && val <= 159) ) + return 0; + return 1; + + /* Which characters can the player type in char input? */ + case gestalt_CharInput: + /* Does not accept control chars or unknown */ + if( val < 32 || (val >= 127 && val <= 159) || val == keycode_Unknown ) + return 0; + return 1; /* Selector not supported */ default: diff --git a/src/glk.c b/src/glk.c index fc22682..dbec759 100644 --- a/src/glk.c +++ b/src/glk.c @@ -6,8 +6,19 @@ /** * glk_exit: * - * End the Glk program. As far as the client program is concerned, this - * function does not return. + * Shuts down the Glk program. This function does not return. + * + * If you print some text to a window and then shut down your program, you can + * assume that the player will be able to read it. + * + * + * You should only shut down your program with glk_exit() or by returning from + * your glk_main() function. If you call the ANSI exit() + * function, bad things may happen. This Glk library is designed for multiple + * sessions, for example, and you would be cutting off all the sessions instead + * of just yours. You would also prevent final text from being visible to the + * player. + * */ void glk_exit(void) @@ -15,12 +26,3 @@ glk_exit(void) g_thread_exit(NULL); } -/* -void -glk_select(event_t *event) -{ - gtk_main_iteration(); -} -*/ - - diff --git a/src/input.c b/src/input.c index caa1824..d9b0965 100644 --- a/src/input.c +++ b/src/input.c @@ -1,59 +1,47 @@ #include "input.h" /** glk_request_char_event: - * @win: A window to request char events from Request + * @win: A window to request char events from. * - * Request input of a Latin-1 character or special key. A window cannot have requests - * for both character and line input at the same time. Nor can it have requests - * for character input of both types (Latin-1 and Unicode). It is illegal to - * call glk_request_char_event() if the window already has a pending request - * for either character or line input. + * Request input of a Latin-1 character or special key. A window cannot have + * requests for both character and line input at the same time. Nor can it have + * requests for character input of both types (Latin-1 and Unicode). It is + * illegal to call glk_request_char_event() if the window already has a pending + * request for either character or line input. */ void glk_request_char_event(winid_t win) { g_return_if_fail(win); g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE); - g_return_if_fail(win->window_type != wintype_TextBuffer || win->window_type != wintype_TextGrid); + g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid); win->input_request_type = INPUT_REQUEST_CHARACTER; g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler ); } /** glk_request_char_event_uni: - * @win: A window to request char events from Request + * @win: A window to request char events from. * - * Request input of a Unicode character or special key. A window cannot have requests - * for both character and line input at the same time. Nor can it have requests - * for character input of both types (Latin-1 and Unicode). It is illegal to - * call glk_request_char_event_uni() if the window already has a pending request - * for either character or line input. + * Request input of a Unicode character or special key. See + * glk_request_char_event(). */ void glk_request_char_event_uni(winid_t win) { g_return_if_fail(win); g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE); - g_return_if_fail(win->window_type != wintype_TextBuffer || win->window_type != wintype_TextGrid); + g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid); win->input_request_type = INPUT_REQUEST_CHARACTER_UNICODE; g_signal_handler_unblock( G_OBJECT(win->widget), win->keypress_handler ); } +/* Internal function: Request either latin-1 or unicode line input. */ void -glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen) +request_line_event_common(winid_t win, gboolean insert, gchar *inserttext) { - GtkTextBuffer *window_buffer; - - g_return_if_fail(win); - g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE); - g_return_if_fail(win->window_type != wintype_TextBuffer || win->window_type != wintype_TextGrid); - - window_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(win->widget)); - - win->input_request_type = INPUT_REQUEST_LINE; - win->line_input_buffer = buf; - win->line_input_buffer_max_len = maxlen; + GtkTextBuffer *window_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(win->widget)); /* Move the input_position mark to the end of the window_buffer */ GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position"); @@ -67,47 +55,92 @@ glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen) gtk_text_buffer_get_start_iter(window_buffer, &start_iter); gtk_text_buffer_remove_tag_by_name(window_buffer, "uneditable", &start_iter, &end_iter); gtk_text_buffer_apply_tag_by_name(window_buffer, "uneditable", &start_iter, &end_iter); - - if(initlen > 0) { - gtk_text_buffer_insert(window_buffer, &end_iter, buf, initlen); - } - + + /* Insert pre-entered text if needed */ + if(insert) + gtk_text_buffer_insert(window_buffer, &end_iter, inserttext, -1); + + /* Scroll to input point */ gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position); g_signal_handler_unblock( G_OBJECT(window_buffer), win->insert_text_handler ); gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE); } +/** + * glk_request_line_event: + * @win: A text buffer or text grid window to request line input on. + * @buf: A buffer of at least @maxlen bytes. + * @maxlen: Length of the buffer. + * @initlen: The number of characters in @buf to pre-enter. + * + * Requests input of a line of Latin-1 characters. A window cannot have requests + * for both character and line input at the same time. Nor can it have requests + * for line input of both types (Latin-1 and Unicode). It is illegal to call + * glk_request_line_event() if the window already has a pending request for + * either character or line input. + * + * The @buf argument is a pointer to space where the line input will be stored. + * (This may not be %NULL.) @maxlen is the length of this space, in bytes; the + * library will not accept more characters than this. If @initlen is nonzero, + * then the first @initlen bytes of @buf will be entered as pre-existing input + * -- just as if the player had typed them himself. (The player can continue + * composing after this pre-entered input, or delete it or edit as usual.) + * + * The contents of the buffer are undefined until the input is completed (either + * by a line input event, or glk_cancel_line_event(). The library may or may not + * fill in the buffer as the player composes, while the input is still pending; + * it is illegal to change the contents of the buffer yourself. + */ void -glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen) +glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen) { - GtkTextBuffer *window_buffer; - g_return_if_fail(win); + g_return_if_fail(buf); g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE); - g_return_if_fail(win->window_type != wintype_TextBuffer || win->window_type != wintype_TextGrid); + g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid); + + win->input_request_type = INPUT_REQUEST_LINE; + win->line_input_buffer = buf; + win->line_input_buffer_max_len = maxlen; + + gchar *inserttext = (initlen > 0)? g_strndup(buf, initlen) : g_strdup(""); + request_line_event_common(win, (initlen > 0), inserttext); + g_free(inserttext); +} - window_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(win->widget)); +/** + * glk_request_line_event_uni: + * @win: A text buffer or text grid window to request line input on. + * @buf: A buffer of at least @maxlen characters. + * @maxlen: Length of the buffer. + * @initlen: The number of characters in @buf to pre-enter. + * + * Request input of a line of Unicode characters. This works the same as + * glk_request_line_event(), except the result is stored in an array of + * glui32 values instead of an array of characters, and the values + * may be any valid Unicode code points. + * + * The result will be in Unicode Normalization Form C. This basically means that + * composite characters will be single characters where possible, instead of + * sequences of base and combining marks. See + * Unicode Standard Annex #15 + * for the details. + */ +void +glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen) +{ + g_return_if_fail(win); + g_return_if_fail(buf); + g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE); + g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid); win->input_request_type = INPUT_REQUEST_LINE_UNICODE; win->line_input_buffer_unicode = buf; win->line_input_buffer_max_len = maxlen; - /* Move the input_position mark to the end of the window_buffer */ - GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position"); - GtkTextIter end_iter; - gtk_text_buffer_get_end_iter(window_buffer, &end_iter); - gtk_text_buffer_move_mark(window_buffer, input_position, &end_iter); - - /* Set the entire contents of the window_buffer as uneditable - * (so input can only be entered at the end) */ - GtkTextIter start_iter; - gtk_text_buffer_get_start_iter(window_buffer, &start_iter); - gtk_text_buffer_remove_tag_by_name(window_buffer, "uneditable", &start_iter, &end_iter); - gtk_text_buffer_apply_tag_by_name(window_buffer, "uneditable", &start_iter, &end_iter); - + gchar *utf8; if(initlen > 0) { GError *error = NULL; - gchar *utf8; utf8 = g_ucs4_to_utf8(buf, initlen, NULL, NULL, &error); if(utf8 == NULL) @@ -115,25 +148,22 @@ glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initl error_dialog(NULL, error, "Error during unicode->utf8 conversion: "); return; } - - gtk_text_buffer_insert(window_buffer, &end_iter, utf8, -1); - g_free(utf8); } + else + utf8 = g_strdup(""); - gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(win->widget), input_position); - g_signal_handler_unblock( G_OBJECT(window_buffer), win->insert_text_handler ); - gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), TRUE); + request_line_event_common(win, (initlen > 0), utf8); + g_free(utf8); } - - +/* Internal function: Callback for signal key-press-event on a text buffer +window. */ gboolean -on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t window) +on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win) { - if(window->input_request_type != INPUT_REQUEST_CHARACTER && - window->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE) { + if(win->input_request_type != INPUT_REQUEST_CHARACTER && + win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE) return FALSE; - } int keycode; @@ -146,13 +176,15 @@ on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t window) case GDK_KP_Left: keycode = keycode_Left; break; case GDK_Right: case GDK_KP_Right: keycode = keycode_Right; break; + case GDK_Linefeed: case GDK_Return: case GDK_KP_Enter: keycode = keycode_Return; break; case GDK_Delete: case GDK_BackSpace: case GDK_KP_Delete: keycode = keycode_Delete; break; case GDK_Escape: keycode = keycode_Escape; break; - case GDK_Tab: keycode = keycode_Tab; break; + case GDK_Tab: + case GDK_KP_Tab: keycode = keycode_Tab; break; case GDK_Page_Up: case GDK_KP_Page_Up: keycode = keycode_PageUp; break; case GDK_Page_Down: @@ -161,10 +193,14 @@ on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t window) case GDK_KP_Home: keycode = keycode_Home; break; case GDK_End: case GDK_KP_End: keycode = keycode_End; break; - case GDK_F1: keycode = keycode_Func1; break; - case GDK_F2: keycode = keycode_Func2; break; - case GDK_F3: keycode = keycode_Func3; break; - case GDK_F4: keycode = keycode_Func4; break; + case GDK_F1: + case GDK_KP_F1: keycode = keycode_Func1; break; + case GDK_F2: + case GDK_KP_F2: keycode = keycode_Func2; break; + case GDK_F3: + case GDK_KP_F3: keycode = keycode_Func3; break; + case GDK_F4: + case GDK_KP_F4: keycode = keycode_Func4; break; case GDK_F5: keycode = keycode_Func5; break; case GDK_F6: keycode = keycode_Func6; break; case GDK_F7: keycode = keycode_Func7; break; @@ -175,30 +211,25 @@ on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t window) case GDK_F12: keycode = keycode_Func12; break; default: keycode = gdk_keyval_to_unicode(event->keyval); + /* If keycode is 0, then keyval was not recognized; also return + unknown if Latin-1 input was requested and the character is not in + Latin-1 */ + if(keycode == 0 || (win->input_request_type == INPUT_REQUEST_CHARACTER && keycode > 255)) + keycode = keycode_Unknown; } - if(window->input_request_type == INPUT_REQUEST_CHARACTER) { - if(keycode >= 1 || keycode <= 255) { - event_throw(evtype_CharInput, window, keycode, 0); - } else { - event_throw(evtype_CharInput, window, keycode_Unknown, 0); - } - } else { - if(keycode == 0) { - event_throw(evtype_CharInput, window, keycode_Unknown, 0); - } else { - event_throw(evtype_CharInput, window, keycode, 0); - } - } - + event_throw(evtype_CharInput, win, keycode, 0); + /* Only one keypress will be handled */ - window->input_request_type = INPUT_REQUEST_NONE; - g_signal_handler_block( G_OBJECT(window->widget), window->keypress_handler ); + win->input_request_type = INPUT_REQUEST_NONE; + g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler ); return TRUE; } -void +/* Internal function: Callback for signal insert-text on a text buffer window. +Not used; cannot modify the text buffer from within the callback?? Segfault! */ +/*void on_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, gpointer user_data) { gchar *newline_pos = strchr(text, '\n'); @@ -206,16 +237,19 @@ on_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *t printf("position: %d\n", newline_pos-text); *newline_pos = 'a'; } -} +}*/ +/* Internal function: Callback for signal insert-text on a text buffer window. +Runs after the default handler has already inserted the text.*/ void -after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t window) +after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win) { - if( strchr(text, '\n') != NULL) { + if( strchr(text, '\n') != NULL ) + { /* Make the window uneditable again and remove signal handlers */ - GtkTextBuffer *window_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(window->widget)); - gtk_text_view_set_editable(GTK_TEXT_VIEW(window->widget), FALSE); - g_signal_handler_block(window_buffer, window->insert_text_handler); + GtkTextBuffer *window_buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); + gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE); + g_signal_handler_block(window_buffer, win->insert_text_handler); /* Retrieve the text that was input */ GtkTextMark *input_position = gtk_text_buffer_get_mark(window_buffer, "input_position"); @@ -225,9 +259,9 @@ after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar gtk_text_buffer_get_end_iter(window_buffer, &end_iter); gchar *inserted_text = gtk_text_buffer_get_text(window_buffer, &start_iter, &end_iter, FALSE); - /* Convert the string from UTF-8 to Latin-1 or Unicode */ - if(window->input_request_type == INPUT_REQUEST_LINE) { + if(win->input_request_type == INPUT_REQUEST_LINE) + { GError *error = NULL; gchar *latin1; gsize bytes_written; @@ -237,20 +271,22 @@ after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar if(latin1 == NULL) { error_dialog(NULL, error, "Error during utf8->latin1 conversion: "); - event_throw(evtype_LineInput, window, 0, 0); + event_throw(evtype_LineInput, win, 0, 0); return; } /* Place input in the echo stream */ - if(window->echo_stream != NULL) - glk_put_string_stream(window->echo_stream, latin1); + if(win->echo_stream != NULL) + glk_put_string_stream(win->echo_stream, latin1); /* Copy the string (but not the NULL at the end) */ - memcpy(window->line_input_buffer, latin1, MIN(window->line_input_buffer_max_len, bytes_written-1)); + int copycount = MIN(win->line_input_buffer_max_len, bytes_written - 1); + memcpy(win->line_input_buffer, latin1, copycount); g_free(latin1); - event_throw(evtype_LineInput, window, MIN(window->line_input_buffer_max_len, bytes_written-1), 0); + event_throw(evtype_LineInput, win, copycount, 0); } - else if(window->input_request_type == INPUT_REQUEST_LINE_UNICODE) { + else if(win->input_request_type == INPUT_REQUEST_LINE_UNICODE) + { gunichar *unicode; glong items_written; unicode = g_utf8_to_ucs4_fast(inserted_text, -1, &items_written); @@ -259,24 +295,24 @@ after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar if(unicode == NULL) { error_dialog(NULL, NULL, "Error during utf8->unicode conversion"); - event_throw(evtype_LineInput, window, 0, 0); + event_throw(evtype_LineInput, win, 0, 0); return; } /* Place input in the echo stream */ - // TODO: fixme - // if(window->echo_stream != NULL) - // glk_put_string_stream_uni(window->echo_stream, unicode); + /* TODO: fixme + if(win->echo_stream != NULL) + glk_put_string_stream_uni(window->echo_stream, unicode);*/ /* Copy the string (but not the NULL at the end) */ - memcpy(window->line_input_buffer_unicode, unicode, MIN(window->line_input_buffer_max_len, items_written)*sizeof(gunichar)); + int copycount = MIN(win->line_input_buffer_max_len, items_written); + memcpy(win->line_input_buffer_unicode, unicode, copycount * sizeof(gunichar)); g_free(unicode); - event_throw(evtype_LineInput, window, MIN(window->line_input_buffer_max_len, items_written), 0); + event_throw(evtype_LineInput, win, copycount, 0); } - else { + else g_warning("%s: Wrong input request type.", __func__); - } - window->input_request_type = INPUT_REQUEST_NONE; + win->input_request_type = INPUT_REQUEST_NONE; } } diff --git a/src/input.h b/src/input.h index e961706..7f1d322 100644 --- a/src/input.h +++ b/src/input.h @@ -9,7 +9,7 @@ #include "window.h" #include "event.h" -gboolean on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t window); +gboolean on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win); void on_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, gpointer user_data); -void after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t window); +void after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win); #endif diff --git a/src/stream.c b/src/stream.c index b737f35..abcf2dd 100644 --- a/src/stream.c +++ b/src/stream.c @@ -13,31 +13,32 @@ strid_t window_stream_new(winid_t window) { /* Create stream and connect it to window */ - strid_t s = g_new0(struct glk_stream_struct, 1); - s->file_mode = filemode_Write; - s->stream_type = STREAM_TYPE_WINDOW; - s->window = window; + strid_t str = g_new0(struct glk_stream_struct, 1); + str->file_mode = filemode_Write; + str->type = STREAM_TYPE_WINDOW; + str->window = window; + /* Add it to the global stream list */ - stream_list = g_list_prepend(stream_list, s); - s->stream_list = stream_list; + stream_list = g_list_prepend(stream_list, str); + str->stream_list = stream_list; - return s; + return str; } /** * glk_stream_iterate: - * @str: A stream, or #NULL. - * @rockptr: Return location for the next window's rock, or #NULL. + * @str: A stream, or %NULL. + * @rockptr: Return location for the next window's rock, or %NULL. * - * Iterates over the list of streams; if @str is #NULL, it returns the first + * Iterates over the list of streams; if @str is %NULL, it returns the first * stream, otherwise the next stream after @str. If there are no more, it - * returns #NULL. The stream's rock is stored in @rockptr. If you don't want - * the rocks to be returned, you may set @rockptr to #NULL. + * returns %NULL. The stream's rock is stored in @rockptr. If you don't want + * the rocks to be returned, you may set @rockptr to %NULL. * * The order in which streams are returned is arbitrary. The order may change * every time you create or destroy a stream, invalidating the iteration. * - * Returns: the next stream, or #NULL if there are no more. + * Returns: the next stream, or %NULL if there are no more. */ strid_t glk_stream_iterate(strid_t str, glui32 *rockptr) @@ -74,17 +75,18 @@ glk_stream_get_rock(strid_t str) /** * glk_stream_set_current: - * @str: An output stream, or NULL. + * @str: An output stream, or %NULL. * - * Sets the current stream to @str, or to nothing if @str is #NULL. + * Sets the current stream to @str, which must be an output stream. You may set + * the current stream to %NULL, which means the current stream is not set to + * anything. */ void glk_stream_set_current(strid_t str) { if(str != NULL && str->file_mode == filemode_Read) { - g_warning("glk_stream_set_current: " - "Cannot set current stream to non output stream"); + g_warning("%s: Cannot set current stream to non output stream", __func__); return; } @@ -94,7 +96,7 @@ glk_stream_set_current(strid_t str) /** * glk_stream_get_current: * - * Returns the current stream, or #NULL if there is none. + * Returns the current stream, or %NULL if there is none. * * Returns: A stream. */ @@ -108,12 +110,12 @@ glk_stream_get_current() * glk_put_char: * @ch: A character in Latin-1 encoding. * - * Prints one character @ch to the current stream. + * Prints one character to the current stream. As with all basic functions, the + * character is assumed to be in the Latin-1 character encoding. */ void glk_put_char(unsigned char ch) { - /* Illegal to print to the current stream if it is NULL */ g_return_if_fail(current_stream != NULL); glk_put_char_stream(current_stream, ch); } @@ -122,12 +124,17 @@ glk_put_char(unsigned char ch) * glk_put_string: * @s: A null-terminated string in Latin-1 encoding. * - * Prints @s to the current stream. + * Prints a null-terminated string to the current stream. It is exactly + * equivalent to: + * + * for (ptr = s; *ptr; ptr++) + * glk_put_char(*ptr); + * + * However, it may be more efficient. */ void glk_put_string(char *s) { - /* Illegal to print to the current stream if it is NULL */ g_return_if_fail(current_stream != NULL); glk_put_string_stream(current_stream, s); } @@ -137,12 +144,17 @@ glk_put_string(char *s) * @buf: An array of characters in Latin-1 encoding. * @len: Length of @buf. * - * Prints @buf to the current stream. + * Prints a block of characters to the current stream. It is exactly equivalent + * to: + * + * for (i = 0; i < len; i++) + * glk_put_char(buf[i]); + * + * However, it may be more efficient. */ void glk_put_buffer(char *buf, glui32 len) { - /* Illegal to print to the current stream if it is NULL */ g_return_if_fail(current_stream != NULL); glk_put_buffer_stream(current_stream, buf, len); } @@ -176,8 +188,8 @@ glk_put_buffer(char *buf, glui32 len) * The data is written to the buffer exactly as it was passed to the printing * functions (glk_put_char(), etc.); input functions will read the data exactly * as it exists in memory. No platform-dependent cookery will be done on it. - * [You can write a disk file in text mode, but a memory stream is effectively - * always in binary mode.] + * (You can write a disk file in text mode, but a memory stream is effectively + * always in binary mode.) * * Unicode values (characters greater than 255) cannot be written to the buffer. * If you try, they will be stored as 0x3F ("?") characters. @@ -192,20 +204,20 @@ glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock) { g_return_val_if_fail(fmode != filemode_WriteAppend, NULL); - strid_t s = g_new0(struct glk_stream_struct, 1); - s->rock = rock; - s->file_mode = fmode; - s->stream_type = STREAM_TYPE_MEMORY; - s->buffer = buf; - s->mark = 0; - s->buflen = buflen; - s->unicode = FALSE; + strid_t str = g_new0(struct glk_stream_struct, 1); + str->rock = rock; + str->file_mode = fmode; + str->type = STREAM_TYPE_MEMORY; + str->buffer = buf; + str->mark = 0; + str->buflen = buflen; + str->unicode = FALSE; /* Add it to the global stream list */ - stream_list = g_list_prepend(stream_list, s); - s->stream_list = stream_list; + stream_list = g_list_prepend(stream_list, str); + str->stream_list = stream_list; - return s; + return str; } /** @@ -222,25 +234,24 @@ glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock) * bytes. */ strid_t -glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, glui32 fmode, - glui32 rock) +glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, glui32 fmode, glui32 rock) { g_return_val_if_fail(fmode != filemode_WriteAppend, NULL); - strid_t s = g_new0(struct glk_stream_struct, 1); - s->rock = rock; - s->file_mode = fmode; - s->stream_type = STREAM_TYPE_MEMORY; - s->ubuffer = buf; - s->mark = 0; - s->buflen = buflen; - s->unicode = TRUE; + strid_t str = g_new0(struct glk_stream_struct, 1); + str->rock = rock; + str->file_mode = fmode; + str->type = STREAM_TYPE_MEMORY; + str->ubuffer = buf; + str->mark = 0; + str->buflen = buflen; + str->unicode = TRUE; /* Add it to the global stream list */ - stream_list = g_list_prepend(stream_list, s); - s->stream_list = stream_list; + stream_list = g_list_prepend(stream_list, str); + str->stream_list = stream_list; - return s; + return str; } /* Internal function: create a stream using the given parameters. */ @@ -306,21 +317,21 @@ file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode) } } - strid_t s = g_new0(struct glk_stream_struct, 1); - s->rock = rock; - s->file_mode = fmode; - s->stream_type = STREAM_TYPE_FILE; - s->file_pointer = fp; - s->binary = binary; - s->unicode = unicode; - s->filename = g_filename_to_utf8(fileref->filename, -1, NULL, NULL, NULL); - if(s->filename == NULL) - s->filename = g_strdup("Unknown file name"); /* fail silently */ + strid_t str = g_new0(struct glk_stream_struct, 1); + str->rock = rock; + str->file_mode = fmode; + str->type = STREAM_TYPE_FILE; + str->file_pointer = fp; + str->binary = binary; + str->unicode = unicode; + str->filename = g_filename_to_utf8(fileref->filename, -1, NULL, NULL, NULL); + if(str->filename == NULL) + str->filename = g_strdup("Unknown file name"); /* fail silently */ /* Add it to the global stream list */ - stream_list = g_list_prepend(stream_list, s); - s->stream_list = stream_list; + stream_list = g_list_prepend(stream_list, str); + str->stream_list = stream_list; - return s; + return str; } /** @@ -394,7 +405,7 @@ glk_stream_close(strid_t str, stream_result_t *result) g_return_if_fail(str != NULL); /* Free resources associated with one specific type of stream */ - switch(str->stream_type) + switch(str->type) { case STREAM_TYPE_WINDOW: g_warning("%s: Attempted to close a window stream. Use glk_window_" @@ -420,20 +431,24 @@ glk_stream_close(strid_t str, stream_result_t *result) stream_close_common(str, result); } +/* Internal function: Stuff to do upon closing any type of stream. */ void stream_close_common(strid_t str, stream_result_t *result) { /* Remove the stream from the global stream list */ stream_list = g_list_delete_link(stream_list, str->stream_list); + /* If it was the current output stream, set that to NULL */ if(current_stream == str) current_stream = NULL; + /* If it was one or more windows' echo streams, set those to NULL */ winid_t win; for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) if(win->echo_stream == str) win->echo_stream = NULL; + /* Return the character counts */ if(result) { diff --git a/src/stream.h b/src/stream.h index b5a1e60..04134f5 100644 --- a/src/stream.h +++ b/src/stream.h @@ -22,7 +22,7 @@ struct glk_stream_struct glui32 file_mode; glui32 read_count; glui32 write_count; - enum StreamType stream_type; + enum StreamType type; /* Specific to window stream: the window this stream is connected to */ winid_t window; /* For memory and file streams */ diff --git a/src/strio.c b/src/strio.c index de6ccb4..6b70dfa 100644 --- a/src/strio.c +++ b/src/strio.c @@ -4,8 +4,6 @@ #include #include -#define min(x,y) ( (x > y)? y : x ) - /* * **************** WRITING FUNCTIONS ******************************************** @@ -71,15 +69,15 @@ write_utf8_to_window(winid_t win, gchar *s) gdk_threads_leave(); } -/* Internal function: write a UTF-8 buffer with length to a stream. */ +/* Internal function: write a Latin-1 buffer with length to a stream. */ static void write_buffer_to_stream(strid_t str, gchar *buf, glui32 len) { - switch(str->stream_type) + switch(str->type) { case STREAM_TYPE_WINDOW: /* Each window type has a different way of printing to it */ - switch(str->window->window_type) + switch(str->window->type) { /* Printing to these windows' streams does nothing */ case wintype_Blank: @@ -100,8 +98,7 @@ write_buffer_to_stream(strid_t str, gchar *buf, glui32 len) str->write_count += len; break; default: - g_warning("%s: Writing to this kind of window unsupported.", - __func__); + g_warning("%s: Writing to this kind of window unsupported.", __func__); } /* Now write the same buffer to the window's echo stream */ @@ -119,7 +116,7 @@ write_buffer_to_stream(strid_t str, gchar *buf, glui32 len) } if(!str->unicode && str->buffer) { - int copycount = min(len, str->buflen - str->mark); + int copycount = MIN(len, str->buflen - str->mark); memmove(str->buffer + str->mark, buf, copycount); str->mark += copycount; } @@ -155,8 +152,7 @@ write_buffer_to_stream(strid_t str, gchar *buf, glui32 len) str->write_count += len; break; default: - g_warning("%s: Writing to this kind of stream unsupported.", - __func__); + g_warning("%s: Writing to this kind of stream unsupported.", __func__); } } @@ -299,10 +295,9 @@ glsi32 glk_get_char_stream(strid_t str) { g_return_val_if_fail(str != NULL, -1); - g_return_val_if_fail(str->file_mode == filemode_Read - || str->file_mode == filemode_ReadWrite, -1); + g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, -1); - switch(str->stream_type) + switch(str->type) { case STREAM_TYPE_MEMORY: if(str->unicode) @@ -354,8 +349,7 @@ glk_get_char_stream(strid_t str) return (ch > 0xFF)? 0x3F : ch; } default: - g_warning("%s: Reading from this kind of stream unsupported.", - __func__); + g_warning("%s: Reading from this kind of stream unsupported.", __func__); return -1; } } @@ -375,19 +369,17 @@ glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len) { g_return_val_if_fail(str != NULL, 0); - g_return_val_if_fail(str->file_mode == filemode_Read - || str->file_mode == filemode_ReadWrite, 0); + g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, 0); g_return_val_if_fail(buf != NULL, 0); - switch(str->stream_type) + switch(str->type) { case STREAM_TYPE_MEMORY: { int copycount = 0; if(str->unicode) { - while(copycount < len && str->ubuffer - && str->mark < str->buflen) + while(copycount < len && str->ubuffer && str->mark < str->buflen) { glui32 ch = str->ubuffer[str->mark++]; buf[copycount++] = (ch > 0xFF)? '?' : (char)ch; @@ -396,7 +388,7 @@ glk_get_buffer_stream(strid_t str, char *buf, glui32 len) else { if(str->buffer) /* if not, copycount stays 0 */ - copycount = min(len, str->buflen - str->mark); + copycount = MIN(len, str->buflen - str->mark); memmove(buf, str->buffer + str->mark, copycount); str->mark += copycount; } @@ -411,14 +403,12 @@ glk_get_buffer_stream(strid_t str, char *buf, glui32 len) { /* Read len characters of 4 bytes each */ unsigned char *readbuffer = g_new0(unsigned char, 4 * len); - size_t count = fread(readbuffer, sizeof(unsigned char), - 4 * len, str->file_pointer); + size_t count = fread(readbuffer, sizeof(unsigned char), 4 * len, str->file_pointer); /* If there was an incomplete character */ if(count % 4 != 0) { count -= count % 4; - g_warning("%s: Incomplete character in binary Unicode " - "file.", __func__); + g_warning("%s: Incomplete character in binary Unicode file.", __func__); } str->read_count += count / 4; @@ -436,8 +426,7 @@ glk_get_buffer_stream(strid_t str, char *buf, glui32 len) } else /* Regular binary file */ { - size_t count = fread(buf, sizeof(char), len, - str->file_pointer); + size_t count = fread(buf, sizeof(char), len, str->file_pointer); str->read_count += count; return count; } @@ -457,8 +446,7 @@ glk_get_buffer_stream(strid_t str, char *buf, glui32 len) return foo; } default: - g_warning("%s: Reading from this kind of stream unsupported.", - __func__); + g_warning("%s: Reading from this kind of stream unsupported.", __func__); return 0; } } @@ -484,11 +472,10 @@ glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len) { g_return_val_if_fail(str != NULL, 0); - g_return_val_if_fail(str->file_mode == filemode_Read - || str->file_mode == filemode_ReadWrite, 0); + g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, 0); g_return_val_if_fail(buf != NULL, 0); - switch(str->stream_type) + switch(str->type) { case STREAM_TYPE_MEMORY: { @@ -496,14 +483,12 @@ glk_get_line_stream(strid_t str, char *buf, glui32 len) if(str->unicode) { /* Do it character-by-character */ - while(copycount < len - 1 && str->ubuffer - && str->mark < str->buflen) + while(copycount < len - 1 && str->ubuffer && str->mark < str->buflen) { glui32 ch = str->ubuffer[str->mark++]; /* Check for Unicode newline; slightly different than in file streams */ - if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 - || ch == 0x2029) + if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 || ch == 0x2029) { buf[copycount++] = '\n'; break; @@ -522,9 +507,8 @@ glk_get_line_stream(strid_t str, char *buf, glui32 len) else { if(str->buffer) /* if not, copycount stays 0 */ - copycount = min(len - 1, str->buflen - str->mark); - char *endptr = memccpy(buf, str->buffer + str->mark, '\n', - copycount); + copycount = MIN(len - 1, str->buflen - str->mark); + char *endptr = memccpy(buf, str->buffer + str->mark, '\n', copycount); if(endptr) /* newline was found */ copycount = endptr - buf; /* Real copy count */ buf[copycount] = '\0'; @@ -543,8 +527,7 @@ glk_get_line_stream(strid_t str, char *buf, glui32 len) int foo; for(foo = 0; foo < len - 1; foo++) { - glsi32 ch = - read_ucs4be_char_from_file(str->file_pointer); + glsi32 ch = read_ucs4be_char_from_file(str->file_pointer); if(ch == -1) { buf[foo] = '\0'; @@ -594,8 +577,7 @@ glk_get_line_stream(strid_t str, char *buf, glui32 len) return foo; } default: - g_warning("%s: Reading from this kind of stream unsupported.", - __func__); + g_warning("%s: Reading from this kind of stream unsupported.", __func__); return 0; } } @@ -631,7 +613,7 @@ glk_stream_get_position(strid_t str) { g_return_val_if_fail(str != NULL, 0); - switch(str->stream_type) + switch(str->type) { case STREAM_TYPE_MEMORY: return str->mark; @@ -683,7 +665,7 @@ glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) g_return_if_fail(!(seekmode == seekmode_Start && pos < 0)); g_return_if_fail(!(seekmode == seekmode_End || pos > 0)); - switch(str->stream_type) + switch(str->type) { case STREAM_TYPE_MEMORY: switch(seekmode) @@ -712,8 +694,7 @@ glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) break; } default: - g_warning("%s: Seeking not supported on this type of stream.", - __func__); + g_warning("%s: Seeking not supported on this type of stream.", __func__); return; } } diff --git a/src/style.c b/src/style.c index c4f51f9..54b8aa2 100644 --- a/src/style.c +++ b/src/style.c @@ -1,5 +1,17 @@ #include "glk.h" +/** + * glk_set_style: + * @val: A style. + * + * Changes the style of the current output stream. @val should be one of + * #style_Normal, #style_Emphasized, #style_Preformatted, #style_Header, + * #style_Subheader, #style_Alert, #style_Note, #style_BlockQuote, #style_Input, + * #style_User1, or #style_User2. However, any value is actually legal; if the + * library does not recognize the style value, it will treat it as + * #style_Normal. (This policy allows for the future definition of styles + * without breaking old Glk libraries.) + */ void glk_set_style(glui32 val) { diff --git a/src/window.c b/src/window.c index c8d793c..db0af1a 100644 --- a/src/window.c +++ b/src/window.c @@ -5,19 +5,19 @@ static GNode *root_window = NULL; /** * glk_window_iterate: - * @win: A window, or #NULL. - * @rockptr: Return location for the next window's rock, or #NULL. + * @win: A window, or %NULL. + * @rockptr: Return location for the next window's rock, or %NULL. * - * Iterates over the list of windows; if @win is #NULL, it returns the first + * Iterates over the list of windows; if @win is %NULL, it returns the first * window, otherwise the next window after @win. If there are no more, it * returns #NULL. The window's rock is stored in @rockptr. If you don't want - * the rocks to be returned, you may set @rockptr to #NULL. + * the rocks to be returned, you may set @rockptr to %NULL. * * The order in which windows are returned is arbitrary. The root window is * not necessarily first, nor is it necessarily last. The order may change * every time you create or destroy a window, invalidating the iteration. * - * Returns: the next window, or #NULL if there are no more. + * Returns: the next window, or %NULL if there are no more. */ winid_t glk_window_iterate(winid_t win, glui32 *rockptr) @@ -54,7 +54,8 @@ glk_window_iterate(winid_t win, glui32 *rockptr) * glk_window_get_rock: * @win: A window. * - * Returns the window @win's rock value. Pair windows always have rock 0. + * Returns @win's rock value. Pair windows always have rock 0; all other windows + * have the rock value you created them with. * * Returns: A rock value. */ @@ -69,7 +70,7 @@ glk_window_get_rock(winid_t win) * glk_window_get_type: * @win: A window. * - * Returns the window @win's type, one of #wintype_Blank, #wintype_Pair, + * Returns @win's type, one of #wintype_Blank, #wintype_Pair, * #wintype_TextBuffer, #wintype_TextGrid, or #wintype_Graphics. * * Returns: The window's type. @@ -78,7 +79,7 @@ glui32 glk_window_get_type(winid_t win) { g_return_val_if_fail(win != NULL, 0); - return win->window_type; + return win->type; } /** @@ -86,7 +87,7 @@ glk_window_get_type(winid_t win) * @win: A window. * * Returns the window @win's parent window. If @win is the root window, this - * returns #NULL, since the root window has no parent. Remember that the parent + * returns %NULL, since the root window has no parent. Remember that the parent * of every window is a pair window; other window types are always childless. * * Returns: A window. @@ -104,9 +105,9 @@ glk_window_get_parent(winid_t win) * @win: A window. * * Returns the other child of the window @win's parent. If @win is the - * root window, this returns #NULL. + * root window, this returns %NULL. * - * Returns: A window, or NULL. + * Returns: A window, or %NULL. */ winid_t glk_window_get_sibling(winid_t win) @@ -176,11 +177,11 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, return NULL; } /* We only create one window and don't support any more than that */ - winid_t new_window = g_new0(struct glk_window_struct, 1); - root_window = g_node_new(new_window); + winid_t win = g_new0(struct glk_window_struct, 1); + root_window = g_node_new(win); - new_window->rock = rock; - new_window->window_type = wintype; + win->rock = rock; + win->type = wintype; gdk_threads_enter(); @@ -197,74 +198,87 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, case wintype_Blank: { /* A blank window will be a label without any text */ - GtkWidget *window = gtk_label_new(""); - gtk_box_pack_end(vbox, window, TRUE, TRUE, 0); - gtk_widget_show(window); + GtkWidget *label = gtk_label_new(""); + gtk_box_pack_end(vbox, label, TRUE, TRUE, 0); + gtk_widget_show(label); - new_window->widget = window; + win->widget = label; /* You can print to a blank window's stream, but it does nothing */ - new_window->window_stream = window_stream_new(new_window); - new_window->echo_stream = NULL; + win->window_stream = window_stream_new(win); + win->echo_stream = NULL; } break; case wintype_TextBuffer: { - GtkWidget *scroll_window = gtk_scrolled_window_new(NULL, NULL); - GtkWidget *window = gtk_text_view_new(); - GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(window) ); + GtkWidget *scrolledwindow = gtk_scrolled_window_new(NULL, NULL); + GtkWidget *textview = gtk_text_view_new(); + GtkTextBuffer *textbuffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(textview) ); - gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(window), GTK_WRAP_WORD_CHAR ); - gtk_text_view_set_editable( GTK_TEXT_VIEW(window), FALSE ); + gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR ); + gtk_text_view_set_editable( GTK_TEXT_VIEW(textview), FALSE ); - gtk_container_add( GTK_CONTAINER(scroll_window), window ); - gtk_box_pack_end(vbox, scroll_window, TRUE, TRUE, 0); - gtk_widget_show_all(scroll_window); + gtk_container_add( GTK_CONTAINER(scrolledwindow), textview ); + gtk_box_pack_end(vbox, scrolledwindow, TRUE, TRUE, 0); + gtk_widget_show_all(scrolledwindow); - new_window->widget = window; - new_window->window_stream = window_stream_new(new_window); - new_window->echo_stream = NULL; - new_window->input_request_type = INPUT_REQUEST_NONE; - new_window->line_input_buffer = NULL; - new_window->line_input_buffer_unicode = NULL; + win->widget = textview; + win->window_stream = window_stream_new(win); + win->echo_stream = NULL; + win->input_request_type = INPUT_REQUEST_NONE; + win->line_input_buffer = NULL; + win->line_input_buffer_unicode = NULL; /* Connect signal handlers */ - new_window->keypress_handler = g_signal_connect( G_OBJECT(window), "key-press-event", G_CALLBACK(on_window_key_press_event), new_window ); - g_signal_handler_block( G_OBJECT(window), new_window->keypress_handler ); + win->keypress_handler = g_signal_connect( G_OBJECT(textview), "key-press-event", G_CALLBACK(on_window_key_press_event), win ); + g_signal_handler_block( G_OBJECT(textview), win->keypress_handler ); - new_window->insert_text_handler = g_signal_connect_after( G_OBJECT(buffer), "insert-text", G_CALLBACK(after_window_insert_text), new_window ); - g_signal_handler_block( G_OBJECT(buffer), new_window->insert_text_handler ); + win->insert_text_handler = g_signal_connect_after( G_OBJECT(textbuffer), "insert-text", G_CALLBACK(after_window_insert_text), win ); + g_signal_handler_block( G_OBJECT(textbuffer), win->insert_text_handler ); - /* Create an editable tag to indicate editable parts of the window (for line input) */ - gtk_text_buffer_create_tag(buffer, "uneditable", "editable", FALSE, "editable-set", TRUE, NULL); + /* Create an editable tag to indicate uneditable parts of the window + (for line input) */ + gtk_text_buffer_create_tag(textbuffer, "uneditable", "editable", FALSE, "editable-set", TRUE, NULL); /* Mark the position where the user will input text */ - GtkTextIter end_iter; - gtk_text_buffer_get_end_iter(buffer, &end_iter); - gtk_text_buffer_create_mark(buffer, "input_position", &end_iter, TRUE); + GtkTextIter end; + gtk_text_buffer_get_end_iter(textbuffer, &end); + gtk_text_buffer_create_mark(textbuffer, "input_position", &end, TRUE); } break; default: - g_warning("glk_window_open: unsupported window type"); - g_free(new_window); + g_warning("%s: unsupported window type", __func__); + g_free(win); gdk_threads_leave(); return NULL; } - new_window->window_node = root_window; + win->window_node = root_window; gdk_threads_leave(); - return new_window; + return win; } +/** + * glk_window_close: + * @win: Window to close. + * @result: Pointer to a #stream_result_t in which to store the write count. + * + * Closes @win, which is pretty much exactly the opposite of opening a window. + * It is legal to close all your windows, or to close the root window (which is + * the same thing.) + * + * The @result argument is filled with the output character count of the window + * stream. + */ void glk_window_close(winid_t win, stream_result_t *result) { g_return_if_fail(win != NULL); - switch(win->window_type) + switch(win->type) { case wintype_TextBuffer: gtk_widget_destroy( gtk_widget_get_parent(win->widget) ); @@ -288,14 +302,36 @@ glk_window_close(winid_t win, stream_result_t *result) * glk_window_clear: * @win: A window. * - * Erases the window @win. + * Erases the window @win. The meaning of this depends on the window type. + * + * + * + * Text buffer: This may do any number of things, such as delete all text in + * the window, or print enough blank lines to scroll all text beyond + * visibility, or insert a page-break marker which is treated specially by the + * display part of the library. + * + * + * Text grid: This will clear the window, filling all positions with blanks. + * The window cursor is moved to the top left corner (position 0,0). + * + * + * Graphics: Clears the entire window to its current background color. + * + * + * Other window types: No effect. + * + * + * + * It is illegal to erase a window which has line input pending. */ void glk_window_clear(winid_t win) { g_return_if_fail(win != NULL); + g_return_if_fail(win->input_request_type != INPUT_REQUEST_LINE && win->input_request_type != INPUT_REQUEST_LINE_UNICODE); - switch(win->window_type) + switch(win->type) { case wintype_Blank: /* do nothing */ @@ -306,8 +342,7 @@ glk_window_clear(winid_t win) { gdk_threads_enter(); - GtkTextBuffer *buffer = - gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); + GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); GtkTextIter start, end; gtk_text_buffer_get_bounds(buffer, &start, &end); gtk_text_buffer_delete(buffer, &start, &end); @@ -325,7 +360,10 @@ glk_window_clear(winid_t win) * glk_set_window: * @win: A window. * - * Sets the current stream to @win's window stream. + * Sets the current stream to @win's window stream. It is exactly equivalent to + * + * glk_stream_set_current(glk_window_get_stream(win)) + * */ void glk_set_window(winid_t win) @@ -337,7 +375,9 @@ glk_set_window(winid_t win) * glk_window_get_stream: * @win: A window. * - * Gets the stream associated with @win. + * Returns the stream which is associated with @win. Every window has a stream + * which can be printed to, but this may not be useful, depending on the window + * type. (For example, printing to a blank window's stream has no effect.) * * Returns: The window stream. */ @@ -350,7 +390,7 @@ strid_t glk_window_get_stream(winid_t win) /** * glk_window_set_echo_stream: * @win: A window. - * @str: A stream to attach to the window, or #NULL. + * @str: A stream to attach to the window, or %NULL. * * Attaches the stream @str to @win as a second stream. Any text printed to the * window is also echoed to this second stream, which is called the window's @@ -368,7 +408,7 @@ strid_t glk_window_get_stream(winid_t win) * which would create an infinite loop. It is similarly illegal to create a * longer loop (two or more windows echoing to each other.) * - * You can reset a window to stop echoing by setting @str to #NULL. + * You can reset a window to stop echoing by setting @str to %NULL. */ void glk_window_set_echo_stream(winid_t win, strid_t str) @@ -376,20 +416,18 @@ glk_window_set_echo_stream(winid_t win, strid_t str) g_return_if_fail(win != NULL); /* Test for an infinite loop */ - strid_t next_str; - for(next_str = str; - next_str != NULL && next_str->stream_type == STREAM_TYPE_WINDOW; - next_str = next_str->window->echo_stream) + strid_t next = str; + for(; next && next->type == STREAM_TYPE_WINDOW; next = next->window->echo_stream) { - if(next_str == win->window_stream) + if(next == win->window_stream) { - g_warning("glk_window_set_echo_stream: Infinite loop detected"); + g_warning("%s: Infinite loop detected", __func__); win->echo_stream = NULL; return; } } - win->echo_stream = str; + win->echo_stream = str; } /** @@ -397,9 +435,9 @@ glk_window_set_echo_stream(winid_t win, strid_t str) * @win: A window. * * Returns the echo stream of window @win. If the window has no echo stream (as - * is initially the case) then this returns #NULL. + * is initially the case) then this returns %NULL. * - * Returns: A stream, or #NULL. + * Returns: A stream, or %NULL. */ strid_t glk_window_get_echo_stream(winid_t win) @@ -408,11 +446,25 @@ glk_window_get_echo_stream(winid_t win) return win->echo_stream; } +/** + * glk_window_get_size: + * @win: A window. + * @widthptr: Pointer to a location to store the window's width, or %NULL. + * @heightptr: Pointer to a location to store the window's height, or %NULL. + * + * Simply returns the actual size of the window, in its measurement system. + * Either @widthptr or @heightptr can be %NULL, if you only want one + * measurement. (Or, in fact, both, if you want to waste time.) + */ void glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) { g_return_if_fail(win != NULL); + /* TODO: Write this function */ + /* For a text buffer window: Return the number of rows and columns which + would be available _if_ the window was filled with "0" (zero) characters in + the "normal" font. */ if(widthptr != NULL) { *widthptr = 0; } @@ -422,8 +474,40 @@ glk_window_get_size(winid_t win, glui32 *widthptr, glui32 *heightptr) } } +/** + * glk_window_move_cursor: + * @win: A text grid window. + * @xpos: Horizontal cursor position. + * @ypos: Vertical cursor position. + * + * Sets the cursor position. If you move the cursor right past the end of a + * line, it wraps; the next character which is printed will appear at the + * beginning of the next line. + * + * If you move the cursor below the last line, or when the cursor reaches the + * end of the last line, it goes "off the screen" and further output has no + * effect. You must call glk_window_move_cursor() or glk_window_clear() to move + * the cursor back into the visible region. + * + * + * Note that the arguments of glk_window_move_cursor() are unsigned + * ints. This is okay, since there are no negative positions. If you try + * to pass a negative value, Glk will interpret it as a huge positive value, + * and it will wrap or go off the last line. + * + * + * + * Also note that the output cursor is not necessarily visible. In particular, + * when you are requesting line or character input in a grid window, you cannot + * rely on the cursor position to prompt the player where input is indicated. + * You should print some character prompt at that spot -- a ">" character, for + * example. + * + */ void glk_window_move_cursor(winid_t win, glui32 xpos, glui32 ypos) { g_return_if_fail(win != NULL); + g_return_if_fail(win->type == wintype_TextGrid); + /* TODO: write this function */ } diff --git a/src/window.h b/src/window.h index 73583a3..1fb98ce 100644 --- a/src/window.h +++ b/src/window.h @@ -24,7 +24,7 @@ struct glk_window_struct /* Pointer to the node in the global tree that contains this window */ GNode *window_node; /* Window parameters */ - glui32 window_type; + glui32 type; GtkWidget *widget; strid_t window_stream; strid_t echo_stream; -- 2.30.2