aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--driver-ti.c136
-rw-r--r--termo-internal.h16
-rw-r--r--termo.c54
-rw-r--r--termo.h27
4 files changed, 196 insertions, 37 deletions
diff --git a/driver-ti.c b/driver-ti.c
index 84c86ff..e31de47 100644
--- a/driver-ti.c
+++ b/driver-ti.c
@@ -1,4 +1,4 @@
-// we want strdup()
+// We want strdup()
#define _XOPEN_SOURCE 600
#include "termo.h"
@@ -261,6 +261,21 @@ load_terminfo (termo_ti_t *ti, const char *term)
}
}
+ if (!ti->have_mouse)
+ ti->tk->guessed_mouse_proto = TERMO_MOUSE_PROTO_NONE;
+ else if (strstr (term, "rxvt") == term)
+ // urxvt generally doesn't understand the SGR protocol.
+ ti->tk->guessed_mouse_proto = TERMO_MOUSE_PROTO_RXVT;
+ else
+ // SGR (1006) is the superior protocol. If it's not supported by the
+ // terminal, nothing much happens and we continue getting events via
+ // the original protocol (1000). We can't afford to enable the UTF-8
+ // protocol (1005) because it collides with the original (1000) and we
+ // have no way of knowing if it's supported by the terminal. Also both
+ // 1000 and 1005 are broken in that they may produce characters that
+ // are illegal in the current locale's charset.
+ ti->tk->guessed_mouse_proto = TERMO_MOUSE_PROTO_SGR;
+
// Take copies of these terminfo strings, in case we build multiple termo
// instances for multiple different termtypes, and it's different by the
// time we want to use it
@@ -289,32 +304,6 @@ load_terminfo (termo_ti_t *ti, const char *term)
return true;
}
-static void *
-new_driver (termo_t *tk, const char *term)
-{
- termo_ti_t *ti = calloc (1, sizeof *ti);
- if (!ti)
- return NULL;
-
- ti->tk = tk;
- ti->root = new_node_arr (0, 0xff);
- if (!ti->root)
- goto abort_free_ti;
-
- if (!load_terminfo (ti, term))
- goto abort_free_trie;
-
- ti->root = compress_trie (ti->root);
- return ti;
-
-abort_free_trie:
- free_trie (ti->root);
-
-abort_free_ti:
- free (ti);
- return NULL;
-}
-
static bool
write_string (termo_t *tk, char *string)
{
@@ -352,32 +341,105 @@ set_mouse (termo_ti_t *ti, bool enable)
return write_string (ti->tk, start_string);
}
+static bool
+mouse_reset (termo_ti_t *ti)
+{
+ // Disable everything, a de-facto reset for all terminal mouse protocols
+ return set_mouse (ti, false)
+ && write_string (ti->tk, "\E[?1002l")
+ && write_string (ti->tk, "\E[?1003l")
+
+ && write_string (ti->tk, "\E[?1005l")
+ && write_string (ti->tk, "\E[?1006l")
+ && write_string (ti->tk, "\E[?1015l");
+}
+
+static bool
+mouse_set_proto (void *data, int proto, bool enable)
+{
+ termo_ti_t *ti = data;
+ bool success = true;
+ if (proto & TERMO_MOUSE_PROTO_VT200)
+ success &= set_mouse (ti, enable);
+ if (proto & TERMO_MOUSE_PROTO_UTF8)
+ success &= write_string (ti->tk, enable ? "\E[?1005h" : "\E[?1005l");
+ if (proto & TERMO_MOUSE_PROTO_SGR)
+ success &= write_string (ti->tk, enable ? "\E[?1006h" : "\E[?1006l");
+ if (proto & TERMO_MOUSE_PROTO_RXVT)
+ success &= write_string (ti->tk, enable ? "\E[?1015h" : "\E[?1015l");
+ return success;
+}
+
+static bool
+mouse_set_tracking_mode (void *data,
+ termo_mouse_tracking_t tracking, bool enable)
+{
+ termo_ti_t *ti = data;
+ if (tracking == TERMO_MOUSE_TRACKING_DRAG)
+ return write_string (ti->tk, enable ? "\E[?1002h" : "\E[?1002l");
+ if (tracking == TERMO_MOUSE_TRACKING_ANY)
+ return write_string (ti->tk, enable ? "\E[?1003h" : "\E[?1003l");
+ return true;
+}
+
static int
start_driver (termo_t *tk, void *info)
{
termo_ti_t *ti = info;
- // TODO: Don't start the mouse automatically, find a nice place to put
- // a public function to be called by users.
- // TODO: Try to autodetect rxvt and use its protocol instead of mode 1000
- // TODO: Also give the user a choice to use 1005, 1006 or 1015
- if (ti->have_mouse && !set_mouse (ti, true))
- return false;
- return write_string (tk, ti->start_string);
+ return write_string (tk, ti->start_string)
+ && ((!ti->have_mouse && tk->mouse_proto == TERMO_MOUSE_PROTO_NONE)
+ || mouse_reset (ti)) // We can never be sure
+ && mouse_set_proto (ti, tk->mouse_proto, true)
+ && mouse_set_tracking_mode (ti, tk->mouse_tracking, true);
}
static int
stop_driver (termo_t *tk, void *info)
{
termo_ti_t *ti = info;
- if (ti->have_mouse && !set_mouse (ti, false))
- return false;
- return write_string (tk, ti->stop_string);
+ return write_string (tk, ti->stop_string)
+ && mouse_set_proto (ti, tk->mouse_proto, false)
+ && mouse_set_tracking_mode (ti, tk->mouse_tracking, false);
+}
+
+static void *
+new_driver (termo_t *tk, const char *term)
+{
+ termo_ti_t *ti = calloc (1, sizeof *ti);
+ if (!ti)
+ return NULL;
+
+ ti->tk = tk;
+ ti->root = new_node_arr (0, 0xff);
+ if (!ti->root)
+ goto abort_free_ti;
+
+ if (!load_terminfo (ti, term))
+ goto abort_free_trie;
+
+ ti->root = compress_trie (ti->root);
+
+ tk->ti_data = ti;
+ tk->ti_method.set_mouse_proto = mouse_set_proto;
+ tk->ti_method.set_mouse_tracking_mode = mouse_set_tracking_mode;
+ return ti;
+
+abort_free_trie:
+ free_trie (ti->root);
+
+abort_free_ti:
+ free (ti);
+ return NULL;
}
static void
free_driver (void *info)
{
termo_ti_t *ti = info;
+ ti->tk->ti_data = NULL;
+ ti->tk->ti_method.set_mouse_proto = NULL;
+ ti->tk->ti_method.set_mouse_tracking_mode = NULL;
+
free_trie (ti->root);
free (ti->set_mouse_string);
free (ti->start_string);
diff --git a/termo-internal.h b/termo-internal.h
index b9a27c2..b2b1c3a 100644
--- a/termo-internal.h
+++ b/termo-internal.h
@@ -80,6 +80,22 @@ struct termo
termo_key_t *key, size_t *nbytes);
}
method;
+
+ int guessed_mouse_proto; // What we think should be the mouse protocol
+ int mouse_proto; // The active mouse protocol
+ termo_mouse_tracking_t mouse_tracking; // Mouse tracking mode
+
+ // The mouse unfortunately directly depends on the terminfo driver to let
+ // it handle changes in the mouse protocol.
+
+ void *ti_data; // termo_ti_t pointer
+
+ struct
+ {
+ bool (*set_mouse_proto) (void *, int, bool);
+ bool (*set_mouse_tracking_mode) (void *, termo_mouse_tracking_t, bool);
+ }
+ ti_method;
};
static inline void
diff --git a/termo.c b/termo.c
index 6970a82..a9addc4 100644
--- a/termo.c
+++ b/termo.c
@@ -313,6 +313,14 @@ termo_alloc (void)
tk->method.emit_codepoint = &emit_codepoint;
tk->method.peekkey_simple = &peekkey_simple;
tk->method.peekkey_mouse = &peekkey_mouse;
+
+ tk->mouse_proto = TERMO_MOUSE_PROTO_NONE;
+ tk->mouse_tracking = TERMO_MOUSE_TRACKING_NORMAL;
+ tk->guessed_mouse_proto = TERMO_MOUSE_PROTO_NONE;
+
+ tk->ti_data = NULL;
+ tk->ti_method.set_mouse_proto = NULL;
+ tk->ti_method.set_mouse_tracking_mode = NULL;
return tk;
}
@@ -625,6 +633,52 @@ termo_get_buffer_remaining (termo_t *tk)
return tk->buffsize - tk->buffcount;
}
+int
+termo_get_mouse_proto (termo_t *tk)
+{
+ return tk->mouse_proto;
+}
+
+int
+termo_guess_mouse_proto (termo_t *tk)
+{
+ return tk->guessed_mouse_proto;
+}
+
+int
+termo_set_mouse_proto (termo_t *tk, int proto)
+{
+ int old_proto = tk->mouse_proto;
+ tk->mouse_proto = proto;
+
+ // Call the TI driver to apply the change if needed
+ if (!tk->is_started
+ || !tk->ti_method.set_mouse_proto)
+ return true;
+ return tk->ti_method.set_mouse_proto (tk->ti_data, old_proto, false)
+ && tk->ti_method.set_mouse_proto (tk->ti_data, proto, true);
+}
+
+termo_mouse_tracking_t
+termo_get_mouse_tracking_mode (termo_t *tk)
+{
+ return tk->mouse_tracking;
+}
+
+int
+termo_set_mouse_tracking_mode (termo_t *tk, termo_mouse_tracking_t mode)
+{
+ termo_mouse_tracking_t old_mode = tk->mouse_tracking;
+ tk->mouse_tracking = mode;
+
+ // Call the TI driver to apply the change if needed
+ if (!tk->is_started
+ || !tk->ti_method.set_mouse_tracking_mode)
+ return true;
+ return tk->ti_method.set_mouse_tracking_mode (tk->ti_data, old_mode, false)
+ && tk->ti_method.set_mouse_tracking_mode (tk->ti_data, mode, true);
+}
+
static void
eat_bytes (termo_t *tk, size_t count)
{
diff --git a/termo.h b/termo.h
index d24dccd..ee24d87 100644
--- a/termo.h
+++ b/termo.h
@@ -121,6 +121,26 @@ enum termo_mouse_event
TERMO_MOUSE_RELEASE
};
+// You will want to handle GPM (incompatible license) and FreeBSD's sysmouse
+// yourself. http://www.monkey.org/openbsd/archive/tech/0009/msg00018.html
+
+enum
+{
+ TERMO_MOUSE_PROTO_NONE = 0,
+ TERMO_MOUSE_PROTO_VT200 = 1 << 0,
+ TERMO_MOUSE_PROTO_UTF8 = 1 << 1 | TERMO_MOUSE_PROTO_VT200,
+ TERMO_MOUSE_PROTO_SGR = 1 << 2 | TERMO_MOUSE_PROTO_VT200,
+ TERMO_MOUSE_PROTO_RXVT = 1 << 3 | TERMO_MOUSE_PROTO_VT200
+};
+
+typedef enum termo_mouse_tracking termo_mouse_tracking_t;
+enum termo_mouse_tracking
+{
+ TERMO_MOUSE_TRACKING_NORMAL,
+ TERMO_MOUSE_TRACKING_DRAG,
+ TERMO_MOUSE_TRACKING_ANY
+};
+
enum
{
TERMO_KEYMOD_SHIFT = 1 << 0,
@@ -209,6 +229,13 @@ size_t termo_get_buffer_remaining (termo_t *tk);
void termo_canonicalise (termo_t *tk, termo_key_t *key);
+int termo_get_mouse_proto (termo_t *tk);
+int termo_set_mouse_proto (termo_t *tk, int proto);
+int termo_guess_mouse_proto (termo_t *tk);
+
+termo_mouse_tracking_t termo_get_mouse_tracking_mode (termo_t *tk);
+int termo_set_mouse_tracking_mode (termo_t *tk, termo_mouse_tracking_t mode);
+
termo_result_t termo_getkey (termo_t *tk, termo_key_t *key);
termo_result_t termo_getkey_force (termo_t *tk, termo_key_t *key);
termo_result_t termo_waitkey (termo_t *tk, termo_key_t *key);