diff --git a/ruamoko/qwaq/Makefile.am b/ruamoko/qwaq/Makefile.am index df02f64ab..f8e402e10 100644 --- a/ruamoko/qwaq/Makefile.am +++ b/ruamoko/qwaq/Makefile.am @@ -12,7 +12,7 @@ QFCC_DEP=$(top_builddir)/tools/qfcc/source/qfcc$(EXEEXT) QFCC=$(top_builddir)/tools/qfcc/source/qfcc QCFLAGS=-qq -O -g -Werror -QCPPFLAGS=--no-default-paths -I$(top_srcdir)/ruamoko/include +QCPPFLAGS=--no-default-paths -I$(top_srcdir)/ruamoko/include -I$(top_srcdir)/include QCLINKFLAGS=--no-default-paths -L$(top_builddir)/ruamoko/lib QCOMPILE=$(QFCC) $(QCFLAGS) $(QCPPFLAGS) QLINK=$(QFCC) $(QCFLAGS) $(QCLINKFLAGS) @@ -36,7 +36,11 @@ qwaq_app_dat_src= \ qwaq-view.r \ qwaq-window.r -qwaq_curses_libs= +#FIXME this coupling is horrible +qwaq_curses_libs= \ + $(top_builddir)/libs/video/targets/libvid_common.la \ + $(top_builddir)/libs/console/libQFconsole.la \ + $(top_builddir)/libs/gib/libQFgib.la qwaq_curses_SOURCES=main.c qwaq-curses.c qwaq-input.c qwaq_curses_LDADD= $(qwaq_curses_libs) $(QWAQ_LIBS) \ $(PANEL_LIBS) $(CURSES_LIBS) $(PTHREAD_LDFLAGS) $(DL_LIBS) @@ -73,7 +77,7 @@ qwaq_app_dat_SOURCES=$(qwaq_app_dat_src) qwaq_app_obj=$(qwaq_app_dat_SOURCES:.r=.o) qwaq_app_dep=$(addprefix ./$(DEPDIR)/,$(qwaq_app_obj:.o=.Qo)) qwaq-app.dat$(EXEEXT): $(qwaq_app_obj) $(QFCC_DEP) - $(QLINK) -o $@ $(qwaq_app_obj) -lr + $(QLINK) -o $@ $(qwaq_app_obj) -lcsqc -lr include $(qwaq_app_dep) # am--include-marker r_depfiles_remade += $(qwaq_app_dep) diff --git a/ruamoko/qwaq/event.h b/ruamoko/qwaq/event.h index f1d9e6fbb..89534f32a 100644 --- a/ruamoko/qwaq/event.h +++ b/ruamoko/qwaq/event.h @@ -41,6 +41,11 @@ typedef struct qwaq_mevent_s { int click; } qwaq_mevent_t; +typedef struct qwaq_kevent_s { + int code; + int shift; +} qwaq_kevent_t; + typedef struct qwaq_message_s { qwaq_command command; } qwaq_message_t; @@ -49,7 +54,7 @@ typedef struct qwaq_event_s { int what; double when; // NOTE: 1<<32 based union { - int key; + qwaq_kevent_t key; qwaq_mevent_t mouse; qwaq_message_t message; }; diff --git a/ruamoko/qwaq/main.c b/ruamoko/qwaq/main.c index 45da8847e..b9f0dd0e4 100644 --- a/ruamoko/qwaq/main.c +++ b/ruamoko/qwaq/main.c @@ -43,6 +43,7 @@ #include "QF/cvar.h" #include "QF/gib.h" #include "QF/idparse.h" +#include "QF/keys.h" #include "QF/progs.h" #include "QF/qargs.h" #include "QF/quakefs.h" @@ -151,6 +152,7 @@ create_progs (void) PR_Init_Cvars (); PR_Init (pr); RUA_Init (pr, 0); + Key_Progs_Init (pr); PR_Cmds_Init (pr); BI_Init (pr); diff --git a/ruamoko/qwaq/qwaq-curses.h b/ruamoko/qwaq/qwaq-curses.h index 9bc5c1d2d..55f121990 100644 --- a/ruamoko/qwaq/qwaq-curses.h +++ b/ruamoko/qwaq/qwaq-curses.h @@ -166,6 +166,7 @@ typedef enum { esc_csi, esc_mouse, esc_sgr, + esc_key, } esc_state_t; typedef struct qwaq_resources_s { @@ -188,6 +189,7 @@ typedef struct qwaq_resources_s { esc_state_t escstate; unsigned button_state; qwaq_event_t lastClick; + struct hashtab_s *key_sequences; } qwaq_resources_t; // gcc stuff diff --git a/ruamoko/qwaq/qwaq-input.c b/ruamoko/qwaq/qwaq-input.c index 016f57aa0..505ea4f8f 100644 --- a/ruamoko/qwaq/qwaq-input.c +++ b/ruamoko/qwaq/qwaq-input.c @@ -40,6 +40,8 @@ #include #include "QF/dstring.h" +#include "QF/hash.h" +#include "QF/keys.h" #include "QF/sys.h" #include "qwaq.h" @@ -54,6 +56,112 @@ #define SGR_OFF "\033[?1006l" #define WHEEL_BUTTONS 0x7c // scroll up/down/left/right - always click +typedef struct qwaq_key_s { + const char *sequence; + knum_t key; + unsigned shift; +} qwaq_key_t; + +static qwaq_key_t default_keys[] = { + { "\033OP", QFK_F1 }, + { "\033OQ", QFK_F2 }, + { "\033OR", QFK_F3 }, + { "\033OS", QFK_F4 }, + { "\033[15~", QFK_F5 }, + { "\033[17~", QFK_F6 }, + { "\033[18~", QFK_F7 }, + { "\033[19~", QFK_F8 }, + { "\033[20~", QFK_F9 }, + { "\033[21~", QFK_F10 }, + { "\033[23~", QFK_F11 }, + { "\033[24~", QFK_F12 }, + // shift F1-F12 + { "\033[1;2P", QFK_F13 }, + { "\033[1;2Q", QFK_F14 }, + { "\033[1;2R", QFK_F15 }, + { "\033[1;2S", QFK_F16 }, + { "\033[15;2~", QFK_F17 }, + { "\033[17;2~", QFK_F18 }, + { "\033[18;2~", QFK_F19 }, + { "\033[19;2~", QFK_F20 }, + { "\033[20;2~", QFK_F21 }, + { "\033[21;2~", QFK_F22 }, + { "\033[23;2~", QFK_F23 }, + { "\033[24;2~", QFK_F24 }, + // control F1-F12 + { "\033[1;5P", QFK_F25 }, + { "\033[1;5Q", QFK_F26 }, + { "\033[1;5R", QFK_F27 }, + { "\033[1;5S", QFK_F28 }, + { "\033[15;5~", QFK_F29 }, + { "\033[17;5~", QFK_F30 }, + { "\033[18;5~", QFK_F31 }, + { "\033[19;5~", QFK_F32 }, + { "\033[20;5~", QFK_F33 }, + { "\033[21;5~", QFK_F34 }, + { "\033[23;5~", QFK_F35 }, + { "\033[24;5~", QFK_F36 }, + // shift control F1-F12 + { "\033[1;6P", QFK_F37 }, + { "\033[1;6Q", QFK_F38 }, + { "\033[1;6R", QFK_F39 }, + { "\033[1;6S", QFK_F40 }, + { "\033[15;6~", QFK_F41 }, + { "\033[17;6~", QFK_F42 }, + { "\033[18;6~", QFK_F43 }, + { "\033[19;6~", QFK_F44 }, + { "\033[20;6~", QFK_F45 }, + { "\033[21;6~", QFK_F46 }, + { "\033[23;6~", QFK_F47 }, + { "\033[24;6~", QFK_F48 }, + + { "\033[2~", QFK_INSERT, 0 }, + { "\033[3~", QFK_DELETE, 0 }, + { "\033[H", QFK_HOME, 0 }, + { "\033[F", QFK_END, 0 }, + { "\033[5~", QFK_PAGEUP, 0 }, + { "\033[6~", QFK_PAGEDOWN, 0 }, + { "\033[A", QFK_UP, 0 }, + { "\033[B", QFK_DOWN, 0 }, + { "\033[C", QFK_RIGHT, 0 }, + { "\033[D", QFK_LEFT, 0 }, + // shift + // there may be a setting to tell xterm to NOT act on shift ins/pgup/pgdn + { "\033[2;2~", QFK_INSERT, 1 }, // xterm gobbles (and pastes) + { "\033[3;2~", QFK_DELETE, 1 }, + { "\033[1;2H", QFK_HOME, 1 }, + { "\033[1;2F", QFK_END, 1 }, + { "\033[5;2~", QFK_PAGEUP, 1 }, // xterm gobbles (scrolls term) + { "\033[6;2~", QFK_PAGEDOWN, 1 }, // xterm gobbles (scrolls term) + { "\033[1;2A", QFK_UP, 1 }, + { "\033[1;2B", QFK_DOWN, 1 }, + { "\033[1;2C", QFK_RIGHT, 1 }, + { "\033[1;2D", QFK_LEFT, 1 }, + { "\033[Z", QFK_TAB, 1 }, + // control + { "\033[2;5~", QFK_INSERT, 4 }, + { "\033[3;5~", QFK_DELETE, 4 }, + { "\033[1;5H", QFK_HOME, 4 }, + { "\033[1;5F", QFK_END, 4 }, + { "\033[5;5~", QFK_PAGEUP, 4 }, + { "\033[6;5~", QFK_PAGEDOWN, 4 }, + { "\033[1;5A", QFK_UP, 4 }, + { "\033[1;5B", QFK_DOWN, 4 }, + { "\033[1;5C", QFK_RIGHT, 4 }, + { "\033[1;5D", QFK_LEFT, 4 }, + // shift control + { "\033[2;6~", QFK_INSERT, 5 }, + { "\033[3;6~", QFK_DELETE, 5 }, + { "\033[1;6H", QFK_HOME, 5 }, + { "\033[1;6F", QFK_END, 5 }, + { "\033[5;6~", QFK_PAGEUP, 5 }, + { "\033[6;6~", QFK_PAGEDOWN, 5 }, + { "\033[1;6A", QFK_UP, 5 }, + { "\033[1;6B", QFK_DOWN, 5 }, + { "\033[1;6C", QFK_RIGHT, 5 }, + { "\033[1;6D", QFK_LEFT, 5 }, +}; + static void add_event (qwaq_resources_t *res, qwaq_event_t *event) { @@ -87,12 +195,13 @@ add_event (qwaq_resources_t *res, qwaq_event_t *event) } static void -key_event (qwaq_resources_t *res, int key) +key_event (qwaq_resources_t *res, int key, unsigned shift) { qwaq_event_t event = {}; event.what = qe_keydown; event.when = Sys_DoubleTime (); - event.key = key; + event.key.code = key; + event.key.shift = shift; add_event (res, &event); } @@ -180,6 +289,19 @@ parse_sgr (qwaq_resources_t *res, char cmd) parse_mouse (res, ctrl, x - 1, y - 1, cmd); } +static void +parse_key (qwaq_resources_t *res) +{ + qwaq_key_t *key = Hash_Find (res->key_sequences, res->escbuff.str); + + Sys_Printf ("parse_key %s %p\n", res->escbuff.str + 1, key); + if (key) { + Sys_Printf (" %d %03x %s\n", key->key, key->shift, + Key_KeynumToString (key->key)); + key_event (res, key->key, key->shift); + } +} + static void process_char (qwaq_resources_t *res, char ch) { if (ch == 0x1b) { @@ -188,11 +310,19 @@ static void process_char (qwaq_resources_t *res, char ch) } else { switch (res->escstate) { case esc_ground: - key_event (res, (byte) ch); + key_event (res, (byte) ch, 0); // shift state unknown break; case esc_escape: if (ch == '[') { res->escstate = esc_csi; + } else if (ch == 'O') { + // will wind up accepting P;P... but meh + res->escstate = esc_key; + dstring_clear (&res->escbuff); + // start the buffer with what got us hear: eases key lookup + dstring_append (&res->escbuff, "\033O", 2); + } else { + res->escstate = esc_ground; } break; case esc_csi: @@ -202,6 +332,14 @@ static void process_char (qwaq_resources_t *res, char ch) } else if (ch == '<') { res->escstate = esc_sgr; dstring_clear (&res->escbuff); + } else if (ch >= '0' && ch < 127) { + res->escstate = esc_key; + dstring_clear (&res->escbuff); + // start the buffer with what got us hear: eases key lookup + dstring_append (&res->escbuff, "\033[", 2); + // the csi code might be short (eg, \e[H for home) so + // need to check for end of string right away + goto esc_key_jump; } else { res->escstate = esc_ground; } @@ -214,23 +352,51 @@ static void process_char (qwaq_resources_t *res, char ch) } break; case esc_sgr: - if (isdigit (ch) || ch ==';') { + if (isdigit (ch) || ch == ';') { dstring_append (&res->escbuff, &ch, 1); } else { if (ch == 'm' || ch == 'M') { + // terminate the string dstring_append (&res->escbuff, "", 1); parse_sgr (res, ch); } res->escstate = esc_ground; } break; + case esc_key: +esc_key_jump: + dstring_append (&res->escbuff, &ch, 1); + if (!isdigit (ch) && ch != ';') { + // terminate the string + dstring_append (&res->escbuff, "", 1); + // parse_key will sort out whether it was a valid sequence + parse_key (res); + res->escstate = esc_ground; + } + break; } //printf("res->escstate %d\n", res->escstate); } } +static const char * +key_sequence_getkey (const void *_seq, void *unused) +{ + __auto_type seq = (const qwaq_key_t *) _seq; + return seq->sequence; +} + void qwaq_input_init (qwaq_resources_t *res) { + if (res->key_sequences) { + Hash_FlushTable (res->key_sequences); + } else { + res->key_sequences = Hash_NewTable (127, key_sequence_getkey, 0, 0); + } + for (size_t i = 0; i < sizeof (default_keys) / sizeof (default_keys[0]); + i++) { + Hash_Add (res->key_sequences, &default_keys[i]); + } // ncurses takes care of input mode for us, so need only tell xterm // what we need write(1, MOUSE_MOVES_ON, sizeof (MOUSE_MOVES_ON) - 1);