- Feeding input programmatically to the Glk program now works: chimara_glk_feed_line_...
authorfliep <fliep@ddfedd41-794f-dd11-ae45-00112f111e67>
Sun, 25 Oct 2009 22:09:33 +0000 (22:09 +0000)
committerfliep <fliep@ddfedd41-794f-dd11-ae45-00112f111e67>
Sun, 25 Oct 2009 22:09:33 +0000 (22:09 +0000)
- Various other small fixes.

18 files changed:
docs/reference/chimara-sections.txt
libchimara/abort.c
libchimara/abort.h
libchimara/chimara-glk-private.h
libchimara/chimara-glk.c
libchimara/chimara-glk.h
libchimara/event.c
libchimara/event.h
libchimara/glk.c
libchimara/input.c
libchimara/input.h
tests/Makefile.am
tests/callbacks.c
tests/callbacks.h [deleted file]
tests/chimara.menus
tests/chimara.ui
tests/main.c
tests/style.css

index 51b9c62a725894f2c57a94a19cffdf840574eda6..1170c7de58b9f654c30a58817341e6b19316ea53 100644 (file)
@@ -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
 <SUBSECTION Standard>
 CHIMARA_GLK
 CHIMARA_IS_GLK
index 18b2725da810fbb143fc44dd173dd54a35ad35db..aa8ed732bddfe4eab579d2be9847196e0e907364 100644 (file)
@@ -1,3 +1,4 @@
+#include "abort.h"
 #include "event.h"
 #include <glib.h>
 #include <gtk/gtk.h>
@@ -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);
+}
index f075550c4d194e3c7792f78fd0d1faa7d1b77670..e6ac904d60b25130f958cf75e4e18887980f5b45 100644 (file)
@@ -1,7 +1,10 @@
 #ifndef ABORT_H
 #define ABORT_H
 
-G_GNUC_INTERNAL void check_for_abort();
+#include <glib.h>
+
+G_GNUC_INTERNAL void check_for_abort(void);
+G_GNUC_INTERNAL void shutdown_glk(void);
 
 #endif
 
index 1ce36c377103f24225a9b1bf6f0c7076e8dd7caf..3b1460a812c124de8a438c54e57e42cd4a9472fd 100644 (file)
@@ -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 */
index d8f071d5efa6bd25b63ee4fd68188bf018b3543d..c21e9cc499202ba32d86919eaea0ad95cd13749c 100644 (file)
@@ -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 <filename 
+ * class="headerfile">gdk/gdkkeysyms.h</filename>
+ * 
+ * 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);
+}
index b8ec38cfbc90c7426df96e355d396681a97de6ad..dfa669e3afa1b4f281712ec65a47876be89dfbd3 100644 (file)
@@ -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
 
index 88b09f02c1f31ce0fba2dfec6c35e2eb2f15432b..1bcdcd9b872ab26a0f3e581d68bf2b8e87eee5c5 100644 (file)
@@ -2,6 +2,7 @@
 #include "magic.h"
 #include "glk.h"
 #include "window.h"
+#include "input.h"
 #include <string.h>
 
 #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 <link 
+ * %evtype_Timer event has occurred. (See <link 
  * linkend="chimara-Timer-Events">Timer Events</link>.)
  * 
  * At the moment, glk_select_poll() checks for %evtype_Timer, %evtype_Arrange,
index 32e5bb92b8ba2bc7441514d28a7aa1c6a2d55f08..946bc156b094719ce224a6ce13600f8e87979527 100644 (file)
@@ -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);
 
index 27e30072769198e0ec2a0ec2959ca0a0a5fee8d6..4304aefae2aa0c80500df836f136f3817d65a693 100644 (file)
@@ -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);
 }
 
index 80455ecd64cd42b9184885776071778dfee4a279..4aca9d965f8dda0daba77e553d71779777c2127f 100644 (file)
@@ -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, charbuf, 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 
- * <ulink url="http://www.unicode.org/reports/tr15/">Unicode Standard Annex #15
- * </ulink> for the details.
+ * <ulink url="http://www.unicode.org/reports/tr15/">Unicode Standard Annex 
+ * #15</ulink> 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 <keycap>enter</keycap>, 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 <keycap>enter</keycap>, 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;
+}
index e4080fd285118b77f0be9fd15c93be7e3e262156..56fb73c7b5aa6d5ecba7cd8b9094ea146639c205 100644 (file)
@@ -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
index 76654eddb4eb008b0d27a5ef454b8f9941db4e3a..d0849b454f1ccf1ec42d3ac3a90d9dd1e66a82e1 100644 (file)
@@ -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)"\" \
index 8c6f83e29a299d224e26e18bf4a6e61869fa08ee..e08b53265656ce0c3baf3e23e53c763df2207195 100644 (file)
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "callbacks.h"
+#include <gdk/gdkkeysyms.h>
+#include <libchimara/chimara-glk.h>
 #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 (file)
index e14a667..0000000
+++ /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 <gtk/gtk.h>
-
-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);
index 4e3e7d45719db1c666449ff3d9ed4cc071ae0396..eba0050de87c04fc57da7d9534ff3fc0d4670c3d 100644 (file)
@@ -5,11 +5,19 @@
       <menuitem action="open"/>
       <menuitem action="save"/>
       <separator/>
+      <menuitem action="hint"/>
+      <menuitem action="char_input"/>
+      <menuitem action="char_input2"/>
+      <separator/>
       <menuitem action="quit"/>
     </menu>
   </menubar>
   <toolbar>
     <toolitem action="save"/>
     <toolitem action="quit"/>
+    <separator/>
+    <toolitem action="hint"/>
+    <toolitem action="char_input"/>
+    <toolitem action="char_input2"/>
   </toolbar>
 </ui>
index 2d0437d3921b462094b794e968ae189e724ff245..af6c547485349b41bf4236b7e36cd27be793e491 100644 (file)
     <property name="stock_id">gtk-quit</property>
     <signal name="activate" handler="on_quit"/>
   </object>
+  <object class="GtkAction" id="hint">
+    <property name="label">_Hint</property>
+    <property name="tooltip">Do the first few moves of the game</property>
+    <property name="stock_id">gtk-dialog-info</property>
+    <signal name="activate" handler="on_hint"/>
+  </object>
+  <object class="GtkAction" id="char_input">
+    <property name="label">Press _R</property>
+    <property name="stock_id">gtk-open</property>
+    <signal name="activate" handler="on_press_r"/>
+  </object>
+  <object class="GtkAction" id="char_input2">
+    <property name="label">3x _Enter</property>
+    <property name="stock_id">gtk-media-play</property>
+    <signal name="activate" handler="on_press_enter"/>
+  </object>
 </interface>
index 60891d3ea273f92feab5cc818167e794f919b1ca..491273903864da625eb74cd74f4cbca70abb6561 100644 (file)
@@ -40,7 +40,6 @@
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
-#include "callbacks.h"
 #include "error.h"
 #include <libchimara/chimara-glk.h>
 #include <libchimara/chimara-if.h>
@@ -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", "<ctrl>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
index 5c6c147f547ad55ac2f74c9f59487f9065da0a35..7677853d84108cc7147efe7e8e4a328bab978c34 100644 (file)
@@ -51,6 +51,8 @@ buffer.block-quote {
 }
 
 buffer.input {
+       color: #0000aa;
+       font-style: italic;
 }
 
 buffer.user1 {