From 2090f5576f520435b22417c3246ad2286d95a391 Mon Sep 17 00:00:00 2001 From: fliep Date: Sun, 25 Oct 2009 22:09:33 +0000 Subject: [PATCH] - Feeding input programmatically to the Glk program now works: chimara_glk_feed_line_input() and chimara_glk_feed_char_input(). - Various other small fixes. --- docs/reference/chimara-sections.txt | 2 + libchimara/abort.c | 30 ++++- libchimara/abort.h | 5 +- libchimara/chimara-glk-private.h | 3 + libchimara/chimara-glk.c | 56 +++++++- libchimara/chimara-glk.h | 2 + libchimara/event.c | 102 +++++++++++--- libchimara/event.h | 2 + libchimara/glk.c | 15 +-- libchimara/input.c | 200 +++++++++++++++++++--------- libchimara/input.h | 3 + tests/Makefile.am | 2 +- tests/callbacks.c | 26 +++- tests/callbacks.h | 37 ----- tests/chimara.menus | 8 ++ tests/chimara.ui | 16 +++ tests/main.c | 11 +- tests/style.css | 2 + 18 files changed, 375 insertions(+), 147 deletions(-) delete mode 100644 tests/callbacks.h diff --git a/docs/reference/chimara-sections.txt b/docs/reference/chimara-sections.txt index 51b9c62..1170c7d 100644 --- a/docs/reference/chimara-sections.txt +++ b/docs/reference/chimara-sections.txt @@ -22,6 +22,8 @@ chimara_glk_run chimara_glk_stop chimara_glk_wait chimara_glk_get_running +chimara_glk_feed_char_input +chimara_glk_feed_line_input CHIMARA_GLK CHIMARA_IS_GLK diff --git a/libchimara/abort.c b/libchimara/abort.c index 18b2725..aa8ed73 100644 --- a/libchimara/abort.c +++ b/libchimara/abort.c @@ -1,3 +1,4 @@ +#include "abort.h" #include "event.h" #include #include @@ -36,18 +37,18 @@ glk_set_interrupt_handler(void (*func)(void)) /* Internal function: abort this Glk program, freeing resources and calling the user's interrupt handler. */ static void -abort_glk() +abort_glk(void) { ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); if(glk_data->interrupt_handler) (*(glk_data->interrupt_handler))(); - g_signal_emit_by_name(glk_data->self, "stopped"); + shutdown_glk(); g_thread_exit(NULL); } /* Internal function: check if the Glk program has been interrupted. */ void -check_for_abort() +check_for_abort(void) { ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); g_mutex_lock(glk_data->abort_lock); @@ -58,3 +59,26 @@ check_for_abort() } g_mutex_unlock(glk_data->abort_lock); } + +/* Internal function: do any cleanup for shutting down the Glk library. */ +void +shutdown_glk(void) +{ + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + + if(!glk_data->in_startup) + g_signal_emit_by_name(glk_data->self, "stopped"); + + /* Stop any timers */ + glk_request_timer_events(0); + + /* Close any open resource files */ + if(glk_data->resource_map != NULL) { + giblorb_destroy_map(glk_data->resource_map); + glk_stream_close(glk_data->resource_file, NULL); + } + + /* Unref the input queues */ + g_async_queue_unref(glk_data->char_input_queue); + g_async_queue_unref(glk_data->line_input_queue); +} diff --git a/libchimara/abort.h b/libchimara/abort.h index f075550..e6ac904 100644 --- a/libchimara/abort.h +++ b/libchimara/abort.h @@ -1,7 +1,10 @@ #ifndef ABORT_H #define ABORT_H -G_GNUC_INTERNAL void check_for_abort(); +#include + +G_GNUC_INTERNAL void check_for_abort(void); +G_GNUC_INTERNAL void shutdown_glk(void); #endif diff --git a/libchimara/chimara-glk-private.h b/libchimara/chimara-glk-private.h index 1ce36c3..3b1460a 100644 --- a/libchimara/chimara-glk-private.h +++ b/libchimara/chimara-glk-private.h @@ -55,6 +55,9 @@ struct _ChimaraGlkPrivate { GCond *rearranged; gboolean needs_rearrange; gboolean ignore_next_arrange_event; + /* Input queues */ + GAsyncQueue *char_input_queue; + GAsyncQueue *line_input_queue; /* *** Glk library data *** */ /* User-defined interrupt handler */ diff --git a/libchimara/chimara-glk.c b/libchimara/chimara-glk.c index d8f071d..c21e9cc 100644 --- a/libchimara/chimara-glk.c +++ b/libchimara/chimara-glk.c @@ -99,7 +99,10 @@ chimara_glk_init(ChimaraGlk *self) priv->rearranged = g_cond_new(); priv->needs_rearrange = FALSE; priv->ignore_next_arrange_event = FALSE; - priv->interrupt_handler = NULL; + priv->char_input_queue = g_async_queue_new(); + priv->line_input_queue = g_async_queue_new(); + /* Should be g_async_queue_new_full(g_free); but only in GTK >= 2.16 */ + priv->interrupt_handler = NULL; priv->root_window = NULL; priv->fileref_list = NULL; priv->current_stream = NULL; @@ -193,6 +196,10 @@ chimara_glk_finalize(GObject *object) g_mutex_free(priv->arrange_lock); priv->arrange_lock = NULL; + /* Unref input queues */ + g_async_queue_unref(priv->char_input_queue); + g_async_queue_unref(priv->line_input_queue); + /* Free private data */ pango_font_description_free(priv->default_font_desc); pango_font_description_free(priv->monospace_font_desc); @@ -1044,6 +1051,9 @@ glk_enter(struct StartupData *startup) extern GPrivate *glk_data_key; g_private_set(glk_data_key, startup->glk_data); + g_async_queue_ref(startup->glk_data->char_input_queue); + g_async_queue_ref(startup->glk_data->line_input_queue); + /* Run startup function */ if(startup->glkunix_startup_code) { startup->glk_data->in_startup = TRUE; @@ -1194,3 +1204,47 @@ chimara_glk_get_running(ChimaraGlk *glk) CHIMARA_GLK_USE_PRIVATE(glk, priv); return priv->running; } + +/** + * chimara_glk_feed_char_input: + * @glk: a #ChimaraGlk widget + * @keyval: a key symbol as defined in gdk/gdkkeysyms.h + * + * Pretend that a key was pressed in the Glk program as a response to a + * character input request. You can call this function even when no window has + * requested character input, in which case the key will be saved for the + * following window that requests character input. This has the disadvantage + * that if more than one window has requested character input, it is arbitrary + * which one gets the key press. + */ +void +chimara_glk_feed_char_input(ChimaraGlk *glk, guint keyval) +{ + g_return_if_fail(glk || CHIMARA_IS_GLK(glk)); + CHIMARA_GLK_USE_PRIVATE(glk, priv); + g_async_queue_push(priv->char_input_queue, GUINT_TO_POINTER(keyval)); + event_throw(glk, evtype_ForcedCharInput, NULL, 0, 0); +} + +/** + * chimara_glk_feed_line_input: + * @glk: a #ChimaraGlk widget + * @text: text to pass to the next line input request + * + * Pretend that @text was typed in the Glk program as a response to a line input + * request. @text does not need to end with a newline. You can call this + * function even when no window has requested line input, in which case the text + * will be saved for the following window that requests line input. This has the + * disadvantage that if more than one window has requested character input, it + * is arbitrary which one gets the text. + */ +void +chimara_glk_feed_line_input(ChimaraGlk *glk, const gchar *text) +{ + g_return_if_fail(glk || CHIMARA_IS_GLK(glk)); + g_return_if_fail(text); + CHIMARA_GLK_USE_PRIVATE(glk, priv); + g_async_queue_push(priv->line_input_queue, g_strdup(text)); + event_throw(glk, evtype_ForcedLineInput, NULL, 0, 0); +} diff --git a/libchimara/chimara-glk.h b/libchimara/chimara-glk.h index b8ec38c..dfa669e 100644 --- a/libchimara/chimara-glk.h +++ b/libchimara/chimara-glk.h @@ -94,6 +94,8 @@ gboolean chimara_glk_run(ChimaraGlk *glk, const gchar *plugin, int argc, char *a void chimara_glk_stop(ChimaraGlk *glk); void chimara_glk_wait(ChimaraGlk *glk); gboolean chimara_glk_get_running(ChimaraGlk *glk); +void chimara_glk_feed_char_input(ChimaraGlk *glk, guint32 keycode); +void chimara_glk_feed_line_input(ChimaraGlk *glk, const gchar *text); G_END_DECLS diff --git a/libchimara/event.c b/libchimara/event.c index 88b09f0..1bcdcd9 100644 --- a/libchimara/event.c +++ b/libchimara/event.c @@ -2,6 +2,7 @@ #include "magic.h" #include "glk.h" #include "window.h" +#include "input.h" #include #include "chimara-glk.h" @@ -50,6 +51,84 @@ event_throw(ChimaraGlk *glk, glui32 type, winid_t win, glui32 val1, glui32 val2) g_mutex_unlock(priv->event_lock); } +/* Helper function: Wait for an event in the event queue. If it is a forced + * input event, but no windows have an input request of that type, then wait + * for the next event and put the forced input event back on top of the queue. + */ +static void +get_appropriate_event(event_t *event) +{ + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + + g_mutex_lock(glk_data->event_lock); + + event_t *retrieved_event = NULL; + + /* Wait for an event */ + if( g_queue_is_empty(glk_data->event_queue) ) + g_cond_wait(glk_data->event_queue_not_empty, glk_data->event_lock); + + retrieved_event = g_queue_pop_tail(glk_data->event_queue); + + /* Signal that the event queue is no longer full */ + g_cond_signal(glk_data->event_queue_not_full); + + g_mutex_unlock(glk_data->event_lock); + + if(retrieved_event->type == evtype_ForcedCharInput) + { + /* Check for forced character input in the queue */ + winid_t win; + for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) + if(win->input_request_type == INPUT_REQUEST_CHARACTER || win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE) + break; + if(win) + { + force_char_input_from_queue(win, event); + g_free(retrieved_event); + } + else + { + get_appropriate_event(event); + g_mutex_lock(glk_data->event_lock); + g_queue_push_tail(glk_data->event_queue, retrieved_event); + g_cond_signal(glk_data->event_queue_not_empty); + g_mutex_unlock(glk_data->event_lock); + } + } + else if(retrieved_event->type == evtype_ForcedLineInput) + { + /* Check for forced line input in the queue */ + winid_t win; + for(win = glk_window_iterate(NULL, NULL); win; win = glk_window_iterate(win, NULL)) + if(win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE) + break; + if(win) + { + force_line_input_from_queue(win, event); + g_free(retrieved_event); + } + else + { + get_appropriate_event(event); + g_mutex_lock(glk_data->event_lock); + g_queue_push_tail(glk_data->event_queue, retrieved_event); + g_cond_signal(glk_data->event_queue_not_empty); + g_mutex_unlock(glk_data->event_lock); + } + } + else + { + if(retrieved_event == NULL) + { + WARNING("Retrieved NULL event from non-empty event queue"); + return; + } + memcpy(event, retrieved_event, sizeof(event_t)); + g_free(retrieved_event); + } +} + /** * glk_select: * @event: Pointer to an #event_t. @@ -73,27 +152,8 @@ glk_select(event_t *event) /* Emit the "waiting" signal to let listeners know we are ready for input */ g_signal_emit_by_name(glk_data->self, "waiting"); - g_mutex_lock(glk_data->event_lock); + get_appropriate_event(event); - /* Wait for an event */ - if( g_queue_is_empty(glk_data->event_queue) ) - g_cond_wait(glk_data->event_queue_not_empty, glk_data->event_lock); - - event_t *retrieved_event = g_queue_pop_tail(glk_data->event_queue); - if(retrieved_event == NULL) - { - g_mutex_unlock(glk_data->event_lock); - WARNING("Retrieved NULL event from non-empty event queue"); - return; - } - memcpy(event, retrieved_event, sizeof(event_t)); - g_free(retrieved_event); - - /* Signal that the event queue is no longer full */ - g_cond_signal(glk_data->event_queue_not_full); - - g_mutex_unlock(glk_data->event_lock); - /* Check for interrupt */ glk_tick(); @@ -128,7 +188,7 @@ glk_select(event_t *event) * intended for you to test conditions which may have occurred while you are * computing, and not interfacing with the player. For example, time may pass * during slow computations; you can use glk_select_poll() to see if a - * %evtype_Timer event has occured. (See Timer Events.) * * At the moment, glk_select_poll() checks for %evtype_Timer, %evtype_Arrange, diff --git a/libchimara/event.h b/libchimara/event.h index 32e5bb9..946bc15 100644 --- a/libchimara/event.h +++ b/libchimara/event.h @@ -7,6 +7,8 @@ #define EVENT_QUEUE_MAX_LENGTH (100) #define evtype_Abort (-1) +#define evtype_ForcedCharInput (-2) +#define evtype_ForcedLineInput (-3) G_GNUC_INTERNAL void event_throw(ChimaraGlk *glk, glui32 type, winid_t win, glui32 val1, glui32 val2); diff --git a/libchimara/glk.c b/libchimara/glk.c index 27e3007..4304aef 100644 --- a/libchimara/glk.c +++ b/libchimara/glk.c @@ -42,20 +42,7 @@ G_GNUC_INTERNAL GPrivate *glk_data_key = NULL; void glk_exit(void) { - ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); - - if(!glk_data->in_startup) - g_signal_emit_by_name(glk_data->self, "stopped"); - - /* Stop any timers */ - glk_request_timer_events(0); - - /* Close any open resource files */ - if(glk_data->resource_map != NULL) { - giblorb_destroy_map(glk_data->resource_map); - glk_stream_close(glk_data->resource_file, NULL); - } - + shutdown_glk(); g_thread_exit(NULL); } diff --git a/libchimara/input.c b/libchimara/input.c index 80455ec..4aca9d9 100644 --- a/libchimara/input.c +++ b/libchimara/input.c @@ -219,7 +219,7 @@ text_buffer_request_line_event_common(winid_t win, glui32 maxlen, gboolean inser * it is illegal to change the contents of the buffer yourself. */ void -glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen) +glk_request_line_event(winid_t win, char *buf, glui32 maxlen, glui32 initlen) { VALID_WINDOW(win, return); g_return_if_fail(buf); @@ -227,8 +227,9 @@ glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen) g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid); g_return_if_fail(initlen <= maxlen); - /* Register the buffer */ ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + + /* Register the buffer */ if(glk_data->register_arr) win->buffer_rock = (*glk_data->register_arr)(buf, maxlen, "&+#!Cn"); @@ -264,8 +265,8 @@ glk_request_line_event(winid_t win, char* buf, glui32 maxlen, glui32 initlen) * 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. + * Unicode Standard Annex + * #15 for the details. */ void glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initlen) @@ -276,8 +277,9 @@ glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initl g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid); g_return_if_fail(initlen <= maxlen); - /* Register the buffer */ ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + + /* Register the buffer */ if(glk_data->register_arr) win->buffer_rock = (*glk_data->register_arr)(buf, maxlen, "&+#!Iu"); @@ -314,9 +316,9 @@ glk_request_line_event_uni(winid_t win, glui32 *buf, glui32 maxlen, glui32 initl * This cancels a pending request for line input. (Either Latin-1 or Unicode.) * * The event pointed to by the event argument will be filled in as if the - * player had hit enter, and the input composed so far will be stored in the - * buffer; see below. If you do not care about this information, pass %NULL as - * the @event argument. (The buffer will still be filled.) + * player had hit enter, and the input composed so far will be + * stored in the buffer; see below. If you do not care about this information, + * pass %NULL as the @event argument. (The buffer will still be filled.) * * For convenience, it is legal to call glk_cancel_line_event() even if there * is no line input request on that window. The event type will be set to @@ -393,58 +395,7 @@ on_window_key_press_event(GtkWidget *widget, GdkEventKey *event, winid_t win) win->input_request_type != INPUT_REQUEST_CHARACTER_UNICODE) return FALSE; - int keycode; - - switch(event->keyval) { - case GDK_Up: - case GDK_KP_Up: keycode = keycode_Up; break; - case GDK_Down: - case GDK_KP_Down: keycode = keycode_Down; break; - case GDK_Left: - 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: - 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: - case GDK_KP_Page_Down: keycode = keycode_PageDown; break; - case GDK_Home: - case GDK_KP_Home: keycode = keycode_Home; break; - case GDK_End: - case GDK_KP_End: keycode = keycode_End; 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; - case GDK_F8: keycode = keycode_Func8; break; - case GDK_F9: keycode = keycode_Func9; break; - case GDK_F10: keycode = keycode_Func10; break; - case GDK_F11: keycode = keycode_Func11; break; - 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; - } + glui32 keycode = keyval_to_glk_keycode(event->keyval, win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE); ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(widget, CHIMARA_TYPE_GLK)); g_assert(glk); @@ -613,9 +564,134 @@ in a text grid window. */ void on_input_entry_activate(GtkEntry *input_entry, winid_t win) { - g_signal_handler_block( G_OBJECT(win->widget), win->keypress_handler ); + g_signal_handler_block(win->widget, win->keypress_handler); int chars_written = finish_text_grid_line_input(win, TRUE); - event_throw(CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK)), evtype_LineInput, win, chars_written, 0); + ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK)); + event_throw(glk, evtype_LineInput, win, chars_written, 0); } +glui32 +keyval_to_glk_keycode(guint keyval, gboolean unicode) +{ + glui32 keycode; + switch(keyval) { + case GDK_Up: + case GDK_KP_Up: return keycode_Up; + case GDK_Down: + case GDK_KP_Down: return keycode_Down; + case GDK_Left: + case GDK_KP_Left: return keycode_Left; + case GDK_Right: + case GDK_KP_Right: return keycode_Right; + case GDK_Linefeed: + case GDK_Return: + case GDK_KP_Enter: return keycode_Return; + case GDK_Delete: + case GDK_BackSpace: + case GDK_KP_Delete: return keycode_Delete; + case GDK_Escape: return keycode_Escape; + case GDK_Tab: + case GDK_KP_Tab: return keycode_Tab; + case GDK_Page_Up: + case GDK_KP_Page_Up: return keycode_PageUp; + case GDK_Page_Down: + case GDK_KP_Page_Down: return keycode_PageDown; + case GDK_Home: + case GDK_KP_Home: return keycode_Home; + case GDK_End: + case GDK_KP_End: return keycode_End; + case GDK_F1: + case GDK_KP_F1: return keycode_Func1; + case GDK_F2: + case GDK_KP_F2: return keycode_Func2; + case GDK_F3: + case GDK_KP_F3: return keycode_Func3; + case GDK_F4: + case GDK_KP_F4: return keycode_Func4; + case GDK_F5: return keycode_Func5; + case GDK_F6: return keycode_Func6; + case GDK_F7: return keycode_Func7; + case GDK_F8: return keycode_Func8; + case GDK_F9: return keycode_Func9; + case GDK_F10: return keycode_Func10; + case GDK_F11: return keycode_Func11; + case GDK_F12: return keycode_Func12; + default: + keycode = gdk_keyval_to_unicode(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 || (!unicode && keycode > 255)) + return keycode_Unknown; + return keycode; + } +} + +void +force_char_input_from_queue(winid_t win, event_t *event) +{ + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + guint keyval = GPOINTER_TO_UINT(g_async_queue_pop(glk_data->char_input_queue)); + + glk_cancel_char_event(win); + + gdk_threads_enter(); + ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK)); + g_assert(glk); + g_signal_emit_by_name(glk, "char-input", win->rock, keyval); + gdk_threads_leave(); + + event->type = evtype_CharInput; + event->win = win; + event->val1 = keyval_to_glk_keycode(keyval, win->input_request_type == INPUT_REQUEST_CHARACTER_UNICODE); + event->val2 = 0; +} + +void +force_line_input_from_queue(winid_t win, event_t *event) +{ + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + const gchar *text = g_async_queue_pop(glk_data->line_input_queue); + glui32 chars_written = 0; + + gdk_threads_enter(); + if(win->type == wintype_TextBuffer) + { + GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) ); + GtkTextIter start, end; + + /* Remove signal handlers so the line input doesn't get picked up again */ + g_signal_handler_block(buffer, win->insert_text_handler); + + /* Erase any text that was already typed */ + GtkTextMark *input_position = gtk_text_buffer_get_mark(buffer, "input_position"); + gtk_text_buffer_get_iter_at_mark(buffer, &start, input_position); + gtk_text_buffer_get_end_iter(buffer, &end); + gtk_text_buffer_delete(buffer, &start, &end); + + /* Make the window uneditable again */ + gtk_text_view_set_editable(GTK_TEXT_VIEW(win->widget), FALSE); + + /* Insert the forced input into the window */ + gtk_text_buffer_get_end_iter(buffer, &end); + gchar *text_to_insert = g_strconcat(text, "\n", NULL); + gtk_text_buffer_insert_with_tags_by_name(buffer, &end, text_to_insert, -1, "input", NULL); + chars_written = finish_text_buffer_line_input(win, TRUE); + } + else if(win->type == wintype_TextGrid) + { + /* Remove signal handlers so the line input doesn't get picked up again */ + g_signal_handler_block(win->widget, win->keypress_handler); + + /* Insert the forced input into the window */ + gtk_entry_set_text(GTK_ENTRY(win->input_entry), text); + chars_written = finish_text_grid_line_input(win, TRUE); + } + gdk_threads_leave(); + + event->type = evtype_LineInput; + event->win = win; + event->val1 = chars_written; + event->val2 = 0; +} diff --git a/libchimara/input.h b/libchimara/input.h index e4080fd..56fb73c 100644 --- a/libchimara/input.h +++ b/libchimara/input.h @@ -12,5 +12,8 @@ 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); G_GNUC_INTERNAL void on_input_entry_activate(GtkEntry *input_entry, winid_t win); +G_GNUC_INTERNAL glui32 keyval_to_glk_keycode(guint keyval, gboolean unicode); +G_GNUC_INTERNAL void force_char_input_from_queue(winid_t win, event_t *event); +G_GNUC_INTERNAL void force_line_input_from_queue(winid_t win, event_t *event); #endif diff --git a/tests/Makefile.am b/tests/Makefile.am index 76654ed..d0849b4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -5,7 +5,7 @@ dist_data_DATA = chimara.ui chimara.menus noinst_PROGRAMS = test-chimara test-multisession -test_chimara_SOURCES = main.c callbacks.c callbacks.h error.c error.h +test_chimara_SOURCES = main.c callbacks.c error.c error.h test_chimara_CPPFLAGS = $(AM_CPPFLAGS) \ -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \ -DPACKAGE_SRC_DIR=\""$(srcdir)"\" \ diff --git a/tests/callbacks.c b/tests/callbacks.c index 8c6f83e..e08b532 100644 --- a/tests/callbacks.c +++ b/tests/callbacks.c @@ -30,21 +30,39 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "callbacks.h" +#include +#include #include "error.h" -void on_save(GtkAction *action, gpointer user_data) { +void on_save(GtkAction *action, ChimaraGlk *glk) { GSList *widgets = gtk_action_get_proxies(action); GtkWindow *top = GTK_WINDOW( gtk_widget_get_toplevel(widgets->data) ); error_dialog(top, NULL, "Not implemented yet"); } -gboolean on_window_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) { +gboolean on_window_delete_event(GtkWidget *widget, GdkEvent *event, ChimaraGlk *glk) { gtk_main_quit(); return TRUE; } -void on_quit(GtkAction *action, gpointer user_data) { +void on_quit(GtkAction *action, ChimaraGlk *glk) { gtk_main_quit(); } +void on_hint(GtkAction *action, ChimaraGlk *glk) { + chimara_glk_feed_line_input(glk, "se"); + chimara_glk_feed_line_input(glk, "push cans to window"); + chimara_glk_feed_line_input(glk, "stand on cans"); + chimara_glk_feed_line_input(glk, "open window"); + chimara_glk_feed_line_input(glk, "enter window"); +} + +void on_press_r(GtkAction *action, ChimaraGlk *glk) { + chimara_glk_feed_char_input(glk, GDK_R); +} + +void on_press_enter(GtkAction *action, ChimaraGlk *glk) { + chimara_glk_feed_char_input(glk, GDK_Return); + chimara_glk_feed_char_input(glk, GDK_Return); + chimara_glk_feed_char_input(glk, GDK_Return); +} diff --git a/tests/callbacks.h b/tests/callbacks.h deleted file mode 100644 index e14a667..0000000 --- a/tests/callbacks.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ -/* - * callbacks.h - * Copyright (C) Philip en Marijn 2008 <> - * - * callbacks.h is free software copyrighted by Philip en Marijn. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name ``Philip en Marijn'' nor the name of any other - * contributor may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * callbacks.h IS PROVIDED BY Philip en Marijn ``AS IS'' AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL Philip en Marijn OR ANY OTHER CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include - -void on_save(GtkAction *action, gpointer user_data); -gboolean on_window_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data); -void on_quit(GtkAction *action, gpointer user_data); diff --git a/tests/chimara.menus b/tests/chimara.menus index 4e3e7d4..eba0050 100644 --- a/tests/chimara.menus +++ b/tests/chimara.menus @@ -5,11 +5,19 @@ + + + + + + + + diff --git a/tests/chimara.ui b/tests/chimara.ui index 2d0437d..af6c547 100644 --- a/tests/chimara.ui +++ b/tests/chimara.ui @@ -39,4 +39,20 @@ gtk-quit + + _Hint + Do the first few moves of the game + gtk-dialog-info + + + + Press _R + gtk-open + + + + 3x _Enter + gtk-media-play + + diff --git a/tests/main.c b/tests/main.c index 60891d3..4912739 100644 --- a/tests/main.c +++ b/tests/main.c @@ -40,7 +40,6 @@ #include #include -#include "callbacks.h" #include "error.h" #include #include @@ -66,7 +65,10 @@ on_stopped(ChimaraGlk *glk) static void on_command(ChimaraGlk *glk, gchar *input, gchar *response) { - g_print("Command: %s\nResponse: %s\n", input, response); + gchar *ellipsized = g_strdelimit(g_strndup(response, 20), "\n", ' '); + g_print("%s - %s%s\n", input, ellipsized, + (strlen(ellipsized) < strlen(response))? "..." : ""); + g_free(ellipsized); } static GObject * @@ -102,6 +104,9 @@ create_window(void) "open", "F7", "save", NULL, /* NULL means use stock accelerator */ "quit", NULL, + "hint", "", + "char_input", "", + "char_input2", "", NULL }; const gchar **ptr; @@ -141,7 +146,7 @@ create_window(void) gtk_box_pack_start(vbox, menubar, FALSE, FALSE, 0); gtk_box_pack_start(vbox, toolbar, FALSE, FALSE, 0); - gtk_builder_connect_signals(builder, NULL); + gtk_builder_connect_signals(builder, glk); } int diff --git a/tests/style.css b/tests/style.css index 5c6c147..7677853 100644 --- a/tests/style.css +++ b/tests/style.css @@ -51,6 +51,8 @@ buffer.block-quote { } buffer.input { + color: #0000aa; + font-style: italic; } buffer.user1 { -- 2.30.2