summaryrefslogtreecommitdiff
path: root/pico/osdep/getkey.c
diff options
context:
space:
mode:
Diffstat (limited to 'pico/osdep/getkey.c')
-rw-r--r--pico/osdep/getkey.c565
1 files changed, 565 insertions, 0 deletions
diff --git a/pico/osdep/getkey.c b/pico/osdep/getkey.c
new file mode 100644
index 00000000..b55dd092
--- /dev/null
+++ b/pico/osdep/getkey.c
@@ -0,0 +1,565 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: getkey.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2007 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../estruct.h"
+#include "../mode.h"
+#include "../pico.h"
+#include "../edef.h"
+#include "../efunc.h"
+#include "../keydefs.h"
+
+#include "tty.h"
+#include "getkey.h"
+#include "read.h"
+#include "mouse.h"
+
+#ifdef _WINDOWS
+#include "mswin.h"
+static int MapMSKEYtoPK(int c);
+#endif /* _WINDOWS */
+
+
+/* internal declarations */
+static int timeo = 0;
+
+/* these next two are declared in pith/conf.h */
+int
+set_input_timeout(int t)
+{
+ int oldtimeo = timeo;
+
+ timeo = t;
+ return(oldtimeo);
+}
+
+int
+get_input_timeout(void)
+{
+ return(timeo);
+}
+
+
+#ifndef _WINDOWS
+
+
+/* internal prototypes */
+void bail(void);
+int ReadyForKey(int);
+
+
+
+void
+bail(void)
+{
+ sleep(30); /* see if os receives SIGHUP */
+ kill(getpid(), SIGHUP); /* eof or bad error */
+}
+
+
+#if TYPEAH
+/*
+ * typahead - Check to see if any characters are already in the
+ * keyboard buffer
+ */
+int
+typahead(void)
+{
+ int x; /* holds # of pending chars */
+
+ return((ioctl(0,FIONREAD,&x) < 0) ? 0 : x);
+}
+#endif /* TYPEAH */
+
+
+
+/*
+ * ReadyForKey - return true if there's no timeout or we're told input
+ * is available...
+ */
+int
+ReadyForKey(int timeout)
+{
+ switch(input_ready(timeout)){
+ case READY_TO_READ:
+ return(1);
+ break;
+
+ case NO_OP_COMMAND:
+ case NO_OP_IDLE:
+ case READ_INTR:
+ return(0);
+
+ case BAIL_OUT:
+ case PANIC_NOW:
+ emlwrite("\007Problem reading from keyboard!", NULL);
+ kill(getpid(), SIGHUP); /* Bomb out (saving our work)! */
+ /* no return */
+ }
+
+ /* can't happen */
+ return(0);
+}
+
+
+
+/*
+ * GetKey - Read in a key.
+ * Do the standard keyboard preprocessing. Convert the keys to the internal
+ * character set. Resolves escape sequences and returns no-op if global
+ * timeout value exceeded.
+ */
+UCS
+GetKey(void)
+{
+ UCS ch, status, cc;
+
+ if(!ReadyForKey(FUDGE-5))
+ return(NODATA);
+
+ switch(status = kbseq(simple_ttgetc, NULL, bail, input_cs, &ch)){
+ case 0: /* regular character */
+ break;
+
+ case KEY_DOUBLE_ESC:
+ /*
+ * Special hack to get around comm devices eating control characters.
+ */
+ if(!ReadyForKey(5))
+ return(BADESC); /* user typed ESC ESC, then stopped */
+ else
+ switch(status = kbseq(simple_ttgetc, NULL, bail, input_cs, &ch)){
+ case KEY_UP :
+ case KEY_DOWN :
+ case KEY_RIGHT :
+ case KEY_LEFT :
+ case KEY_PGUP :
+ case KEY_PGDN :
+ case KEY_HOME :
+ case KEY_END :
+ case KEY_DEL :
+ case F1 :
+ case F2 :
+ case F3 :
+ case F4 :
+ case F5 :
+ case F6 :
+ case F7 :
+ case F8 :
+ case F9 :
+ case F10 :
+ case F11 :
+ case F12 :
+ return(CTRL | status);
+ break;
+
+ case 0: /* regular character */
+ break;
+
+ default: /* punt the whole thing */
+ (*term.t_beep)();
+ return(BADESC);
+ break;
+ }
+
+ ch &= 0x7f;
+ if(isdigit((unsigned char)ch)){
+ int n = 0, i = ch - '0';
+
+ if(i < 0 || i > 2)
+ return(BADESC); /* bogus literal char value */
+
+ while(n++ < 2){
+ if(!ReadyForKey(5)
+ || (!isdigit((unsigned char) (ch =
+ (*term.t_getchar)(NODATA, NULL, bail)))
+ || (n == 1 && i == 2 && ch > '5')
+ || (n == 2 && i == 25 && ch > '5'))){
+ return(BADESC);
+ }
+
+ i = (i * 10) + (ch - '0');
+ }
+
+ ch = i;
+ }
+ else{
+ if(islower((unsigned char)ch)) /* canonicalize if alpha */
+ ch = toupper((unsigned char)ch);
+
+ return((isalpha((unsigned char)ch) || ch == '@'
+ || (ch >= '[' && ch <= '_'))
+ ? (CTRL | ch) : ((ch == ' ') ? (CTRL | '@') : ch));
+ }
+
+ break;
+
+#ifdef MOUSE
+ case KEY_XTERM_MOUSE:
+ {
+ /*
+ * Special hack to get mouse events from an xterm.
+ * Get the details, then pass it past the keymenu event
+ * handler, and then to the installed handler if there
+ * is one...
+ */
+ static int down = 0;
+ int x, y, button;
+ unsigned long cmd;
+
+ button = (*term.t_getchar)(NODATA, NULL, bail) & 0x03;
+
+ x = (*term.t_getchar)(NODATA, NULL, bail) - '!';
+ y = (*term.t_getchar)(NODATA, NULL, bail) - '!';
+
+ if(button == 0){
+ down = 1;
+ if(checkmouse(&cmd, 1, x, y))
+ return((UCS) cmd);
+ }
+ else if(down && button == 3){
+ down = 0;
+ if(checkmouse(&cmd, 0, x, y))
+ return((UCS) cmd);
+ }
+
+ return(NODATA);
+ }
+
+ break;
+#endif /* MOUSE */
+
+ case KEY_UP :
+ case KEY_DOWN :
+ case KEY_RIGHT :
+ case KEY_LEFT :
+ case KEY_PGUP :
+ case KEY_PGDN :
+ case KEY_HOME :
+ case KEY_END :
+ case KEY_DEL :
+ case F1 :
+ case F2 :
+ case F3 :
+ case F4 :
+ case F5 :
+ case F6 :
+ case F7 :
+ case F8 :
+ case F9 :
+ case F10 :
+ case F11 :
+ case F12 :
+ return(status);
+
+ case CTRL_KEY_UP :
+ return(CTRL | KEY_UP);
+ case CTRL_KEY_DOWN :
+ return(CTRL | KEY_DOWN);
+ case CTRL_KEY_RIGHT :
+ return(CTRL | KEY_RIGHT);
+ case CTRL_KEY_LEFT :
+ return(CTRL | KEY_LEFT);
+
+ case KEY_SWALLOW_Z:
+ status = BADESC;
+ case KEY_SWAL_UP:
+ case KEY_SWAL_DOWN:
+ case KEY_SWAL_LEFT:
+ case KEY_SWAL_RIGHT:
+ do
+ if(!ReadyForKey(2)){
+ status = BADESC;
+ break;
+ }
+ while(!strchr("~qz", (*term.t_getchar)(NODATA, NULL, bail)));
+
+ return((status == BADESC)
+ ? status
+ : status - (KEY_SWAL_UP - KEY_UP));
+ break;
+
+ case KEY_KERMIT:
+ do{
+ cc = ch;
+ if(!ReadyForKey(2))
+ return(BADESC);
+ else
+ ch = (*term.t_getchar)(NODATA, NULL, bail) & 0x7f;
+ }while(cc != '\033' && ch != '\\');
+
+ ch = NODATA;
+ break;
+
+ case BADESC:
+ (*term.t_beep)();
+ return(status);
+
+ default: /* punt the whole thing */
+ (*term.t_beep)();
+ break;
+ }
+
+ if (ch >= 0x00 && ch <= 0x1F) /* C0 control -> C- */
+ ch = CTRL | (ch+'@');
+
+ return(ch);
+}
+
+
+/*
+ * kbseq - looks at an escape sequence coming from the keyboard and
+ * compares it to a trie of known keyboard escape sequences, and
+ * returns the function bound to the escape sequence.
+ *
+ * Args: getcfunc -- Function to get a single character from stdin,
+ * called with the next two arguments to this
+ * function as its arguments.
+ * recorder -- If non-NULL, function used to record keystroke.
+ * bail_handler -- Function used to bail out on read error.
+ * c -- Pointer to returned character.
+ *
+ * Returns: BADESC
+ * The escaped function.
+ * 0 if a regular char with char stuffed in location c.
+ */
+UCS
+kbseq(int (*getcfunc)(int (*recorder)(int ), void (*bail_handler)(void )),
+ int (*recorder)(int),
+ void (*bail_handler)(void),
+ void *data,
+ UCS *ch)
+{
+ unsigned char c;
+ int first = 1;
+ KBESC_T *current;
+
+ current = kbesc;
+ if(current == NULL) /* bag it */
+ return(BADESC);
+
+ while(1){
+ c = (*getcfunc)(recorder, bail_handler);
+
+ while(current->value != c){
+ if(current->left == NULL){ /* NO MATCH */
+ if(first){
+ unsigned long octets_so_far, remaining_octets;
+ unsigned char *inputp;
+ UCS ucs;
+ unsigned char inputbuf[20];
+
+ /*
+ * Regular character.
+ * Read enough bytes to make up a character and convert it to UCS-4.
+ */
+ memset(inputbuf, 0, sizeof(inputbuf));
+ inputbuf[0] = c;
+ octets_so_far = 1;
+ for(;;){
+ remaining_octets = octets_so_far;
+ inputp = inputbuf;
+ ucs = mbtow(data, &inputp, &remaining_octets);
+ switch(ucs){
+ case CCONV_BADCHAR:
+ /*
+ * Not really a BADESC but that ought to
+ * be sufficient. We can add another type if
+ * we need to.
+ */
+ return(BADESC);
+
+ case CCONV_NEEDMORE:
+ if(octets_so_far >= sizeof(inputbuf))
+ return(BADESC);
+
+ c = (*getcfunc)(recorder, bail_handler);
+ inputbuf[octets_so_far++] = c;
+ break;
+
+ default:
+ /* got a good UCS-4 character */
+ *ch = ucs;
+ return(0);
+ }
+ }
+
+ /* NOTREACHED */
+ return(0);
+ }
+ else
+ return(BADESC);
+ }
+ current = current->left;
+ }
+
+ if(current->down == NULL) /* match!!!*/
+ return(current->func);
+ else
+ current = current->down;
+
+ first = 0;
+ }
+}
+
+
+#define newnode() (KBESC_T *)malloc(sizeof(KBESC_T))
+
+/*
+ * kpinsert - insert a keystroke escape sequence into the global search
+ * structure.
+ */
+void
+kpinsert(char *kstr, int kval, int termcap_wins)
+{
+ register char *buf;
+ register KBESC_T *temp;
+ register KBESC_T *trail;
+
+ if(kstr == NULL)
+ return;
+
+ /*
+ * Don't allow escape sequences that don't start with ESC unless
+ * termcap_wins. This is to protect against mistakes in termcap files.
+ */
+ if(!termcap_wins && *kstr != '\033')
+ return;
+
+ temp = trail = kbesc;
+ buf = kstr;
+
+ for(;;){
+ if(temp == NULL){
+ temp = newnode();
+ temp->value = *buf;
+ temp->func = 0;
+ temp->left = NULL;
+ temp->down = NULL;
+ if(kbesc == NULL)
+ kbesc = temp;
+ else
+ trail->down = temp;
+ }
+ else{ /* first entry */
+ while((temp != NULL) && (temp->value != *buf)){
+ trail = temp;
+ temp = temp->left;
+ }
+
+ if(temp == NULL){ /* add new val */
+ temp = newnode();
+ temp->value = *buf;
+ temp->func = 0;
+ temp->left = NULL;
+ temp->down = NULL;
+ trail->left = temp;
+ }
+ }
+
+ if(*(++buf) == '\0')
+ break;
+ else{
+ /*
+ * Ignore attempt to overwrite shorter existing escape sequence.
+ * That means that sequences with higher priority should be
+ * set up first, so if we want termcap sequences to override
+ * hardwired sequences, put the kpinsert calls for the
+ * termcap sequences first. (That's what you get if you define
+ * TERMCAP_WINS.)
+ */
+ if(temp->func != 0)
+ return;
+
+ trail = temp;
+ temp = temp->down;
+ }
+ }
+
+ /*
+ * Ignore attempt to overwrite longer sequences we are a prefix
+ * of (down != NULL) and exact same sequence (func != 0).
+ */
+ if(temp != NULL && temp->down == NULL && temp->func == 0)
+ temp->func = kval;
+}
+
+
+
+/*
+ * kbdestroy() - kills the key pad function key search tree
+ * and frees all lines associated with it
+ *
+ * Should be called with arg kbesc, the top of the tree.
+ */
+void
+kbdestroy(KBESC_T *kb)
+{
+ if(kb){
+ kbdestroy(kb->left);
+ kbdestroy(kb->down);
+ free((char *)kb);
+ kb = NULL;
+ }
+}
+
+#else /* _WINDOWS */
+
+/*
+ * Read in a key.
+ * Do the standard keyboard preprocessing. Convert the keys to the internal
+ * character set. Resolves escape sequences and returns no-op if global
+ * timeout value exceeded.
+ */
+UCS
+GetKey(void)
+{
+ UCS ch = 0;
+ long timein;
+
+
+ ch = NODATA;
+ timein = time(0L);
+
+ /*
+ * Main character processing loop.
+ */
+ while(!mswin_charavail()) {
+
+#ifdef MOUSE
+ /* Check Mouse. If we get a mouse event, convert to char
+ * event and return that. */
+ if (checkmouse (&ch,0,0,0)) {
+ curwp->w_flag |= WFHARD;
+ return (ch);
+ }
+#endif /* MOUSE */
+
+
+ /* Check Timeout. */
+ if(time(0L) >= timein+(FUDGE-10))
+ return(NODATA);
+ }
+
+
+ return (mswin_getc_fast());
+}
+
+#endif /* _WINDOWS */