]>
Commit | Line | Data |
---|---|---|
3bd189cb JR |
1 | /* ------------------------------------------------------------------------ */ |
2 | /* You have just entered the world of inp.c */ | |
3 | /* In this file we will strive for excellence in keyboard input. That is */ | |
4 | /* all this file is for. I have adopted some ideas from the makers of IRCII */ | |
5 | /* ------------------------------------------------------------------------ */ | |
6 | /* All rights reserved. Do not distribute this code without permission of */ | |
7 | /* the programmer(s). (Douglas Lewis) */ | |
8 | /* ------------------------------------------------------------------------ */ | |
9 | #define NEW_INP | |
10 | ||
11 | #include <stdio.h> | |
12 | #include <ssdef.h> | |
13 | #include <descrip.h> | |
14 | #ifdef __GNUC__ | |
15 | #define PSL$C_USER 3 | |
16 | ||
17 | #define SMG$K_TRM_UP 274 | |
18 | #define SMG$K_TRM_DOWN 275 | |
19 | #define SMG$K_TRM_LEFT 276 | |
20 | #define SMG$K_TRM_RIGHT 277 | |
21 | #else | |
22 | #include <smgdef.h> | |
23 | #include <psldef.h> | |
24 | #endif | |
25 | #include <iodef.h> | |
26 | #include <ttdef.h> | |
27 | #include <tt2def.h> | |
28 | #include <ssdef.h> | |
29 | #include <string.h> | |
30 | #include <stdlib.h> | |
31 | #include <time.h> | |
32 | #include <ctype.h> | |
33 | #include <lib$routines.h> | |
34 | ||
35 | #include "base_includes.h" | |
36 | #include "binding.h" | |
37 | #include "qio.h" | |
38 | #include "list.h" | |
39 | #include "inp.h" | |
40 | #include "system.h" | |
41 | ||
42 | extern server_ptr gsrv; | |
43 | extern int DUMB; | |
44 | ||
45 | #define NUMKEYS 26 /* Number of special keys defined. */ | |
46 | ||
47 | static unsigned in_chan = 0; | |
48 | ||
49 | #define INPUT_QIO 2 | |
50 | #define INPUT_GETS 1 | |
51 | #define INPUT_NONE 0 | |
52 | int INPUT_DEFINED = INPUT_QIO; | |
53 | ||
54 | static unsigned long keybd_efn; | |
55 | static unsigned char new_keyget(void); | |
56 | unsigned char (*input_routine)(void) = new_keyget; | |
57 | int last_keyhit; | |
58 | static int dont_restart_qio = 1; | |
59 | ||
60 | static struct iosb_type ast_iosb; | |
61 | static char inp_ast, buff_ast[256]; | |
62 | static int buff_left = 0, buff_start = 0, buff_curr = 0; | |
63 | ||
64 | /* ------------------------------------------------------------------------ */ | |
65 | static void ctrlc_ast(void) | |
66 | { | |
67 | yell("received a control-c"); | |
68 | tcp_connect_cancel(); | |
69 | } | |
70 | ||
71 | /* ------------------------------------------------------------------------ */ | |
72 | static void new_keyget_ast(void) | |
73 | { | |
74 | unsigned status; | |
75 | if (!(ast_iosb.status & 1)) { | |
76 | /* 2104 is a data overrun */ | |
77 | if (ast_iosb.status && ast_iosb.status != 2104) { | |
78 | if (dont_restart_qio) | |
79 | { | |
80 | /* abort - called by spawn */ | |
81 | if (ast_iosb.status != 44) | |
82 | my_sig(ast_iosb.status, "keyget not restarting"); | |
83 | return; | |
84 | } | |
85 | } | |
86 | } else { | |
87 | last_keyhit = time(NULL); | |
88 | if (inp_ast==3) ctrlc_ast(); | |
89 | if (buff_start==256) buff_start = 0; | |
90 | if (buff_left==256) | |
91 | yell("input buffer is full"); | |
92 | else { | |
93 | buff_ast[buff_start++] = inp_ast; | |
94 | buff_left++; | |
95 | status = sys$setef(KEYGET_EF); | |
96 | } | |
97 | } | |
98 | status = sys$qio(keybd_efn, in_chan, IO$_READVBLK|IO$M_NOECHO, &ast_iosb, | |
99 | new_keyget_ast, 0, &inp_ast, sizeof(inp_ast), 0, | |
100 | 0, 0, 0); | |
101 | } | |
102 | /* ------------------------------------------------------------------------ */ | |
103 | static void start_keyget_ast(void) | |
104 | { | |
105 | unsigned status, loop; | |
106 | ||
107 | buff_curr = buff_left = buff_start = 0; | |
108 | status = sys$clref(KEYGET_EF); | |
109 | status = sys$qio(keybd_efn, in_chan, IO$_READVBLK|IO$M_NOECHO, &ast_iosb, | |
110 | new_keyget_ast, 0, &inp_ast, sizeof(inp_ast), 0, 0, 0, 0); | |
111 | } | |
112 | /* ------------------------------------------------------------------------ */ | |
113 | static unsigned char new_keyget(void) | |
114 | { | |
115 | extern void wait_routine(int); | |
116 | int old; | |
117 | old = sys$setast(0); | |
118 | if (!buff_left) { | |
119 | if (old==SS$_WASSET) sys$setast(1); | |
120 | wait_routine(KEYGET_EF); | |
121 | old = sys$setast(0); | |
122 | } | |
123 | if (buff_curr==256) buff_curr = 0; | |
124 | buff_left--; | |
125 | if (!buff_left) sys$clref(KEYGET_EF); | |
126 | if (old==SS$_WASSET) sys$setast(1); | |
127 | return buff_ast[buff_curr++]; | |
128 | } | |
129 | /* ------------------------------------------------------------------------ */ | |
130 | /* ------------------------------------------------------------------------ */ | |
131 | /* Initialize the keyboard information. */ | |
132 | /* ------------------------------------------------------------------------ */ | |
133 | ||
134 | struct devchars { | |
135 | char class; | |
136 | char ttype; | |
137 | unsigned short width; | |
138 | unsigned long basic; | |
139 | union tt2def exten; | |
140 | }; | |
141 | ||
142 | static int oldpasthru = -1; | |
143 | ||
144 | void set_pasthru(int reset) | |
145 | { | |
146 | int status; | |
147 | struct devchars dc; | |
148 | struct iosb_type iosb; | |
149 | ||
150 | if (in_chan==0) return; | |
151 | status = sys$qiow(0, in_chan, IO$_SENSEMODE, &iosb, 0, 0, | |
152 | &dc, sizeof(dc),0,0,0,0); | |
153 | if (!ODD(status) || !ODD(iosb.status)) | |
154 | yell("Error in sensemode pasthru: %d", ODD(status)?iosb.status:status); | |
155 | ||
156 | #ifdef __GNUC__ | |
157 | if (reset) { | |
158 | if (oldpasthru != -1) | |
159 | dc.exten.tt2$r_tt2def_bits.tt2$v_pasthru = oldpasthru; | |
160 | } else { | |
161 | if (oldpasthru == -1) oldpasthru = dc.exten.tt2$r_tt2def_bits.tt2$v_pasthru; | |
162 | dc.exten.tt2$r_tt2def_bits.tt2$v_pasthru = 1; /**/ | |
163 | } | |
164 | #else | |
165 | if (reset) { | |
166 | if (oldpasthru != -1) dc.exten.tt2$v_pasthru = oldpasthru; | |
167 | } else { | |
168 | if (oldpasthru == -1) oldpasthru = dc.exten.tt2$v_pasthru; | |
169 | dc.exten.tt2$v_pasthru = 1; /**/ | |
170 | } | |
171 | #endif | |
172 | status = sys$qiow(0, in_chan, IO$_SETMODE, &iosb, 0, 0, | |
173 | &dc, sizeof(dc),0,0,0,0); | |
174 | if (!ODD(status) || !ODD(iosb.status)) | |
175 | yell("Error in setmode pasthru: %d", ODD(status)?iosb.status:status); | |
176 | } | |
177 | ||
178 | void stop_input(void) | |
179 | { | |
180 | if (INPUT_DEFINED!=INPUT_QIO) return; | |
181 | dont_restart_qio = TRUE; | |
182 | sys$cancel(in_chan); | |
183 | } | |
184 | void cleanup_input(void) | |
185 | { | |
186 | int loop; | |
187 | unsigned status; | |
188 | if (INPUT_DEFINED!=INPUT_QIO) return; | |
189 | set_pasthru(TRUE); | |
190 | } | |
191 | ||
192 | void init_keybdio(void) | |
193 | { | |
194 | unsigned status, loop; | |
195 | char input[10] = "SYS$INPUT"; | |
196 | $DESCRIPTOR(input_d, "TT"); | |
197 | unsigned prcpri; | |
198 | struct devchars dc; | |
199 | struct iosb_type iosb; | |
200 | ||
201 | last_keyhit = time(NULL); | |
202 | if (DUMB==1) INPUT_DEFINED = INPUT_GETS; | |
203 | if (DUMB==2) INPUT_DEFINED = INPUT_NONE; | |
204 | if (INPUT_DEFINED!=INPUT_QIO) return; | |
205 | status = sys$setpri(0, 0, 4, &prcpri); | |
206 | if (!ODD(status)) my_sig(status, "keybdio"); | |
207 | ||
208 | status = sys$assign(&input_d, &in_chan, PSL$C_USER, 0, 0); | |
209 | if (!ODD(status)) my_sig(status, "keybdio2"); | |
210 | ||
211 | status = sys$qiow(0, in_chan, IO$_SENSEMODE, &iosb, 0, 0, | |
212 | &dc, sizeof(dc),0,0,0,0); | |
213 | if (!ODD(status) || !ODD(iosb.status)) | |
214 | say("Error in sensemode2 pasthru: %d", ODD(status)?iosb.status:status); | |
215 | if (!((dc.ttype == TT$_VT100) || (dc.ttype == TT$_VT200_SERIES) || | |
216 | (dc.ttype == TT$_VT300_SERIES) || (dc.ttype == TT$_VT102))) { | |
217 | (void)printf("Please use a VT100/VT200/VT300 compatible terminal.\n"); | |
218 | exit(0); | |
219 | } | |
220 | ||
221 | set_pasthru(FALSE); | |
222 | lib$get_ef(&keybd_efn); | |
223 | } | |
224 | ||
225 | void start_keybdio(void) | |
226 | { | |
227 | if (dont_restart_qio == FALSE) return; | |
228 | dont_restart_qio = FALSE; | |
229 | start_keyget_ast(); | |
230 | } | |
231 | ||
232 | /* ------------------------------------------------------------------------ */ | |
233 | /* Offset is used to determine when the input buffer should be "scrolled" */ | |
234 | /* to the next line of input. When the current screen position falls out of */ | |
235 | /* the range, the input buffer will be scrolled. I.E. (curr<OFFSET or */ | |
236 | /* Curr> (screen_width-OFFSET)) */ | |
237 | /* ------------------------------------------------------------------------ */ | |
238 | ||
239 | #define OFFSET 10 | |
240 | ||
241 | /* ------------------------------------------------------------------------ */ | |
242 | /* Global variables used to handle the input line. Determines cursor */ | |
243 | /* position, the "visible window", line length, actual position in the */ | |
244 | /* string and various limits. */ | |
245 | /* ------------------------------------------------------------------------ */ | |
246 | ||
247 | static int wstart = OFFSET, wcurs=0, wend, line_len = 0; | |
248 | static int actual = 0; | |
249 | static int pl = 0, max; | |
250 | static char *work = NULL; | |
251 | static int screen_width = 80; | |
252 | static int global_maxlen; | |
253 | static char prompt[80]; | |
254 | ||
255 | /* ------------------------------------------------------------------------ */ | |
256 | /* Check to see if the position where we are in the input string is actual- */ | |
257 | /* ly in the current window. If it is not, scroll the window to get it in. */ | |
258 | /* If we had to scroll, or all is true, then reprint the input line. */ | |
259 | /* ------------------------------------------------------------------------ */ | |
260 | ||
261 | static void check_input(int all) | |
262 | { | |
263 | int oldstart, tot, old; | |
264 | ||
265 | if (INPUT_DEFINED != INPUT_QIO) return; | |
266 | old = sys$setast(0); | |
267 | oldstart = wstart; | |
268 | wend = wstart + screen_width- 2 * OFFSET; | |
269 | ||
270 | while ((actual < wstart) && (wstart > OFFSET)) | |
271 | { | |
272 | wend = wstart; | |
273 | wstart = wstart - max; | |
274 | } | |
275 | while (actual >= wend) | |
276 | { | |
277 | wstart = wend; | |
278 | wend = wend + max; | |
279 | } | |
280 | wcurs = actual-wstart+OFFSET+1; | |
281 | if ((tot = (line_len-wstart+OFFSET)) > screen_width) | |
282 | tot = screen_width; | |
283 | if ((wstart!=oldstart) || (all)) { | |
284 | (void)print_at(&work[wstart-OFFSET], tot, pl); | |
285 | } | |
286 | (void)putcursor(wcurs); | |
287 | if (old==SS$_WASSET) (void)sys$setast(1); | |
288 | } | |
289 | ||
290 | static char prompt_format[80] = ""; | |
291 | ||
292 | /* ----------------------------------------------------------------------- */ | |
293 | /* "Rebuild" the prompt and compare it to the old one. If there is a */ | |
294 | /* change, rewrite the input line to correct for a different prompt. */ | |
295 | /* flag is FALSE if is the begginning of a new input line, else it is TRUE */ | |
296 | /* ----------------------------------------------------------------------- */ | |
297 | ||
298 | void find_input_prompt(flag) | |
299 | int flag; | |
300 | { | |
301 | char *temp; | |
302 | int temppl, loop, num = 0, added; | |
303 | ||
304 | if (INPUT_DEFINED != INPUT_QIO) return; | |
305 | expand_alias(prompt_format, "", &added, prompt, sizeof(prompt)); | |
306 | temppl = strlen(prompt); | |
307 | ||
308 | /* Check if the prompt has changed */ | |
309 | if (flag || (strncasecmp(prompt, work, temppl) != 0) || (temppl!=pl)) | |
310 | { | |
311 | temp = (char *)mymalloc(global_maxlen + temppl + 1); | |
312 | (void)strcpy(temp, prompt); | |
313 | ||
314 | if (line_len == 0) num = 0; | |
315 | else num = line_len - pl; | |
316 | ||
317 | for (loop=0; loop<num; loop++) temp[temppl+loop] = work[pl+loop]; | |
318 | ||
319 | line_len = num + temppl; | |
320 | if (!actual) actual = temppl; | |
321 | else actual = actual + temppl - pl; | |
322 | ||
323 | pl = temppl; | |
324 | if (work) myfree(work); | |
325 | /* yell("printing [%s] at len 0, pl %d", temp, pl); */ | |
326 | screen_width = print_at(temp, 0, pl); /* Was line_len */ | |
327 | work = temp; | |
328 | wstart = OFFSET; | |
329 | wend = wstart + screen_width- 2 * OFFSET; | |
330 | max = screen_width - 2 * OFFSET; | |
331 | check_input(TRUE); | |
332 | } | |
333 | } | |
334 | ||
335 | /* ----------------------------------------------------------------------- */ | |
336 | /* Go left a character in the input string. */ | |
337 | /* ----------------------------------------------------------------------- */ | |
338 | void inp_backward_character(void) | |
339 | { | |
340 | if (actual > pl) { | |
341 | actual--; | |
342 | wcurs--; | |
343 | (void)putcursor(wcurs); | |
344 | } | |
345 | } | |
346 | ||
347 | static void inp_history_line(int flag) | |
348 | { | |
349 | char *str; | |
350 | str = get_prev_input(flag, TRUE); | |
351 | if (!str) return; | |
352 | (void)sprintf(work+pl, "%s\0", str); | |
353 | line_len = pl+strlen(str); | |
354 | wcurs = line_len; | |
355 | actual = line_len; | |
356 | check_input(TRUE); | |
357 | } | |
358 | ||
359 | void inp_forward_history(void) | |
360 | { inp_history_line(0); } | |
361 | ||
362 | void inp_backward_history(void) | |
363 | { inp_history_line(1); } | |
364 | ||
365 | void inp_beginning_of_line(void) | |
366 | { | |
367 | actual = pl; | |
368 | check_input(TRUE); | |
369 | } | |
370 | ||
371 | void inp_forward_character(void) | |
372 | { | |
373 | if (actual < line_len) { | |
374 | actual++; | |
375 | wcurs++; | |
376 | (void)putcursor(wcurs); | |
377 | } | |
378 | } | |
379 | ||
380 | int inp_backspace(void) | |
381 | { | |
382 | int loop; | |
383 | if (actual > pl) { /* Are we at the start of the line when we delete */ | |
384 | for (loop=actual-1; loop<line_len; loop++) | |
385 | work[loop] = work[loop+1]; | |
386 | actual--; line_len--; wcurs--; | |
387 | (void)del_char_from_input(wcurs, 1); | |
388 | } | |
389 | return 1; | |
390 | } | |
391 | ||
392 | int inp_toggle_insert_mode(void) | |
393 | { setivar(VAR_INSERT_MODE, !getivar(VAR_INSERT_MODE)); return 1; } | |
394 | ||
395 | void inp_insert_char(char key) | |
396 | { | |
397 | int loop; | |
398 | if (getivar(VAR_INSERT_MODE)) { | |
399 | if ((line_len-pl) < global_maxlen) { | |
400 | for (loop=line_len; (loop>actual); loop--) | |
401 | work[loop] = work[loop-1]; | |
402 | line_len++; | |
403 | (void)insert_char((char)key, wcurs); | |
404 | work[actual++] = (char)key; wcurs++; | |
405 | } | |
406 | } else { | |
407 | if ((actual-pl) < global_maxlen) { | |
408 | (void)rep_char_in_input((char)key, wcurs); | |
409 | if (line_len==actual) line_len++; | |
410 | work[actual++] = (char)key; wcurs++; | |
411 | } | |
412 | } | |
413 | } | |
414 | ||
415 | int inp_eol(void) | |
416 | { | |
417 | actual = line_len; | |
418 | wcurs = line_len; | |
419 | (void)putcursor(wcurs+pl); | |
420 | return 1; | |
421 | } | |
422 | ||
423 | static int global_done = FALSE; | |
424 | int inp_send_line(void) | |
425 | { | |
426 | global_done = TRUE; | |
427 | return 1; | |
428 | } | |
429 | ||
430 | int inp_delete_current_char(void) | |
431 | { | |
432 | int loop; | |
433 | if (actual<line_len) { | |
434 | line_len--; | |
435 | for (loop=actual; loop<line_len; loop++) | |
436 | work[loop] = work[loop+1]; | |
437 | (void)del_char_from_input(wcurs, 1); | |
438 | } | |
439 | return 1; | |
440 | } | |
441 | ||
442 | int inp_delete_to_bol(void) | |
443 | { | |
444 | int loop, count; | |
445 | for (loop=actual, count = pl; loop<line_len; loop++) | |
446 | work[count++] = work[loop]; | |
447 | line_len=(line_len-actual+pl); | |
448 | wcurs = pl; actual = pl; | |
449 | check_input(TRUE); | |
450 | return 1; | |
451 | } | |
452 | ||
453 | /* ----------------------------------------------------------------------- */ | |
454 | /* Read input from the input device and handle any characters as they come */ | |
455 | /* up. Read up to maxlen characters for the input. */ | |
456 | /* ----------------------------------------------------------------------- */ | |
457 | ||
458 | static char *input_line; | |
459 | ||
460 | static void handle_key(unsigned char real) | |
461 | { | |
462 | int key, done, num = 0, ac_loop, meta_key = MAX_META; | |
463 | u_char saved_bind[10]; /* Assume there are no sequences over 10 characters */ | |
464 | ||
465 | key = real; | |
466 | if (key<128) | |
467 | done = (int)(*META)[meta_key][key].func_bind >= 0; | |
468 | while (!done) { | |
469 | saved_bind[num++] = key; | |
470 | meta_key = (int)(*META)[meta_key][key].func_bind; | |
471 | if (meta_key<0) { | |
472 | key = input_routine(); | |
473 | if (key>127) done = TRUE; | |
474 | meta_key += MAX_META; | |
475 | if (!done) done = (int)(*META)[meta_key][key].func_bind >= 0; | |
476 | } else done = TRUE; | |
477 | } | |
478 | ||
479 | if (key<128) | |
480 | if ((int)(*META)[meta_key][key].func_bind>0) { | |
481 | (void)(*META)[meta_key][key].func_bind((*META)[meta_key][key].param, FALSE, 0); | |
482 | return; | |
483 | } | |
484 | saved_bind[num++] = key; | |
485 | for (ac_loop=0;ac_loop<num;ac_loop++) { | |
486 | key = saved_bind[ac_loop]; | |
487 | inp_insert_char(key); | |
488 | } /* for loop */ | |
489 | } | |
490 | ||
491 | static char *dumb_input(char *input) | |
492 | { | |
493 | gets(input); | |
494 | return input; | |
495 | } | |
496 | ||
497 | char *read_input(char input[MAXLEN], int maxlen, char *pr) | |
498 | { | |
499 | int count, num, old_global_done, old_global_maxlen; | |
500 | char oldprompt[80]; | |
501 | int oldline_len; | |
502 | ||
503 | oldline_len = line_len; | |
504 | (void)strcpy(oldprompt, prompt_format); | |
505 | if (pr) | |
506 | (void)strcpy(prompt_format, pr); | |
507 | else | |
508 | (void)strcpy(prompt_format, getvar(VAR_INPUT_PROMPT)); | |
509 | old_global_maxlen = global_maxlen; | |
510 | global_maxlen = maxlen; | |
511 | if (INPUT_DEFINED == INPUT_GETS) return dumb_input(input); | |
512 | if (INPUT_DEFINED == INPUT_NONE) { | |
513 | yell("Oh shit, we ended up in a hiber loop!"); | |
514 | *input = 0; | |
515 | sys$hiber(); | |
516 | return NULL; | |
517 | } | |
518 | ||
519 | line_len = 0; | |
520 | actual = 0; | |
521 | ||
522 | num = sys$setast(0); | |
523 | find_input_prompt(TRUE); | |
524 | if (num == SS$_WASSET) (void)sys$setast(1); | |
525 | ||
526 | (void)putcursor(pl); | |
527 | old_global_done = global_done; | |
528 | global_done = FALSE; | |
529 | do { | |
530 | check_input(FALSE); | |
531 | handle_key(input_routine()); | |
532 | } while (!global_done); | |
533 | global_maxlen = old_global_maxlen; | |
534 | global_done = old_global_done; | |
535 | (void)strncpy(input, &work[pl], line_len-pl); | |
536 | work[pl] = 0; | |
537 | input[line_len-pl] = 0; | |
538 | line_len=pl; | |
539 | actual = line_len; | |
540 | strcpy(prompt_format, oldprompt); | |
541 | return input; | |
542 | } | |
543 | ||
544 | /* ----------------------------------------------------------------------- */ | |
545 | /* End of file. */ | |
546 | /* ----------------------------------------------------------------------- */ |