This is keys.c in view mode; [Download] [Up]
/* Terminfo database scanning and keyboard escape sequence matching functions. Copyright (C) 1993 Sebastiano Vigna This file is part of ne, the nice editor. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. In other words, you are welcome to use, share and improve this program. You are forbidden to forbid anyone else to use, share and improve what you give them. Help stamp out software-hoarding! */ #include "ne.h" #include <time.h> #include <signal.h> #include <errno.h> #include <string.h> #ifdef _AMIGA #include <proto/dos.h> #endif /* Maximum number of key definitions from terminfo */ #define MAX_TERM_KEY 100 /* Size of the keyboard input buffer. */ #define KBD_BUF_SIZE 512 /* This structure describes a key in the terminfo database. These structures are ordered with respect to the string field, in order to greatly optimize their scanning. */ typedef struct { unsigned char *string; int code; } term_key; static term_key key[MAX_TERM_KEY]; static int num_keys = 0; /* Function to pass to qsort for sorting the key capabilities array. */ static int keycmp(const void *t1, const void *t2) { return(-strcmp((char *)((term_key *)t1)->string, (char *)((term_key *)t2)->string)); } /* This function sets the first free position in the key capabilities array to the cap_string capability, and increment the first free position counter. */ static void key_set(char *cap_string, int code) { if (!cap_string) return; key[num_keys].string = (unsigned char *)cap_string; key[num_keys].code = code; num_keys++; } /* Here we scan the terminfo database and build a term_key structure for each key available. num_keys records the number of entries. The array is sorted in reverse order with respect to string field (this optimizes the comparisons, assuming that usually almost all control sequences start with a character smaller than ' ', while the characters typed by the user are almost always greater than or equal to ' '). */ void read_key_capabilities(void) { /* Cursor movement keys */ key_set(key_up, NE_KEY_UP); key_set(key_down, NE_KEY_DOWN); key_set(key_left, NE_KEY_LEFT); key_set(key_right, NE_KEY_RIGHT); key_set(key_home, NE_KEY_HOME); key_set(key_ll, NE_KEY_HOME_DOWN); key_set(key_npage, NE_KEY_NPAGE); key_set(key_ppage, NE_KEY_PPAGE); key_set(key_sf, NE_KEY_SCROLL_FORWARD); key_set(key_sr, NE_KEY_SCROLL_REVERSE); /* Editing keys */ key_set(key_eol, NE_KEY_CLEAR_TO_EOL); key_set(key_eos, NE_KEY_CLEAR_TO_EOS); key_set(key_backspace, NE_KEY_BACKSPACE); key_set(key_dl, NE_KEY_DELETE_LINE); key_set(key_il, NE_KEY_INSERT_LINE); key_set(key_dc, NE_KEY_DELETE_CHAR); key_set(key_ic, NE_KEY_INSERT_CHAR); key_set(key_eic, NE_KEY_EXIT_INSERT_CHAR); key_set(key_clear, NE_KEY_CLEAR); /* Keypad keys */ key_set(key_a1, NE_KEY_A1); key_set(key_a3, NE_KEY_A3); key_set(key_b2, NE_KEY_B2); key_set(key_c1, NE_KEY_C1); key_set(key_c3, NE_KEY_C3); /* Tab keys (never used in the standard configuration) */ key_set(key_catab, NE_KEY_CLEAR_ALL_TABS); key_set(key_ctab, NE_KEY_CLEAR_TAB); key_set(key_stab, NE_KEY_SET_TAB); /* Function keys */ key_set(key_f0, NE_KEY_F(0)); key_set(key_f1, NE_KEY_F(1)); key_set(key_f2, NE_KEY_F(2)); key_set(key_f3, NE_KEY_F(3)); key_set(key_f4, NE_KEY_F(4)); key_set(key_f5, NE_KEY_F(5)); key_set(key_f6, NE_KEY_F(6)); key_set(key_f7, NE_KEY_F(7)); key_set(key_f8, NE_KEY_F(8)); key_set(key_f9, NE_KEY_F(9)); key_set(key_f10, NE_KEY_F(10)); key_set(key_f11, NE_KEY_F(11)); key_set(key_f12, NE_KEY_F(12)); key_set(key_f13, NE_KEY_F(13)); key_set(key_f14, NE_KEY_F(14)); key_set(key_f15, NE_KEY_F(15)); key_set(key_f16, NE_KEY_F(16)); key_set(key_f17, NE_KEY_F(17)); key_set(key_f18, NE_KEY_F(18)); key_set(key_f19, NE_KEY_F(19)); key_set(key_f20, NE_KEY_F(20)); key_set(key_f21, NE_KEY_F(21)); key_set(key_f22, NE_KEY_F(22)); key_set(key_f23, NE_KEY_F(23)); key_set(key_f24, NE_KEY_F(24)); key_set(key_f25, NE_KEY_F(25)); key_set(key_f26, NE_KEY_F(26)); key_set(key_f27, NE_KEY_F(27)); key_set(key_f28, NE_KEY_F(28)); key_set(key_f29, NE_KEY_F(29)); key_set(key_f30, NE_KEY_F(30)); key_set(key_f31, NE_KEY_F(31)); key_set(key_f32, NE_KEY_F(32)); key_set(key_f33, NE_KEY_F(33)); key_set(key_f34, NE_KEY_F(34)); key_set(key_f35, NE_KEY_F(35)); key_set(key_f36, NE_KEY_F(36)); key_set(key_f37, NE_KEY_F(37)); key_set(key_f38, NE_KEY_F(38)); key_set(key_f39, NE_KEY_F(39)); key_set(key_f40, NE_KEY_F(40)); key_set(key_f41, NE_KEY_F(41)); key_set(key_f42, NE_KEY_F(42)); key_set(key_f43, NE_KEY_F(43)); key_set(key_f44, NE_KEY_F(44)); key_set(key_f45, NE_KEY_F(45)); key_set(key_f46, NE_KEY_F(46)); key_set(key_f47, NE_KEY_F(47)); key_set(key_f48, NE_KEY_F(48)); key_set(key_f49, NE_KEY_F(49)); key_set(key_f50, NE_KEY_F(50)); key_set(key_f51, NE_KEY_F(51)); key_set(key_f52, NE_KEY_F(52)); key_set(key_f53, NE_KEY_F(53)); key_set(key_f54, NE_KEY_F(54)); key_set(key_f55, NE_KEY_F(55)); key_set(key_f56, NE_KEY_F(56)); key_set(key_f57, NE_KEY_F(57)); key_set(key_f58, NE_KEY_F(58)); key_set(key_f59, NE_KEY_F(59)); key_set(key_f60, NE_KEY_F(60)); key_set(key_f61, NE_KEY_F(61)); key_set(key_f62, NE_KEY_F(62)); key_set(key_f63, NE_KEY_F(63)); /* Fake (simulated) command key. */ key_set("\x1B:", NE_KEY_COMMAND); assert(num_keys<MAX_TERM_KEY-1); D(printf("Got %d keys from terminfo\n\r", num_keys);) qsort(key, num_keys, sizeof(term_key), keycmp); } /* This function sets the escape time, which is an option, but it's global to ne and it's not saved in autopreferences files. However, an EscapeTime command can be attached manually to any preferences file. */ static escape_time = 10; void set_escape_time(int new_escape_time) { escape_time = new_escape_time; } /* This function sets the current timeout in the termios structure relative to stdin. If the timeout value (in tenth of a second) is positive, VMIN is set to 0, otherwise to 1. */ #ifndef _AMIGA static void set_termios_timeout(int timeout) { struct termios termios; tcgetattr(0, &termios); termios.c_cc[VTIME] = timeout; termios.c_cc[VMIN] = timeout ? 0 : 1; tcsetattr(0, TCSANOW, &termios); } #endif /* This function reads in characters, and tries to match them with the sequences corresponding to special keys. It tries to be highly optimized and efficient by employing a sorted array of strings for the terminal keys. An index keeps track of the key which has a partial match with the current contents of the keyboard buffer. As each character is input, a match is tried with the rest of the string. If a new character does not match, we can just increment the key counter (because the array is sorted). When we get out of the array, we give back the first char in the keyboard buffer (the next call will retry a match on the following chars). */ int get_key_code(void) { static int cur_len = 0; static unsigned char kbd_buffer[KBD_BUF_SIZE]; int c, last_match = 0, cur_key = 0, partial_match = FALSE; while(TRUE) { if (cur_len) { /* Something is already in the buffer. last_match is the position we have to check. */ while(last_match < cur_len) { /* First easy case. We felt off the array. We return the first character in the buffer and restart the match. */ if (!key[cur_key].string) { c = kbd_buffer[0]; if (--cur_len) memmove(kbd_buffer, kbd_buffer+1, cur_len); return(c); } /* Second case. We have a partial match on the first last_match characters. If another character matches, either the string is terminated, and we return the key code, or we increment the match count. */ else if (key[cur_key].string[last_match] == kbd_buffer[last_match]) { if (key[cur_key].string[last_match+1] == 0) { if (cur_len -= last_match+1) memmove(kbd_buffer, kbd_buffer+last_match+1, cur_len); assert(key[cur_key].code < NUM_KEYS); return(key[cur_key].code); } else last_match++; } /* The tricky part. If there is a failed match, the order guarantees that no match if possible if the code of the keyboard char is greater than the code of the capability char. Otherwise, we check for the first capability starting with the current keyboard characters. */ else { if (kbd_buffer[last_match] > key[cur_key].string[last_match]) { c = kbd_buffer[0]; if (--cur_len) memmove(kbd_buffer, kbd_buffer+1, cur_len); return(c); } else { last_match = 0; cur_key++; } } } /* If we have a partial match, let's look at stdin for escape_time tenths of second. If nothing arrives, it is probably time to return what we got. Note that this won't work properly if the terminal has a key capability which is a prefix of another key capability. */ partial_match = TRUE; } fflush(stdout); #ifdef _AMIGA if (!partial_match || WaitForChar(Input(), 100000*escape_time)) c = getchar(); else c = EOF; #else if (partial_match) set_termios_timeout(escape_time); errno = 0; c = getchar(); if (c == EOF && (!partial_match || errno) && errno != EINTR) kill(getpid(), SIGTERM); if (partial_match) set_termios_timeout(0); #endif partial_match = FALSE; if (c != EOF) { if (cur_len < KBD_BUF_SIZE) kbd_buffer[cur_len++] = c; } else { if (cur_len) { /* We ran out of time. The current partial match is discarded, and the first character of the keyboard buffer is returned. */ c = kbd_buffer[0]; if (--cur_len) memmove(kbd_buffer, kbd_buffer+1, cur_len); return(c); } } } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.