From f48bef46bd39ddac5067af1a4ac9b977b6158d07 Mon Sep 17 00:00:00 2001 From: rodin Date: Sat, 14 Nov 2009 15:36:47 +0000 Subject: [PATCH] Implemented output buffering. --- libchimara/Makefile.am | 2 +- libchimara/event.c | 9 ++++++- libchimara/hyperlink.c | 61 +++++++++++++++++++++++++++++++++++++++++- libchimara/input.c | 4 +++ libchimara/input.h | 1 + libchimara/stream.h | 1 + libchimara/strio.c | 57 +++++++++++++++++++++++++++++++-------- libchimara/strio.h | 6 +++++ libchimara/style.c | 37 +++++++++++++++++++++++++ libchimara/style.h | 1 + libchimara/window.c | 14 ++++++++++ libchimara/window.h | 5 ++++ 12 files changed, 184 insertions(+), 14 deletions(-) create mode 100644 libchimara/strio.h diff --git a/libchimara/Makefile.am b/libchimara/Makefile.am index b7a79e3..2024138 100644 --- a/libchimara/Makefile.am +++ b/libchimara/Makefile.am @@ -30,7 +30,7 @@ libchimara_la_SOURCES = \ resource.c resource.h \ schannel.c \ stream.c stream.h \ - strio.c \ + strio.c strio.h \ style.c style.h \ timer.c timer.h \ window.c window.h diff --git a/libchimara/event.c b/libchimara/event.c index 765f664..366c189 100644 --- a/libchimara/event.c +++ b/libchimara/event.c @@ -147,8 +147,15 @@ glk_select(event_t *event) { g_return_if_fail(event != NULL); + /* Flush all window buffers */ + winid_t win; + for(win = glk_window_iterate(NULL, NULL); win != NULL; win = glk_window_iterate(win, NULL)) { + if(win->type == wintype_TextBuffer) + flush_window_buffer(win); + } + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); - + get_appropriate_event(event); /* Check for interrupt */ diff --git a/libchimara/hyperlink.c b/libchimara/hyperlink.c index af8275e..6d0d263 100644 --- a/libchimara/hyperlink.c +++ b/libchimara/hyperlink.c @@ -1,21 +1,80 @@ -#include +#include "hyperlink.h" +#include "chimara-glk-private.h" +extern GPrivate *glk_data_key; + +/** + * glk_set_hyperlink: + * @linkval: Set to nonzero to initiate hyperlink mode. Set to zero to disengage. + * + * Use this function to create hyperlinks in a textbuffer. It sets the current stream + * to hyperlink mode, after which text will become a hyperlink until hyperlink mode + * is turned off. If the current stream does not write to a textbuffer window, this function + * does nothing. + * + * You can request hyperlink events with glk_request_hyperlink_event() to react + * to clicks on the link. + */ void glk_set_hyperlink(glui32 linkval) { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + g_return_if_fail(glk_data->current_stream != NULL); + glk_set_hyperlink_stream(glk_data->current_stream, linkval); } +/** + * glk_set_hyperlink: + * @str: The stream to set the hyperlink mode on. + * @linkval: Set to nonzero to initiate hyperlink mode. Set to zero to disengage. + * + * Use this function to create hyperlinks in a textbuffer. It sets a stream to a textbuffer + * window to hyperlink mode, after which text will become a hyperlink until hyperlink mode + * is turned off. Calling this function on a stream that does not write to a textbuffer does + * nothing. + * + * You can request hyperlink events with glk_request_hyperlink_event() to react + * to clicks on the link. + */ void glk_set_hyperlink_stream(strid_t str, glui32 linkval) { + g_return_if_fail(str != NULL); + g_return_if_fail(str->window != NULL); + g_return_if_fail(str->window->type == wintype_TextBuffer); + + str->hyperlink_mode = (linkval != 0); } void glk_request_hyperlink_event(winid_t win) { + VALID_WINDOW(win, return); + g_return_if_fail(win != NULL); + g_return_if_fail(win->mouse_click_handler != 0); + g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid); + + g_signal_handler_unblock( G_OBJECT(win->widget), win->mouse_click_handler ); } void glk_cancel_hyperlink_event(winid_t win) { + VALID_WINDOW(win, return); + g_return_if_fail(win != NULL); + g_return_if_fail(win->mouse_click_handler != 0); + g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid); + + g_signal_handler_block( G_OBJECT(win->widget), win->mouse_click_handler ); +} + +/* Internal function: General callback for signal button-release-event on a + * text buffer or text grid window. Used for detecting clicks on hyperlinks. + * Blocked when not in use. + */ +gboolean +on_window_button_release_event(GtkWidget *widget, GdkEventButton *event, winid_t win) +{ + printf("Click on (%f,%f)\n", event->x, event->y); + return TRUE; } diff --git a/libchimara/input.c b/libchimara/input.c index 86cbe34..f48aa84 100644 --- a/libchimara/input.c +++ b/libchimara/input.c @@ -16,6 +16,8 @@ request_char_event_common(winid_t win, gboolean unicode) VALID_WINDOW(win, return); g_return_if_fail(win->input_request_type == INPUT_REQUEST_NONE); g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid); + + flush_window_buffer(win); ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); @@ -166,6 +168,8 @@ text_grid_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, static void text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean insert, gchar *inserttext) { + flush_window_buffer(win); + gdk_threads_enter(); GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); diff --git a/libchimara/input.h b/libchimara/input.h index 56fb73c..94cc88c 100644 --- a/libchimara/input.h +++ b/libchimara/input.h @@ -8,6 +8,7 @@ #include "window.h" #include "event.h" +#include "strio.h" G_GNUC_INTERNAL gboolean on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win); G_GNUC_INTERNAL void after_window_insert_text(GtkTextBuffer *textbuffer, GtkTextIter *location, gchar *text, gint len, winid_t win); diff --git a/libchimara/stream.h b/libchimara/stream.h index 9636fac..1e5a720 100644 --- a/libchimara/stream.h +++ b/libchimara/stream.h @@ -49,6 +49,7 @@ struct glk_stream_struct gchar *filename; /* Displayable filename in UTF-8 for error handling */ gchar *style; /* Name of the current style */ + gboolean hyperlink_mode; /* WHen turned on, text written to the stream will be a hyperlink */ }; G_GNUC_INTERNAL strid_t file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode); diff --git a/libchimara/strio.c b/libchimara/strio.c index f01099a..3309392 100644 --- a/libchimara/strio.c +++ b/libchimara/strio.c @@ -61,29 +61,43 @@ write_utf8_to_grid(winid_t win, gchar *s) gdk_threads_leave(); } + /* Internal function: write a UTF-8 string to a text buffer window's text buffer. */ static void -write_utf8_to_window(winid_t win, gchar *s) +write_utf8_to_window_buffer(winid_t win, gchar *s) { if(win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE) { ILLEGAL("Tried to print to a text buffer window with line input pending."); return; } + + // Write to the buffer + g_string_append(win->buffer, s); +} +/* Internal function: flush a window's text buffer to the screen. */ +void +flush_window_buffer(winid_t win) +{ + if(win->buffer->len == 0) + return; + gdk_threads_enter(); GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); GtkTextIter iter; gtk_text_buffer_get_end_iter(buffer, &iter); - gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, s, -1, win->window_stream->style, NULL); + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, win->buffer->str, -1, win->window_stream->style, NULL); gdk_threads_leave(); ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK)); g_assert(glk); - g_signal_emit_by_name(glk, "text-buffer-output", win->rock, s); + g_signal_emit_by_name(glk, "text-buffer-output", win->rock, win->buffer->str); + + g_string_truncate(win->buffer, 0); } /* Internal function: write a Latin-1 buffer with length to a stream. */ @@ -123,7 +137,7 @@ write_buffer_to_stream(strid_t str, gchar *buf, glui32 len) gchar *utf8 = convert_latin1_to_utf8(buf, len); if(utf8 != NULL) { - write_utf8_to_window(str->window, utf8); + write_utf8_to_window_buffer(str->window, utf8); g_free(utf8); } } @@ -224,7 +238,7 @@ write_buffer_to_stream_uni(strid_t str, glui32 *buf, glui32 len) gchar *utf8 = convert_ucs4_to_utf8(buf, len); if(utf8 != NULL) { - write_utf8_to_window(str->window, utf8); + write_utf8_to_window_buffer(str->window, utf8); g_free(utf8); } } @@ -341,6 +355,9 @@ void glk_put_string_stream(strid_t str, char *s) { VALID_STREAM(str, return); + if(*s == 0) + return; + g_return_if_fail(str->file_mode != filemode_Read); write_buffer_to_stream(str, s, strlen(s)); @@ -359,6 +376,9 @@ void glk_put_string_stream_uni(strid_t str, glui32 *s) { VALID_STREAM(str, return); + if(*s == 0) + return; + g_return_if_fail(str->file_mode != filemode_Read); /* An impromptu strlen() for glui32 arrays */ @@ -383,6 +403,9 @@ void glk_put_buffer_stream(strid_t str, char *buf, glui32 len) { VALID_STREAM(str, return); + if(len == 0) + return; + g_return_if_fail(str->file_mode != filemode_Read); write_buffer_to_stream(str, buf, len); @@ -402,6 +425,9 @@ void glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) { VALID_STREAM(str, return); + if(len == 0) + return; + g_return_if_fail(str->file_mode != filemode_Read); write_buffer_to_stream_uni(str, buf, len); @@ -884,9 +910,14 @@ glk_get_line_stream(strid_t str, char *buf, glui32 len) } else /* Regular binary file */ { - fgets(buf, len, str->file_pointer); - str->read_count += strlen(buf); - return strlen(buf); + if( !fgets(buf, len, str->file_pointer) ) { + *buf = 0; + return 0; + } + + int nread = strlen(buf); + str->read_count += nread; + return nread; } } else /* Text mode is the same for Unicode and regular files */ @@ -1022,10 +1053,14 @@ glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) else /* Regular binary file */ { gchar *readbuffer = g_new0(gchar, len); - fgets(readbuffer, len, str->file_pointer); - glui32 count = strlen(readbuffer) + 1; /* Copy terminator */ + if( !fgets(readbuffer, len, str->file_pointer) ) { + *buf = 0; + return 0; + } + + glui32 count = strlen(readbuffer); int foo; - for(foo = 0; foo < count; foo++) + for(foo = 0; foo < count + 1; foo++) /* Copy terminator */ buf[foo] = (unsigned char)(readbuffer[foo]); str->read_count += count; return count; diff --git a/libchimara/strio.h b/libchimara/strio.h new file mode 100644 index 0000000..57e98f4 --- /dev/null +++ b/libchimara/strio.h @@ -0,0 +1,6 @@ +#ifndef STRIO_H +#define STRIO_H + +G_GNUC_INTERNAL void flush_window_buffer(winid_t win); + +#endif diff --git a/libchimara/style.c b/libchimara/style.c index 0734fd3..cf20f0c 100644 --- a/libchimara/style.c +++ b/libchimara/style.c @@ -67,6 +67,10 @@ get_tag_name(glui32 style) */ void glk_set_style_stream(strid_t str, glui32 styl) { + if(str->window == NULL) + return; + + flush_window_buffer(str->window); str->style = get_tag_name(styl); } @@ -691,6 +695,20 @@ glk_stylehint_set(glui32 wintype, glui32 styl, glui32 hint, glsi32 val) } } +/** + * glk_stylehint_clear: + * @wintype: The window type to set a style hint on, or %wintype_AllTypes. + * @styl: The style to set a hint for. + * @hint: The type of style hint, one of the stylehint_ constants. + * + * Resets a hint about the appearance of one style for a particular type of + * window to it's default value. You can also set wintype to %wintype_AllTypes, which resets a hint for + * all types of window. + * + * There is no equivalent constant to reset a hint for all styles of a single + * window type. + * + */ void glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint) { @@ -710,12 +728,31 @@ glk_stylehint_clear(glui32 wintype, glui32 styl, glui32 hint) } } +/** + * glk_style_distinguish: + * @win: The window in which the styles are to be distinguished. + * @styl1: The first style to be distinguished from the second style. + * @styl2: The second styel to be distinguished from the first style. + * + * Returns: TRUE if the two styles are visually distinguishable in the given window. + * If they are not, it returns FALSE. + */ glui32 glk_style_distinguish(winid_t win, glui32 styl1, glui32 styl2) { return styl1 != styl2; } +/** + * glk_style_measure: + * @win: The window from which to take the style. + * @styl: The style to perform the measurement on. + * @hint: The stylehint to measure. + * @result: Address to write the result to. + * + * This function can be used to query the current value of a particular style hint. + * Returns: TRUE upon successul retrievel, otherwise FALSE. + */ glui32 glk_style_measure(winid_t win, glui32 styl, glui32 hint, glui32 *result) { diff --git a/libchimara/style.h b/libchimara/style.h index 7dc507f..7b56760 100644 --- a/libchimara/style.h +++ b/libchimara/style.h @@ -9,6 +9,7 @@ #include "magic.h" #include "chimara-glk-private.h" #include "stream.h" +#include "strio.h" G_GNUC_INTERNAL void style_init_textbuffer(GtkTextBuffer *buffer); G_GNUC_INTERNAL void style_init_textgrid(GtkTextBuffer *buffer); diff --git a/libchimara/window.c b/libchimara/window.c index b3995f0..a31bed1 100644 --- a/libchimara/window.c +++ b/libchimara/window.c @@ -31,6 +31,9 @@ window_new_common(glui32 rock) win->line_input_buffer = NULL; win->line_input_buffer_unicode = NULL; + /* Initialise the buffer */ + win->buffer = g_string_sized_new(1024); + return win; } @@ -48,6 +51,8 @@ window_close_common(winid_t win, gboolean destroy_node) if(destroy_node) g_node_destroy(win->window_node); win->magic = MAGIC_FREE; + + g_string_free(win->buffer, TRUE); g_free(win); } @@ -482,6 +487,10 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, 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 ); + gtk_widget_add_events( GTK_WIDGET(textview), GDK_BUTTON_RELEASE_MASK ); + win->mouse_click_handler = g_signal_connect_after( G_OBJECT(textview), "button-release-event", G_CALLBACK(on_window_button_release_event), win ); + g_signal_handler_block( G_OBJECT(textview), win->mouse_click_handler ); + /* Create the styles available to the window stream */ style_init_textgrid(textbuffer); } @@ -520,9 +529,14 @@ glk_window_open(winid_t split, glui32 method, glui32 size, glui32 wintype, 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 ); + gtk_widget_add_events( GTK_WIDGET(textview), GDK_BUTTON_RELEASE_MASK ); + win->mouse_click_handler = g_signal_connect_after( G_OBJECT(textview), "button-release-event", G_CALLBACK(on_window_button_release_event), win ); + g_signal_handler_block( G_OBJECT(textview), win->mouse_click_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 uneditable parts of the window (for line input) */ gtk_text_buffer_create_tag(textbuffer, "uneditable", "editable", FALSE, "editable-set", TRUE, NULL); diff --git a/libchimara/window.h b/libchimara/window.h index fd61ca3..71bbb74 100644 --- a/libchimara/window.h +++ b/libchimara/window.h @@ -7,6 +7,7 @@ #include "stream.h" #include "input.h" #include "style.h" +#include "hyperlink.h" enum InputRequestType @@ -65,6 +66,10 @@ struct glk_window_struct /* Signal handlers */ gulong keypress_handler; gulong insert_text_handler; + gulong mouse_click_handler; + gulong mouse_move_handler; + /* Window buffer */ + GString *buffer; }; #endif -- 2.30.2