Allow incomplete imap sequence to timeout
authorThomas Legris <legris.thomas@gmail.com>
Thu, 14 Mar 2019 21:44:15 +0000 (21:44 +0000)
committerDaniel Carl <danielcarl@gmx.de>
Thu, 14 Mar 2019 22:00:14 +0000 (23:00 +0100)
src/events.c [new file with mode: 0644]
src/events.h [new file with mode: 0644]
src/map.c

diff --git a/src/events.c b/src/events.c
new file mode 100644 (file)
index 0000000..41a9975
--- /dev/null
@@ -0,0 +1,83 @@
+/* Copyright (C) 2016-2019 Michael Mackus */
+#include "events.h"
+
+/* this is only to queue GDK key events, in order to later send them if the map didn't match */
+static struct {
+    GdkEventKey **queue;               /* queue holding submitted events */
+    int           qlen;                /* pointer to last char in queue */
+    bool          processing;          /* whether or not events are processing */
+} events = {0};
+
+/**
+ * Append an event into the queue.
+ */
+void queue_event(GdkEventKey *e)
+{
+    GdkEventKey **newqueue = realloc(events.queue, (events.qlen + 1) * sizeof **newqueue);
+
+    if (newqueue == NULL) {
+        // error allocating memory
+        return;
+    }
+
+    events.queue = newqueue;
+
+    /* copy memory (otherwise event gets cleared by gdk) */
+    GdkEventKey *tmp = malloc(sizeof *tmp);
+    memcpy(tmp, e, sizeof *e);
+
+    if (tmp == NULL) {
+        // error allocating memory
+        return;
+    }
+
+    events.queue[events.qlen] = tmp;
+    events.qlen ++;
+}
+
+void process_event(GdkEventKey* event)
+{
+    if (event == NULL) {
+        return;
+    }
+
+    events.processing = true; /* signal not to queue other events */
+    gtk_main_do_event ((GdkEvent *) event);
+    events.processing = false;
+    free(event);
+}
+
+/**
+ * Process events in the queue, sending the key events to GDK.
+ */
+void process_events()
+{
+    for (int i = 0; i < events.qlen; ++i) {
+        process_event(events.queue[i]); /* process & free the event */
+        /* TODO take into account qk mapped key? */
+    }
+
+    events.qlen = 0;
+}
+
+/**
+ * Check if the events are currently processing (i.e. being sent to GDK
+ * unhandled).  Provided in order to encapsulate the "events" global struct.
+ */
+bool is_processing_events()
+{
+    return events.processing;
+}
+
+/**
+ * Clear the event queue by resetting the length. Provided in order to
+ * encapsulate the "events" global struct.
+ */
+void free_events()
+{
+    for (int i = 0; i < events.qlen; ++i) {
+        free(events.queue[i]);
+    }
+
+    events.qlen = 0;
+}
diff --git a/src/events.h b/src/events.h
new file mode 100644 (file)
index 0000000..565de6b
--- /dev/null
@@ -0,0 +1,15 @@
+/* Copyright (C) 2016-2019 Michael Mackus */
+#ifndef _EVENTS_H
+#define _EVENTS_H
+
+#include <gdk/gdkkeysyms.h>
+#include <gdk/gdkkeysyms-compat.h>
+#include "main.h"
+#include "map.h"
+
+void queue_event(GdkEventKey *e);
+void process_events();
+bool is_processing_events();
+void free_events();
+
+#endif /* end of include guard: _MAP_H */
index b854207..bb35808 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -1,7 +1,7 @@
 /**
  * vimb - a webkit based vim like browser.
  *
- * Copyright (C) 2012-2018 Daniel Carl
+ * Copyright (C) 2012-2019 Daniel Carl
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,6 +23,7 @@
 
 #include "ascii.h"
 #include "config.h"
+#include "events.h"
 #include "main.h"
 #include "map.h"
 #include "util.h"
@@ -324,6 +325,11 @@ gboolean map_delete(Client *c, const char *in, char mode)
  */
 gboolean on_map_keypress(GtkWidget *widget, GdkEventKey* event, Client *c)
 {
+    if (is_processing_events()) {
+        /* events are processing, pass all keys unmodified */
+        return false;
+    }
+
     guint state  = event->state;
     guint keyval = event->keyval;
     guchar string[32];
@@ -362,12 +368,25 @@ gboolean on_map_keypress(GtkWidget *widget, GdkEventKey* event, Client *c)
     c->state.typed         = TRUE;
     c->state.processed_key = TRUE;
 
-    map_handle_keys(c, string, len, TRUE);
+    queue_event(event);
+
+    MapState res = map_handle_keys(c, string, len, true);
+
+    if (res != MAP_AMBIGUOUS) {
+        if (!c->state.processed_key) {
+            /* events ready to be consumed */
+            process_events();
+        } else {
+            /* no ambiguous - key processed elsewhere */
+            free_events();
+        }
+    }
 
     /* reset the typed flag */
     c->state.typed = FALSE;
 
-    return c->state.processed_key;
+    /* prevent input from going to GDK - input is sent via process_events(); */
+    return TRUE;
 }
 
 /**
@@ -476,6 +495,9 @@ static gboolean do_timeout(Client *c)
     /* signalize the timeout to the key handler */
     map_handle_keys(c, (guchar*)"", 0, TRUE);
 
+    /* consume any unprocessed events */
+    process_events();
+
     /* we return TRUE to not automatically remove the resource - this is
      * required to prevent critical error when we remove the source in
      * map_handle_keys where we don't know if the timeout was called or not */