1 //  This may look like C code, but it is really -*- C++ -*-
2 
3 //  ------------------------------------------------------------------
4 //  The Goldware Library
5 //  Copyright (C) 1990-1999 Odinn Sorensen
6 //  ------------------------------------------------------------------
7 //  This library is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU Library General Public
9 //  License as published by the Free Software Foundation; either
10 //  version 2 of the License, or (at your option) any later version.
11 //
12 //  This library is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  Library General Public License for more details.
16 //
17 //  You should have received a copy of the GNU Library General Public
18 //  License along with this program; if not, write to the Free
19 //  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 //  MA 02111-1307, USA
21 //  ------------------------------------------------------------------
22 //  $Id: gkbdunix.cpp,v 1.5 2003/04/28 14:42:26 asa Exp $
23 //  ------------------------------------------------------------------
24 //  Unix keyboard functions. Based on SLang source code.
25 //  ------------------------------------------------------------------
26 
27 #include <unistd.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <signal.h>
33 #include <stdlib.h>
34 #include <termios.h>
35 #include <sys/time.h>
36 #include <sys/types.h>
37 #include <sys/file.h>
38 #include <sys/ioctl.h>
39 #include <sys/stat.h>
40 #include <gutlunix.h>
41 #include <gsigunix.h>
42 #include <gkbdunix.h>
43 
44 #ifdef __BEOS__
45 //sz: some undocumented call that behaves in same manner as select ...
46 //used in BEOS_BONE builds ...
47 extern "C" int waiton( int, fd_set *, fd_set *, fd_set *, bigtime_t);
48 #endif
49 //  ------------------------------------------------------------------
50 
51 int gkbd_stdin = -1;
52 
53 
54 //  ------------------------------------------------------------------
55 
56 static termios gkbd_oldtty;
57 
58 
59 //  ------------------------------------------------------------------
60 
61 static bool gkbd_tty_inited = false;
62 static bool gkbd_tty_open = false;
63 
64 
65 //  ------------------------------------------------------------------
66 
gkbd_tty_init()67 int gkbd_tty_init() {
68 
69   gsig_block_signals();
70 
71   gkbd_tty_open = false;
72 
73   if((gkbd_stdin == -1) or (isatty(gkbd_stdin) != 1)) {
74 
75     gkbd_stdin = open("/dev/tty", O_RDWR);
76 
77     if(gkbd_stdin >= 0)
78       gkbd_tty_open = true;
79 
80     if(not gkbd_tty_open) {
81       gkbd_stdin = fileno(stderr);
82       if(isatty(gkbd_stdin) != 1) {
83         gkbd_stdin = fileno (stdin);
84         if(isatty(gkbd_stdin) != 1)
85           return -1;
86       }
87     }
88   }
89 
90   while(GET_TERMIOS(gkbd_stdin, &gkbd_oldtty) == -1) {
91     if(errno != EINTR) {
92       gsig_unblock_signals();
93       return -1;
94     }
95   }
96 
97   termios newtty;
98 
99   while(GET_TERMIOS(gkbd_stdin, &newtty) == -1) {
100     if(errno != EINTR) {
101       gsig_unblock_signals();
102       return -1;
103     }
104   }
105 
106   newtty.c_oflag &= ~OPOST;
107 
108   newtty.c_lflag = ISIG | NOFLSH;
109 
110   newtty.c_iflag &= ~(ECHO | INLCR | ICRNL | IXON);
111 
112   newtty.c_cc[VMIN] = 1;
113   newtty.c_cc[VEOF] = 1;
114   newtty.c_cc[VTIME] = 0;
115   newtty.c_cc[VINTR] = NULL_VALUE;
116   newtty.c_cc[VQUIT] = NULL_VALUE;
117   newtty.c_cc[VSUSP] = NULL_VALUE;
118 
119   while(SET_TERMIOS(gkbd_stdin, &newtty) == -1) {
120     if(errno != EINTR) {
121       gsig_unblock_signals();
122       return -1;
123     }
124   }
125 
126   gkbd_tty_inited = true;
127 
128   gsig_unblock_signals();
129 
130   return 0;
131 }
132 
133 
134 //  ------------------------------------------------------------------
135 
gkbd_tty_reset()136 void gkbd_tty_reset() {
137 
138   gsig_block_signals();
139 
140   if(not gkbd_tty_inited) {
141     gsig_unblock_signals();
142     return;
143   }
144 
145   while((SET_TERMIOS(gkbd_stdin, &gkbd_oldtty) == -1) and (errno == EINTR))
146     ;
147 
148   if(gkbd_tty_open) {
149     while((close(gkbd_stdin) == -1) and (errno == EINTR))
150       ;
151     gkbd_tty_open = false;
152     gkbd_stdin = -1;
153   }
154 
155   gkbd_tty_inited = false;
156 
157   gsig_unblock_signals();
158 }
159 
160 
161 //  ------------------------------------------------------------------
162 
gkbd_sys_input_pending(int tsecs)163 int gkbd_sys_input_pending(int tsecs) {
164 
165 #if !defined(__BEOS__)
166   static fd_set read_fd_set;
167   struct timeval wait;
168   long usecs, secs;
169 
170   if(tsecs >= 0) {
171     secs = tsecs / 10;
172     usecs = (tsecs % 10) * 100000;
173   }
174   else {
175     tsecs = -tsecs;
176     secs = tsecs / 1000;
177     usecs = (tsecs % 1000) * 1000;
178   }
179 
180   wait.tv_sec = secs;
181   wait.tv_usec = usecs;
182 
183   FD_ZERO(&read_fd_set);
184   FD_SET(gkbd_stdin, &read_fd_set);
185 
186   return select(gkbd_stdin+1, &read_fd_set, NULL, NULL, &wait);
187 #else // BeOS input handling ...
188  #if defined(BEOS_BONE_BUILD)
189   static fd_set read_fd_set;
190 
191   FD_ZERO(&read_fd_set);
192   FD_SET(gkbd_stdin, &read_fd_set);
193 
194   return waiton(gkbd_stdin+1, &read_fd_set, NULL, NULL, 0);
195  #else // not a BEOS_BONE_BUILD - use classical input check scheme ...
196   struct termios term, oterm;
197   // get the terminal settings
198   tcgetattr(gkbd_stdin, &oterm);
199   // get a copy of the settings, which we modify
200   memcpy(&term, &oterm, sizeof(term));
201   // put the terminal in non-canonical mode, any
202   // reads timeout after 0.1 seconds or when a
203   // single character is read
204   term.c_lflag = term.c_lflag & (!ICANON);
205   term.c_cc[VMIN] = 0;
206   term.c_cc[VTIME] = 1;
207   tcsetattr(gkbd_stdin, TCSANOW, &term);
208   //check input
209   int bytes = -1;
210   ioctl(gkbd_stdin, TCWAITEVENT, &bytes);
211   // reset the terminal to original state
212   tcsetattr(gkbd_stdin, TCSANOW, &oterm);
213 
214   return bytes;
215  #endif //defined(BEOS_BONE_BUILD)
216 #endif
217 }
218 
219 //  ------------------------------------------------------------------
220 
gkbd_sys_getkey()221 uint gkbd_sys_getkey() {
222 
223   while(1) {
224     int ret = gkbd_sys_input_pending(100);
225     if(ret == 0)
226       continue;
227     if(ret != -1)
228       break;
229     if(errno == EINTR)
230       continue;
231     break;
232   }
233 
234   char c;
235 
236   while(read(gkbd_stdin, &c, 1) == -1) {
237     if(errno == EINTR)
238       continue;
239     if(errno == EAGAIN) {
240       sleep(1);
241       continue;
242     }
243     #ifndef __DJGPP__
244     if(errno == EWOULDBLOCK) {
245       sleep(1);
246       continue;
247     }
248     #endif
249     return (uint)-1;
250   }
251 
252   return (uint)c;
253 }
254 
255 
256 //  ------------------------------------------------------------------
257 
258 uint gkbd_input_buffer_len = 0;
259 char gkbd_input_buffer[GKBD_MAX_INPUT_BUFFER_LEN];
260 
261 
262 //  ------------------------------------------------------------------
263 
gkbd_getkey()264 uint gkbd_getkey() {
265 
266   uint imax;
267   uint ch;
268 
269   if(gkbd_input_buffer_len) {
270     ch = (uint)*gkbd_input_buffer;
271     gkbd_input_buffer_len--;
272     imax = gkbd_input_buffer_len;
273     memcpy(gkbd_input_buffer, gkbd_input_buffer+1, imax);
274   }
275   else {
276     ch = gkbd_sys_getkey();
277   }
278 
279   return ch;
280 }
281 
282 
283 //  ------------------------------------------------------------------
284 
gkbd_ungetkey_string(char * s,uint n)285 void gkbd_ungetkey_string(char *s, uint n) {
286 
287   char* bmax;
288   char* b1;
289   char* b;
290 
291   if(gkbd_input_buffer_len + n + 3 > GKBD_MAX_INPUT_BUFFER_LEN)
292     return;
293 
294   b = gkbd_input_buffer;
295   bmax = (b - 1) + gkbd_input_buffer_len;
296   b1 = bmax + n;
297   while(bmax >= b)
298     *b1-- = *bmax--;
299   bmax = b + n;
300   while(b < bmax)
301     *b++ = *s++;
302   gkbd_input_buffer_len += n;
303 }
304 
305 
306 //  ------------------------------------------------------------------
307 
gkbd_input_pending(int tsecs)308 int gkbd_input_pending(int tsecs) {
309 
310   if(gkbd_input_buffer_len)
311     return gkbd_input_buffer_len;
312 
313   int n = gkbd_sys_input_pending(tsecs);
314   if(n <= 0)
315     return 0;
316 
317   char c = (char)gkbd_getkey();
318   gkbd_ungetkey_string(&c, 1);
319 
320   return n;
321 }
322 
323 
324 //  ------------------------------------------------------------------
325 
gkbd_flush_input()326 void gkbd_flush_input() {
327 
328   gkbd_input_buffer_len = 0;
329   while(gkbd_sys_input_pending(0) > 0)
330     gkbd_sys_getkey();
331 }
332 
333 
334 //  ------------------------------------------------------------------
335 
336 struct gkbd_key_type {
337 
338   char str[7];
339   bool active;
340   uint keysym;
341   gkbd_key_type* next;
342 };
343 
344 
345 //  ------------------------------------------------------------------
346 
347 #define UPPER_CASE_KEY(x) (((x) >= 'a') and ((x) <= 'z') ? (x) - 32 : (x))
348 #define LOWER_CASE_KEY(x) (((x) >= 'A') and ((x) <= 'Z') ? (x) + 32 : (x))
349 
350 
351 //  ------------------------------------------------------------------
352 
353 gkbd_key_type* gkbd_keymap = NULL;
354 
355 
356 //  ------------------------------------------------------------------
357 
gkbd_keymap_init()358 void gkbd_keymap_init() {
359 
360   gkbd_keymap = (gkbd_key_type*)calloc(256, sizeof(gkbd_key_type));
361 }
362 
363 
364 //  ------------------------------------------------------------------
365 
gkbd_keymap_reset()366 void gkbd_keymap_reset() {
367 
368   if(gkbd_keymap) {
369     for(int n=0; n<256; n++) {
370       gkbd_key_type* next = gkbd_keymap[n].next;
371       while(next) {
372         gkbd_key_type* current = next;
373         next = current->next;
374         free(current);
375       }
376     }
377     free(gkbd_keymap);
378     gkbd_keymap = NULL;
379   }
380 }
381 
382 
383 //  ------------------------------------------------------------------
384 
malloc_key(char * str)385 static gkbd_key_type* malloc_key(char *str) {
386 
387   gkbd_key_type* key = (gkbd_key_type*)malloc(sizeof(gkbd_key_type));
388 
389   memset(key, 0, sizeof(gkbd_key_type));
390   memcpy(key->str, str, *str);
391 
392   return key;
393 }
394 
395 
396 //  ------------------------------------------------------------------
397 //  Convert things like "^A" to 1 etc... The 0th char is the strlen
398 //  INCLUDING the length character itself.
399 
gkbd_process_keystring(char * s)400 static char* gkbd_process_keystring(char* s) {
401 
402   static char str[32];
403   char ch;
404 
405   int i = 1;
406   while(*s != 0) {
407     ch = *s++;
408     if(ch == '^') {
409       ch = *s++;
410       if(ch == 0) {
411         if(i < 32)
412           str[i++] = '^';
413         break;
414       }
415       ch = UPPER_CASE_KEY(ch);
416       if(ch == '?')
417         ch = 127;
418       else
419         ch = ch - 'A' + 1;
420     }
421 
422     if(i >= 32)
423       break;
424     str[i++] = ch;
425   }
426   str[0] = i;
427 
428   return str;
429 }
430 
431 
432 //  ------------------------------------------------------------------
433 
key_string_compare(char * a,char * b,uint len)434 static int key_string_compare(char* a, char* b, uint len) {
435 
436   char* amax = a + len;
437 
438   while(a < amax) {
439 
440     int cha = *a++;
441     int chb = *b++;
442 
443     if(cha == chb)
444       continue;
445 
446     int cha_up = UPPER_CASE_KEY(cha);
447     int chb_up = UPPER_CASE_KEY(chb);
448 
449     if(cha_up == chb_up) {
450       // Use case-sensitive result.
451       return cha - chb;
452     }
453     // Use case-insensitive result.
454     return cha_up - chb_up;
455   }
456 
457   return 0;
458 }
459 
460 
461 //  ------------------------------------------------------------------
462 //  This function also performs an insertion in an ordered way.
463 
find_the_key(char * s,gkbd_key_type ** keyp)464 static int find_the_key(char* s, gkbd_key_type** keyp) {
465 
466   *keyp = NULL;
467 
468   char* str = gkbd_process_keystring(s);
469 
470   uint str_len = str[0];
471   if(str_len == 1)
472     return 0;
473 
474   char ch = str[1];
475   gkbd_key_type* key = gkbd_keymap + ch;
476 
477   if(str_len == 2) {
478     if(key->next)
479       return -2;
480 
481     key->str[0] = str_len;
482     key->str[1] = ch;
483 
484     *keyp = key;
485     return 0;
486   }
487 
488   // insert the key definition
489   while(1) {
490 
491     gkbd_key_type* last = key;
492     key = key->next;
493 
494     if(key and key->str) {
495 
496       uint key_len = key->str[0];
497       uint len = key_len;
498       if(len > str_len)
499         len = str_len;
500 
501       int cmp = key_string_compare(str+1, key->str+1, len-1);
502 
503       if(cmp > 0)
504         continue;
505 
506       if(cmp == 0) {
507         if(key_len != str_len)
508           return -2;
509 
510         *keyp = key;
511 
512         return 0;
513       }
514       // Drop to cmp < 0 case
515     }
516 
517     gkbd_key_type* neew = malloc_key(str);
518 
519     neew->next = key;
520     last->next = neew;
521 
522     *keyp = neew;
523 
524     return 0;
525   }
526 }
527 
528 
529 //  ------------------------------------------------------------------
530 
gkbd_define_keysym(char * s,uint keysym)531 int gkbd_define_keysym(char* s, uint keysym) {
532 
533   gkbd_key_type* key;
534 
535   int ret = find_the_key(s, &key);
536 
537   if((ret != 0) or (key == NULL))
538     return ret;
539 
540   key->active = true;
541   key->keysym = keysym;
542 
543   return 0;
544 }
545 
546 
547 //  ------------------------------------------------------------------
548 
549 uint gkbd_last_key_char = 0;
550 
gkbd_do_key()551 static gkbd_key_type* gkbd_do_key() {
552 
553   gkbd_last_key_char = gkbd_getkey();
554 
555   if(gkbd_last_key_char == (uint)-1)
556     return NULL;
557 
558   char input_ch = (char)gkbd_last_key_char;
559 
560   gkbd_key_type* key = gkbd_keymap + input_ch;
561 
562   // if the next one is null, then we know this MAY be it.
563   while(key->next == NULL) {
564 
565     if(key->active)
566       return key;
567 
568     // Try its opposite case counterpart
569     char chlow = LOWER_CASE_KEY(input_ch);
570     if(input_ch == chlow)
571       input_ch = UPPER_CASE_KEY(input_ch);
572 
573     key = gkbd_keymap + input_ch;
574     if(not key->active)
575       return NULL;
576   }
577 
578   if(gkbd_input_pending(1) == 0)
579     if(key->active)
580       return key;
581 
582   // It appears to be a prefix character in a key sequence.
583 
584   uint len = 1;                 // already read one character
585   key = key->next;              // Now we are in the key list
586   gkbd_key_type* kmax = NULL;   // set to end of list
587   gkbd_key_type* next;
588 
589   while(1) {
590 
591     gkbd_last_key_char = gkbd_getkey();
592 
593     len++;
594 
595     if(gkbd_last_key_char == (uint)-1)
596       break;
597 
598     input_ch = (char)gkbd_last_key_char;
599 
600     char key_ch = 0;
601     char chup = UPPER_CASE_KEY(input_ch);
602 
603     while(key != kmax) {
604       if(key->str[0] > len) {
605         key_ch = key->str[len];
606         if(chup == UPPER_CASE_KEY(key_ch))
607           break;
608       }
609       key = key->next;
610     }
611 
612     if(key == kmax)
613       break;
614 
615     // If the input character is lowercase, check to see if there is
616     // a lowercase match.  If so, set key to it.  Note: the
617     // algorithm assumes the sorting performed by key_string_compare.
618 
619     if(input_ch != key_ch) {
620       next = key->next;
621       while(next != kmax) {
622         if(next->str[0] > len) {
623           char next_ch = next->str[len];
624           if(next_ch == input_ch) {
625             key = next;
626             break;
627           }
628           if(next_ch != chup)
629             break;
630         }
631         next = next->next;
632       }
633     }
634 
635     // Ok, we found the first position of a possible match.  If it
636     // is exact, we are done.
637 
638     if(key->str[0] == len + 1)
639       return key;
640 
641     // Apparently, there are some ambiguities. Read next key to
642     // resolve the ambiguity.  Adjust kmax to encompass ambiguities.
643 
644     next = key->next;
645     while(next != kmax) {
646       if(next->str[0] > len) {
647         char key_ch = next->str[len];
648         if(chup != UPPER_CASE_KEY(key_ch))
649           break;
650       }
651       next = next->next;
652     }
653     kmax = next;
654   }
655 
656   return NULL;
657 }
658 
659 
660 //  ------------------------------------------------------------------
661 
gkbd_getmappedkey()662 uint gkbd_getmappedkey() {
663 
664   gkbd_key_type* key = gkbd_do_key();
665   if((key == NULL) or (not key->active)) {
666     gkbd_flush_input();
667     return (uint)-1;
668   }
669 
670   return key->keysym;
671 }
672 
673 
674 //  ------------------------------------------------------------------
675