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