* Deactivate some color code from Pico (as standalone editor) until
[alpine.git] / alpine / osdep / termin.gen.c
blob0103e36463e39391b7cd85b5f933134f56c5cc0a
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: termin.gen.c 1025 2008-04-08 22:59:38Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2017 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
18 #include <system.h>
19 #include <general.h>
21 #include "../../c-client/mail.h" /* for MAILSTREAM and friends */
22 #include "../../c-client/osdep.h"
23 #include "../../c-client/rfc822.h" /* for soutr_t and such */
24 #include "../../c-client/misc.h" /* for cpystr proto */
25 #include "../../c-client/utf8.h" /* for CHARSET and such*/
26 #include "../../c-client/imap4r1.h"
28 #include "../../pith/osdep/color.h"
30 #include "../../pith/charconv/utf8.h"
32 #include "../../pith/debug.h"
33 #include "../../pith/newmail.h"
34 #include "../../pith/conf.h"
35 #include "../../pith/busy.h"
37 #include "../../pico/estruct.h"
38 #include "../../pico/pico.h"
39 #include "../../pico/keydefs.h"
41 #include "../../pico/osdep/color.h"
43 #include "../status.h"
44 #include "../folder.h"
45 #include "../keymenu.h"
46 #include "../send.h"
47 #include "../radio.h"
48 #include "../busy.h"
50 #ifdef _WINDOWS
51 #include "../../pico/osdep/mswin.h"
52 #include "termin.wnt.h"
53 #include "termout.wnt.h"
54 #else
55 #include "termout.unx.h"
56 #endif
58 #include "termin.gen.h"
59 #include "termout.gen.h"
61 #include "../mailcmd.h"
64 #ifdef _WINDOWS
65 static int g_mc_row, g_mc_col;
67 int pcpine_oe_cursor(int, long);
68 #endif
72 * Generic tty input routines
76 /*----------------------------------------------------------------------
77 Read a character from keyboard with timeout
78 Input: none
80 Result: Returns command read via read_char
81 Times out and returns a null command every so often
83 Calculates the timeout for the read, and does a few other house keeping
84 things. The duration of the timeout is set in pine.c.
85 ----------------------------------------------------------------------*/
86 UCS
87 read_command(char **utf8str)
89 int tm = 0, more_freq_timeo;
90 UCS ucs;
91 long dtime;
92 static unsigned char utf8buf[7];
93 unsigned char *newdestp;
96 * timeo is the mail-check-interval. What we want to do (ignoring the
97 * messages_queued part) is timeout more often than timeo but only
98 * check for new mail every timeo or so seconds. The reason we want to
99 * timeout more often is so that we will have a chance to catch the user
100 * in an idle period where we can do a check_point(). Otherwise, with
101 * a default mail-check-interval, we are almost always calling newmail
102 * right after the user presses a key, making it the worst possible
103 * time to do a checkpoint.
106 more_freq_timeo = MIN(get_input_timeout(), IDLE_TIMEOUT);
107 if(more_freq_timeo == 0)
108 more_freq_timeo = IDLE_TIMEOUT;
110 cancel_busy_cue(-1);
111 tm = (messages_queued(&dtime) > 1) ? (int)dtime : more_freq_timeo;
113 if(utf8str)
114 *utf8str = NULL;
116 ucs = read_char(tm);
117 if(ucs != NO_OP_COMMAND && ucs != NO_OP_IDLE && ucs != KEY_RESIZE)
118 zero_new_mail_count();
120 #ifdef BACKGROUND_POST
122 * Any expired children to report on?
124 if(ps_global->post && ps_global->post->pid == 0){
125 int winner = 0;
127 if(ps_global->post->status < 0){
128 q_status_message(SM_ORDER | SM_DING, 3, 3, "Abysmal failure!");
130 else{
131 (void) pine_send_status(ps_global->post->status,
132 ps_global->post->fcc, tmp_20k_buf, SIZEOF_20KBUF,
133 &winner);
134 q_status_message(SM_ORDER | (winner ? 0 : SM_DING), 3, 3,
135 tmp_20k_buf);
139 if(!winner)
140 q_status_message(SM_ORDER, 0, 3,
141 "Re-send via \"Compose\" then \"Yes\" to \"Continue INTERRUPTED?\"");
143 if(ps_global->post->fcc)
144 fs_give((void **) &ps_global->post->fcc);
146 fs_give((void **) &ps_global->post);
148 #endif
151 * The character we get from read_char() is a UCS-4 char. Or it could be a special
152 * value like KEY_UP or NO_OP_IDLE or something similar. From here on out
153 * we're going to operate with UTF-8 internally. This is the point where we
154 * convert the UCS-4 input (actually whatever sort of input the user is typing
155 * was converted to UCS-4 first) to UTF-8. It's easy in this read_command
156 * case because if user types a non-ascii character as a command it's going to be
157 * an error. All commands are ascii. In order to present a reasonable error
158 * message we pass back the UTF-8 string to the caller.
160 if(ucs >= 0x80 && ucs < KEY_BASE){
162 * User typed a character that is non-ascii. Convert it to
163 * UTF-8.
165 memset(utf8buf, 0, sizeof(utf8buf));
166 newdestp = utf8_put(utf8buf, (unsigned long) ucs);
167 if(newdestp - utf8buf > 1){ /* this should happen */
168 if(utf8str)
169 *utf8str = (char *) utf8buf;
171 dprint((9, "Read command: looks like user typed non-ascii command 0x%x %s: returning KEY_UTF8\n", ucs, pretty_command(ucs)));
172 ucs = KEY_UTF8;
174 else{
175 dprint((9, "Read command: looks like user typed unknown, non-ascii command 0x%x %s: returning KEY_UNKNOWN\n", ucs, pretty_command(ucs)));
176 ucs = KEY_UNKNOWN; /* best we can do, shouldn't happen */
179 else{
180 dprint((9, "Read command returning: 0x%x %s\n", ucs, pretty_command(ucs)));
183 return(ucs);
188 read_command_prep()
190 int i;
191 char *fname;
192 MAILSTREAM *m;
195 * Before we sniff at the input queue, make sure no external event's
196 * changed our picture of the message sequence mapping. If so,
197 * recalculate the dang thing and run thru whatever processing loop
198 * we're in again...
200 for(i = 0; i < ps_global->s_pool.nstream; i++){
201 m = ps_global->s_pool.streams[i];
202 if(m && sp_flagged(m, SP_LOCKED) && sp_flagged(m, SP_USERFLDR)
203 && sp_expunge_count(m)){
204 fname = STREAMNAME(m);
205 q_status_message3(SM_ORDER, 3, 3,
206 "%s message%s expunged from folder \"%s\"",
207 long2string(sp_expunge_count(m)),
208 plural(sp_expunge_count(m)),
209 pretty_fn(fname));
210 sp_set_expunge_count(m, 0L);
211 display_message('x');
215 if(sp_mail_box_changed(ps_global->mail_stream)
216 && sp_new_mail_count(ps_global->mail_stream)){
217 dprint((2, "Noticed %ld new msgs! \n",
218 sp_new_mail_count(ps_global->mail_stream)));
219 return(FALSE); /* cycle thru so caller can update */
222 return(TRUE);
226 /*----------------------------------------------------------------------
227 Prompt user for a string in status line with various options
229 Args: utf8string -- the buffer result is returned in, and original string (if
230 any) is passed in.
231 y_base -- y position on screen to start on. 0,0 is upper left
232 negative numbers start from bottom
233 x_base -- column position on screen to start on. 0,0 is upper left
234 utf8string_size -- Length of utf8string buffer
235 utf8prompt -- The string to prompt with
236 escape_list -- pointer to array of ESCKEY_S's. input chars matching
237 those in list return value from list.
238 help -- Array of strings for help text in bottom screen lines
239 flags -- pointer (because some are return values) to flags
240 OE_USER_MODIFIED - Set on return if user modified buffer
241 OE_DISALLOW_CANCEL - No cancel in menu.
242 OE_DISALLOW_HELP - No help in menu.
243 OE_KEEP_TRAILING_SPACE - Allow trailing space.
244 OE_SEQ_SENSITIVE - Caller is sensitive to sequence
245 number changes.
246 OE_APPEND_CURRENT - String should not be truncated
247 before accepting user input.
248 OE_PASSWD - Don't echo on screen.
250 Result: editing input string
251 returns -1 unexpected errors
252 returns 0 normal entry typed (editing and return or PF2)
253 returns 1 typed ^C or PF2 (cancel)
254 returns 3 typed ^G or PF1 (help)
255 returns 4 typed ^L for a screen redraw
257 WARNING: Care is required with regard to the escape_list processing.
258 The passed array is terminated with an entry that has ch = -1.
259 Function key labels and key strokes need to be setup externally!
260 Traditionally, a return value of 2 is used for ^T escapes.
262 Unless in escape_list, tabs are trapped by isprint().
263 This allows near full weemacs style editing in the line
264 ^A beginning of line
265 ^E End of line
266 ^R Redraw line
267 ^G Help
268 ^F forward
269 ^B backward
270 ^D delete
271 ----------------------------------------------------------------------*/
274 optionally_enter(char *utf8string, int y_base, int x_base, int utf8string_size,
275 char *utf8prompt, ESCKEY_S *escape_list, HelpType help, int *flags)
277 UCS *string = NULL, ucs;
278 size_t string_size;
279 UCS *s2;
280 UCS *saved_original = NULL;
281 char *candidate;
282 UCS *kill_buffer = NULL;
283 UCS *k, *kb;
284 int field_pos; /* offset into array dline.vl */
285 int i, j, return_v, cols, prompt_width, too_thin,
286 real_y_base, km_popped, passwd;
287 char **help_text;
288 long fkey_table[12];
289 struct key_menu *km;
290 bitmap_t bitmap;
291 COLOR_PAIR *lastc = NULL, *promptc = NULL;
292 struct variable *vars = ps_global->vars;
293 struct display_line dline;
294 #ifdef _WINDOWS
295 int cursor_shown;
296 #endif
298 dprint((5, "=== optionally_enter called ===\n"));
299 dprint((9, "utf8string:\"%s\" y:%d x:%d length: %d append: %d\n",
300 utf8string ? utf8string : "",
301 x_base, y_base, utf8string_size,
302 (flags && *flags & OE_APPEND_CURRENT)));
303 dprint((9, "passwd:%d utf8prompt:\"%s\" label:\"%s\"\n",
304 (flags && *flags & OE_PASSWD_NOAST) ? 10 :
305 (flags && *flags & OE_PASSWD) ? 1 : 0,
306 utf8prompt ? utf8prompt : "",
307 (escape_list && escape_list[0].ch != -1 && escape_list[0].label)
308 ? escape_list[0].label: ""));
310 if(!ps_global->ttyo)
311 return(pre_screen_config_opt_enter(utf8string, utf8string_size, utf8prompt,
312 escape_list, help, flags));
314 #ifdef _WINDOWS
315 if (mswin_usedialog ())
316 return(win_dialog_opt_enter(utf8string, utf8string_size, utf8prompt,
317 escape_list, help, flags));
318 #endif
322 * Utf8string comes in as UTF-8. We'll convert it to a UCS-4 array and operate on
323 * that array, then convert it back before returning. Utf8string_size is the size
324 * of the utf8string array but that doesn't help us much for the array we need to
325 * operate on here. We'll just allocate a big array and then cut it off when
326 * sending it back.
328 * This should come before the specialized calls above but those aren't
329 * converted to use UCS-4 yet.
331 string = utf8_to_ucs4_cpystr(utf8string);
332 dline.vused = ucs4_strlen(string);
334 string_size = (2 * MAX(utf8string_size,dline.vused) + 100);
335 fs_resize((void **) &string, string_size * sizeof(UCS));
337 suspend_busy_cue();
338 cols = ps_global->ttyo->screen_cols;
339 prompt_width = utf8_width(utf8prompt);
340 too_thin = 0;
341 km_popped = 0;
342 if(y_base > 0)
343 real_y_base = y_base;
344 else{
345 real_y_base = y_base + ps_global->ttyo->screen_rows;
346 real_y_base = MAX(real_y_base, 0);
349 flush_ordered_messages();
350 mark_status_dirty();
352 if(flags && *flags & OE_APPEND_CURRENT) /* save a copy in case of cancel */
353 saved_original = ucs4_cpystr(string);
356 * build the function key mapping table, skipping predefined keys...
358 memset(fkey_table, NO_OP_COMMAND, 12 * sizeof(long));
359 for(i = 0, j = 0; escape_list && escape_list[i].ch != -1 && i+j < 12; i++){
360 if(i+j == OE_HELP_KEY)
361 j++;
363 if(i+j == OE_CANCEL_KEY)
364 j++;
366 if(i+j == OE_ENTER_KEY)
367 j++;
369 fkey_table[i+j] = escape_list[i].ch;
372 /* assumption that HelpType is char ** */
373 help_text = help;
374 if(help_text){ /*---- Show help text -----*/
375 int width = ps_global->ttyo->screen_cols - x_base;
377 if(FOOTER_ROWS(ps_global) == 1){
378 km_popped++;
379 FOOTER_ROWS(ps_global) = 3;
380 clearfooter(ps_global);
382 y_base = -3;
383 real_y_base = y_base + ps_global->ttyo->screen_rows;
386 for(j = 0; j < 2 && help_text[j]; j++){
387 MoveCursor(real_y_base + 1 + j, x_base);
388 CleartoEOLN();
390 if(width < utf8_width(help_text[j])){
391 char *tmp = cpystr(help_text[j]);
392 (void) utf8_truncate(tmp, width);
393 PutLine0(real_y_base + 1 + j, x_base, tmp);
394 fs_give((void **) &tmp);
396 else
397 PutLine0(real_y_base + 1 + j, x_base, help_text[j]);
400 else{
401 clrbitmap(bitmap);
402 clrbitmap((km = &oe_keymenu)->bitmap); /* force formatting */
403 if(!(flags && (*flags) & OE_DISALLOW_HELP))
404 setbitn(OE_HELP_KEY, bitmap);
406 setbitn(OE_ENTER_KEY, bitmap);
407 if(!(flags && (*flags) & OE_DISALLOW_CANCEL))
408 setbitn(OE_CANCEL_KEY, bitmap);
410 setbitn(OE_CTRL_T_KEY, bitmap);
412 /*---- Show the usual possible keys ----*/
413 for(i=0,j=0; escape_list && escape_list[i].ch != -1 && i+j < 12; i++){
414 if(i+j == OE_HELP_KEY)
415 j++;
417 if(i+j == OE_CANCEL_KEY)
418 j++;
420 if(i+j == OE_ENTER_KEY)
421 j++;
423 oe_keymenu.keys[i+j].label = escape_list[i].label;
424 oe_keymenu.keys[i+j].name = escape_list[i].name;
425 setbitn(i+j, bitmap);
428 for(i = i+j; i < 12; i++)
429 if(!(i == OE_HELP_KEY || i == OE_ENTER_KEY || i == OE_CANCEL_KEY))
430 oe_keymenu.keys[i].name = NULL;
432 draw_keymenu(km, bitmap, cols, 1-FOOTER_ROWS(ps_global), 0, FirstMenu);
435 if(pico_usingcolor() && VAR_PROMPT_FORE_COLOR &&
436 VAR_PROMPT_BACK_COLOR &&
437 pico_is_good_color(VAR_PROMPT_FORE_COLOR) &&
438 pico_is_good_color(VAR_PROMPT_BACK_COLOR)){
439 lastc = pico_get_cur_color();
440 if(lastc){
441 promptc = new_color_pair(VAR_PROMPT_FORE_COLOR,
442 VAR_PROMPT_BACK_COLOR);
443 (void)pico_set_colorp(promptc, PSC_NONE);
446 else
447 StartInverse();
450 * if display length isn't wide enough to support input,
451 * shorten up the prompt...
453 if((dline.dwid = cols - (x_base + prompt_width)) < MIN_OPT_ENT_WIDTH){
454 char *p;
455 unsigned got_width;
458 * Scoot prompt pointer forward at least (MIN_OPT_ENT_WIDTH - dline.dwid) screencells.
460 p = utf8_count_forw_width(utf8prompt, MIN_OPT_ENT_WIDTH-dline.dwid, &got_width);
461 if(got_width < MIN_OPT_ENT_WIDTH-dline.dwid)
462 p = utf8_count_forw_width(utf8prompt, MIN_OPT_ENT_WIDTH+1-dline.dwid, &got_width);
464 if(p){
465 prompt_width = utf8_width(p);
466 dline.dwid = cols - (x_base + prompt_width);
467 utf8prompt = p;
472 * How many UCS-4 characters will we need to make up the width dwid? It could be
473 * unlimited because of zero-width characters, I suppose, but realistically it
474 * isn't going to be much more than dwid.
476 dline.dlen = 2 * dline.dwid + 100;
478 dline.dl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
479 dline.olddl = (UCS *) fs_get(dline.dlen * sizeof(UCS));
480 memset(dline.dl, 0, dline.dlen * sizeof(UCS));
481 memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
483 dline.movecursor = MoveCursor;
484 dline.writechar = Writewchar;
486 dline.row = real_y_base;
487 dline.col = x_base + prompt_width;
489 dline.vl = string;
490 dline.vlen = --string_size; /* -1 for terminating zero */
491 dline.vbase = field_pos = 0;
493 #ifdef _WINDOWS
494 cursor_shown = mswin_showcaret(1);
495 #endif
497 PutLine0(real_y_base, x_base, utf8prompt);
500 * If appending, position field_pos at end of input.
502 if(flags && *flags & OE_APPEND_CURRENT)
503 while(string[field_pos])
504 field_pos++;
506 passwd = (flags && *flags & OE_PASSWD_NOAST) ? 10 :
507 (flags && *flags & OE_PASSWD) ? 1 : 0;
508 line_paint(field_pos, &dline, &passwd);
510 /*----------------------------------------------------------------------
511 The main loop
512 loops until someone sets the return_v.
513 ----------------------------------------------------------------------*/
514 return_v = -10;
516 while(return_v == -10) {
518 #ifdef MOUSE
519 mouse_in_content(KEY_MOUSE, -1, -1, 0x5, 0);
520 register_mfunc(mouse_in_content,
521 real_y_base, x_base + prompt_width,
522 real_y_base, ps_global->ttyo->screen_cols);
523 #endif
524 #ifdef _WINDOWS
525 mswin_allowpaste(MSWIN_PASTE_LINE);
526 g_mc_row = real_y_base;
527 g_mc_col = x_base + prompt_width;
528 mswin_mousetrackcallback(pcpine_oe_cursor);
529 #endif
531 /* Timeout 10 min to keep imap mail stream alive */
532 ps_global->conceal_sensitive_debugging = passwd ? 1 : 0;
533 ucs = read_char(600);
534 ps_global->conceal_sensitive_debugging = 0;
536 #ifdef MOUSE
537 clear_mfunc(mouse_in_content);
538 #endif
539 #ifdef _WINDOWS
540 mswin_allowpaste(MSWIN_PASTE_DISABLE);
541 mswin_mousetrackcallback(NULL);
542 #endif
545 * Don't want to intercept all characters if typing in passwd.
546 * We select an ad hoc set that we will catch and let the rest
547 * through. We would have caught the set below in the big switch
548 * but we skip the switch instead. Still catch things like ^K,
549 * DELETE, ^C, RETURN.
551 if(passwd)
552 switch(ucs){
553 case ctrl('F'):
554 case KEY_RIGHT:
555 case ctrl('B'):
556 case KEY_LEFT:
557 case ctrl('U'):
558 case ctrl('A'):
559 case KEY_HOME:
560 case ctrl('E'):
561 case KEY_END:
562 case TAB:
563 goto ok_for_passwd;
566 if(too_thin && ucs != KEY_RESIZE && ucs != ctrl('Z') && ucs != ctrl('C'))
567 goto bleep;
569 switch(ucs){
571 /*--------------- KEY RIGHT ---------------*/
572 case ctrl('F'):
573 case KEY_RIGHT:
574 if(field_pos >= string_size || string[field_pos] == '\0')
575 goto bleep;
577 line_paint(++field_pos, &dline, &passwd);
578 break;
580 /*--------------- KEY LEFT ---------------*/
581 case ctrl('B'):
582 case KEY_LEFT:
583 if(field_pos <= 0)
584 goto bleep;
586 line_paint(--field_pos, &dline, &passwd);
587 break;
589 /*-------------------- WORD SKIP --------------------*/
590 case ctrl('@'):
592 * Note: read_char *can* return NO_OP_COMMAND which is
593 * the def'd with the same value as ^@ (NULL), BUT since
594 * read_char has a big timeout (>25 secs) it won't.
597 /* skip thru current word */
598 while(string[field_pos]
599 && isalnum((unsigned char) string[field_pos]))
600 field_pos++;
602 /* skip thru current white space to next word */
603 while(string[field_pos]
604 && !isalnum((unsigned char) string[field_pos]))
605 field_pos++;
607 line_paint(field_pos, &dline, &passwd);
608 break;
610 /*-------------------- RETURN --------------------*/
611 case PF4:
612 if(F_OFF(F_USE_FK,ps_global)) goto bleep;
613 case ctrl('J'):
614 case ctrl('M'):
615 return_v = 0;
616 break;
618 /*-------------------- Destructive backspace --------------------*/
619 case '\177': /* DEL */
620 case ctrl('H'):
621 /* Try and do this with by telling the terminal to delete a
622 a character. If that fails, then repaint the rest of the
623 line, acheiving the same much less efficiently
625 if(field_pos <= 0)
626 goto bleep;
628 field_pos--;
629 /* drop thru to pull line back ... */
631 /*-------------------- Delete char --------------------*/
632 case ctrl('D'):
633 case KEY_DEL:
634 if(field_pos >= string_size || !string[field_pos])
635 goto bleep;
637 dline.vused--;
638 for(s2 = &string[field_pos]; *s2 != 0; s2++)
639 *s2 = s2[1];
641 *s2 = 0; /* Copy last NULL */
642 line_paint(field_pos, &dline, &passwd);
643 if(flags) /* record change if requested */
644 *flags |= OE_USER_MODIFIED;
646 break;
648 /*--------------- Kill line -----------------*/
649 case ctrl('K'):
650 if(kill_buffer != NULL)
651 fs_give((void **) &kill_buffer);
653 if(field_pos != 0 || string[0]){
654 if(!passwd && F_ON(F_DEL_FROM_DOT, ps_global))
655 dline.vused -= ucs4_strlen(&string[i = field_pos]);
656 else
657 dline.vused = i = 0;
659 kill_buffer = ucs4_cpystr(&string[field_pos = i]);
660 string[field_pos] = '\0';
661 line_paint(field_pos, &dline, &passwd);
662 if(flags) /* record change if requested */
663 *flags |= OE_USER_MODIFIED;
666 break;
668 /*------------------- Undelete line --------------------*/
669 case ctrl('U'):
670 if(kill_buffer == NULL)
671 goto bleep;
673 /* Make string so it will fit */
674 kb = ucs4_cpystr(kill_buffer);
675 if(ucs4_strlen(kb) + ucs4_strlen(string) > string_size)
676 kb[string_size - ucs4_strlen(string)] = '\0';
678 if(string[field_pos] == '\0') {
679 /*--- adding to the end of the string ----*/
680 for(k = kb; *k; k++)
681 string[field_pos++] = *k;
683 string[field_pos] = '\0';
685 else{
686 int shift;
688 shift = ucs4_strlen(kb);
690 /* shift field_pos ... end to right */
691 for(k = &string[field_pos] + ucs4_strlen(&string[field_pos]);
692 k >= &string[field_pos]; k--)
693 *(k+shift) = *k;
695 for(k = kb; *k; k++)
696 string[field_pos++] = *k;
699 if(*kb && flags) /* record change if requested */
700 *flags |= OE_USER_MODIFIED;
702 dline.vused = ucs4_strlen(string);
703 fs_give((void **) &kb);
704 line_paint(field_pos, &dline, &passwd);
705 break;
707 /*-------------------- Interrupt --------------------*/
708 case ctrl('C'): /* ^C */
709 if(F_ON(F_USE_FK,ps_global) || (flags && ((*flags) & OE_DISALLOW_CANCEL)))
710 goto bleep;
712 goto cancel;
714 case PF2:
715 if(F_OFF(F_USE_FK,ps_global) || (flags && ((*flags) & OE_DISALLOW_CANCEL)))
716 goto bleep;
718 cancel:
719 return_v = 1;
720 if(saved_original){
721 for(i = 0; saved_original[i]; i++)
722 string[i] = saved_original[i];
724 string[i] = 0;
727 break;
729 case ctrl('A'):
730 case KEY_HOME:
731 /*-------------------- Start of line -------------*/
732 line_paint(field_pos = 0, &dline, &passwd);
733 break;
735 case ctrl('E'):
736 case KEY_END:
737 /*-------------------- End of line ---------------*/
738 line_paint(field_pos = dline.vused, &dline, &passwd);
739 break;
741 /*-------------------- Help --------------------*/
742 case ctrl('G') :
743 case PF1:
744 if(flags && ((*flags) & OE_DISALLOW_HELP))
745 goto bleep;
746 else if(FOOTER_ROWS(ps_global) == 1 && km_popped == 0){
747 km_popped++;
748 FOOTER_ROWS(ps_global) = 3;
749 clearfooter(ps_global);
750 if(lastc)
751 (void)pico_set_colorp(lastc, PSC_NONE);
752 else
753 EndInverse();
755 draw_keymenu(km, bitmap, cols, 1-FOOTER_ROWS(ps_global),
756 0, FirstMenu);
758 if(promptc)
759 (void)pico_set_colorp(promptc, PSC_NONE);
760 else
761 StartInverse();
763 mark_keymenu_dirty();
764 y_base = -3;
765 dline.row = real_y_base = y_base + ps_global->ttyo->screen_rows;
766 PutLine0(real_y_base, x_base, utf8prompt);
767 memset(dline.dl, 0, dline.dlen * sizeof(UCS));
768 memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
769 line_paint(field_pos, &dline, &passwd);
770 break;
773 if(FOOTER_ROWS(ps_global) > 1){
774 mark_keymenu_dirty();
775 return_v = 3;
777 else
778 goto bleep;
780 break;
783 #ifdef MOUSE
784 /* Mouse support untested in pine 5.00 */
785 case KEY_MOUSE :
787 MOUSEPRESS mp;
788 int w;
790 mouse_get_last (NULL, &mp);
792 switch(mp.button){
793 case M_BUTTON_LEFT : /* position cursor */
794 mp.col -= dline.col;
797 * We have to figure out which character is under the cursor.
798 * This is complicated by the fact that characters may
799 * be other than one cell wide.
802 /* the -1 is for the '<' when text is offscreen left */
803 w = (dline.vbase > 0) ? mp.col-1 : mp.col;
805 if(mp.col <= 0)
806 field_pos = dline.vbase - 1;
807 else{
808 if(dline.vused <= dline.vbase
809 || ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vused-1) <= w)
810 field_pos = dline.vused;
811 else{
813 * Find index of 1st character that causes the
814 * width to be > w.
816 for(i = 0;
817 ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vbase+i) <= w;
818 i++)
821 field_pos = dline.vbase + i;
825 field_pos = MIN(MAX(field_pos, 0), dline.vused);
827 /* just allow line_paint to choose vbase */
828 line_paint(field_pos, &dline, &passwd);
829 break;
831 case M_BUTTON_RIGHT :
832 #ifdef _WINDOWS
835 * Same as M_BUTTON_LEFT except we paste in text after
836 * moving the cursor.
839 mp.col -= dline.col;
841 /* the -1 is for the '<' when text is offscreen left */
842 w = (dline.vbase > 0) ? mp.col-1 : mp.col;
844 if(mp.col <= 0)
845 field_pos = dline.vbase - 1;
846 else{
847 if(dline.vused <= dline.vbase
848 || ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vused-1) <= w)
849 field_pos = dline.vused;
850 else{
852 * Find index of 1st character that causes the
853 * width to be > w.
855 for(i = 0;
856 ucs4_str_width_a_to_b(dline.vl,dline.vbase,dline.vbase+i) <= w;
857 i++)
860 field_pos = dline.vbase + i;
864 field_pos = MIN(MAX(field_pos, 0), dline.vused);
866 line_paint(field_pos, &dline, &passwd);
868 mswin_allowpaste(MSWIN_PASTE_LINE);
869 mswin_paste_popup();
870 mswin_allowpaste(MSWIN_PASTE_DISABLE);
871 break;
872 #endif
874 case M_BUTTON_MIDDLE : /* NO-OP for now */
875 default: /* just ignore */
876 break;
880 break;
881 #endif
884 case NO_OP_IDLE:
886 * Keep mail stream alive by checking for new mail.
887 * If we're asking for a password in a login prompt
888 * we don't want to check for new_mail because the
889 * new mail check might be what got us here in the first
890 * place (because of a filter trying to save a message).
891 * If we need to wait for the user to come back then
892 * the caller will just have to deal with the failure
893 * to login.
895 i = -1;
896 if(!ps_global->no_newmail_check_from_optionally_enter)
897 i = new_mail(0, 2, NM_DEFER_SORT);
899 if(sp_expunge_count(ps_global->mail_stream) &&
900 flags && ((*flags) & OE_SEQ_SENSITIVE))
901 goto cancel;
903 if(i < 0){
904 line_paint(field_pos, &dline, &passwd);
905 break; /* no changes, get on with life */
907 /* Else fall into redraw */
909 /*-------------------- Redraw --------------------*/
910 case ctrl('L'):
911 /*---------------- re size ----------------*/
912 case KEY_RESIZE:
914 dline.row = real_y_base = y_base > 0 ? y_base :
915 y_base + ps_global->ttyo->screen_rows;
916 if(lastc)
917 (void)pico_set_colorp(lastc, PSC_NONE);
918 else
919 EndInverse();
921 ClearScreen();
922 redraw_titlebar();
923 if(ps_global->redrawer != (void (*)(void))NULL)
924 (*ps_global->redrawer)();
926 redraw_keymenu();
927 if(promptc)
928 (void)pico_set_colorp(promptc, PSC_NONE);
929 else
930 StartInverse();
932 PutLine0(real_y_base, x_base, utf8prompt);
933 cols = ps_global->ttyo->screen_cols;
934 too_thin = 0;
935 if(cols < x_base + prompt_width + 4){
936 Writechar(BELL, 0);
937 PutLine0(real_y_base, 0, "Screen's too thin. Ouch!");
938 too_thin = 1;
940 else{
941 dline.col = x_base + prompt_width;
942 dline.dwid = cols - (x_base + prompt_width);
943 dline.dlen = 2 * dline.dwid + 100;
944 fs_resize((void **) &dline.dl, (size_t) dline.dlen * sizeof(UCS));
945 fs_resize((void **) &dline.olddl, (size_t) dline.dlen * sizeof(UCS));
946 memset(dline.dl, 0, dline.dlen * sizeof(UCS));
947 memset(dline.olddl, 0, dline.dlen * sizeof(UCS));
948 line_paint(field_pos, &dline, &passwd);
951 fflush(stdout);
953 dprint((9,
954 "optionally_enter RESIZE new_cols:%d too_thin: %d\n",
955 cols, too_thin));
956 break;
958 case PF3 : /* input to potentially remap */
959 case PF5 :
960 case PF6 :
961 case PF7 :
962 case PF8 :
963 case PF9 :
964 case PF10 :
965 case PF11 :
966 case PF12 :
967 if(F_ON(F_USE_FK,ps_global)
968 && fkey_table[ucs - PF1] != NO_OP_COMMAND)
969 ucs = fkey_table[ucs - PF1]; /* remap function key input */
971 default:
972 if(escape_list){ /* in the escape key list? */
973 for(j=0; escape_list[j].ch != -1; j++){
974 if(escape_list[j].ch == ucs){
975 return_v = escape_list[j].rval;
976 break;
980 if(return_v != -10)
981 break;
984 if(ucs < 0x80 && FILTER_THIS((unsigned char) ucs)){
985 bleep:
986 putc(BELL, stdout);
987 continue;
990 ok_for_passwd:
991 /*--- Insert a character -----*/
992 if(dline.vused >= string_size)
993 goto bleep;
995 /*---- extending the length of the string ---*/
996 for(s2 = &string[++dline.vused]; s2 - string > field_pos; s2--)
997 *s2 = *(s2-1);
999 string[field_pos++] = ucs;
1000 line_paint(field_pos, &dline, &passwd);
1001 if(flags) /* record change if requested */
1002 *flags |= OE_USER_MODIFIED;
1004 } /*---- End of switch on char ----*/
1007 #ifdef _WINDOWS
1008 if(!cursor_shown)
1009 mswin_showcaret(0);
1010 #endif
1012 if(dline.dl)
1013 fs_give((void **) &dline.dl);
1015 if(dline.olddl)
1016 fs_give((void **) &dline.olddl);
1018 if(saved_original)
1019 fs_give((void **) &saved_original);
1021 if(kill_buffer)
1022 fs_give((void **) &kill_buffer);
1025 * Change string back into UTF-8.
1027 candidate = ucs4_to_utf8_cpystr(string);
1029 if(string)
1030 fs_give((void **) &string);
1032 if(candidate){
1033 strncpy(utf8string, candidate, utf8string_size);
1034 utf8string[utf8string_size-1] = '\0';
1035 fs_give((void **) &candidate);
1038 if (!(flags && (*flags) & OE_KEEP_TRAILING_SPACE))
1039 removing_trailing_white_space(utf8string);
1041 if(lastc){
1042 (void)pico_set_colorp(lastc, PSC_NONE);
1043 free_color_pair(&lastc);
1044 if(promptc)
1045 free_color_pair(&promptc);
1047 else
1048 EndInverse();
1050 MoveCursor(real_y_base, x_base); /* Move the cursor to show we're done */
1051 fflush(stdout);
1052 resume_busy_cue(0);
1053 if(km_popped){
1054 FOOTER_ROWS(ps_global) = 1;
1055 clearfooter(ps_global);
1056 ps_global->mangled_body = 1;
1059 return(return_v);
1063 /*----------------------------------------------------------------------
1064 Check to see if the given command is reasonably valid
1066 Args: ch -- the character to check
1068 Result: A valid command is returned, or a well know bad command is returned.
1070 ---*/
1072 validatekeys(UCS ch)
1074 if(F_ON(F_USE_FK,ps_global)){
1075 if(ch >= 'a' && ch <= 'z')
1076 return(KEY_JUNK);
1078 else{
1079 if(ch >= PF1 && ch <= PF12)
1080 return(KEY_JUNK);
1083 return(ch);
1088 /*----------------------------------------------------------------------
1089 Prepend config'd commands to keyboard input
1091 Args: ch -- pointer to storage for returned command
1093 Returns: TRUE if we're passing back a useful command, FALSE otherwise
1095 ---*/
1097 process_config_input(UCS *ch)
1099 static char firsttime = (char) 1;
1100 int c;
1101 unsigned long octets_so_far, remaining_octets, ret = 0;
1102 unsigned char *inputp;
1103 UCS ucs;
1104 unsigned char inputbuf[20];
1106 /* commands in config file */
1107 if(ps_global->initial_cmds && *ps_global->initial_cmds) {
1109 * There are a few commands that may require keyboard input before
1110 * we enter the main command loop. That input should be interactive,
1111 * not from our list of initial keystrokes.
1113 if(ps_global->dont_use_init_cmds)
1114 return(ret);
1116 c = *ps_global->initial_cmds++;
1119 * Use enough bytes to make up a character and convert it to UCS-4.
1121 if(c < 0x80 || c > KEY_BASE){
1122 *ch = (UCS) c;
1123 ret = 1;
1125 else{
1126 memset(inputbuf, 0, sizeof(inputbuf));
1127 inputbuf[0] = (0xff & c);
1128 octets_so_far = 1;
1130 while(!ret){
1131 remaining_octets = octets_so_far;
1132 inputp = inputbuf;
1133 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
1134 switch(ucs){
1135 case U8G_ENDSTRG: /* incomplete character, wait */
1136 case U8G_ENDSTRI: /* incomplete character, wait */
1137 if(!*ps_global->initial_cmds || octets_so_far >= sizeof(inputbuf)){
1138 *ch = BADESC;
1139 ret = 1;
1141 else
1142 inputbuf[octets_so_far++] = (0xff & *ps_global->initial_cmds++);
1144 break;
1146 default:
1147 if(ucs & U8G_ERROR || ucs == UBOGON)
1148 *ch = BADESC;
1149 else
1150 *ch = ucs;
1152 ret = 1;
1153 break;
1158 if(!*ps_global->initial_cmds && ps_global->free_initial_cmds){
1159 fs_give((void **) &ps_global->free_initial_cmds);
1160 ps_global->initial_cmds = NULL;
1163 return(ret);
1166 if(firsttime) {
1167 firsttime = 0;
1168 if(ps_global->in_init_seq) {
1169 ps_global->in_init_seq = 0;
1170 ps_global->save_in_init_seq = 0;
1171 clear_cursor_pos();
1172 F_SET(F_USE_FK,ps_global,ps_global->orig_use_fkeys);
1173 /* draw screen */
1174 *ch = (UCS) ctrl('L');
1175 return(1);
1179 return(0);
1183 #define TAPELEN 256
1184 static int tape[TAPELEN];
1185 static long recorded = 0L;
1186 static short length = 0;
1190 * record user keystrokes
1192 * Args: ch -- the character to record
1194 * Returns: character recorded
1197 key_recorder(int ch)
1199 tape[recorded++ % TAPELEN] = ch;
1200 if(length < TAPELEN)
1201 length++;
1203 return(ch);
1208 * playback user keystrokes
1210 * Args: ch -- ignored
1212 * Returns: character played back or -1 to indicate end of tape
1215 key_playback(int ch)
1217 ch = length ? tape[(recorded + TAPELEN - length--) % TAPELEN] : -1;
1218 return(ch);
1223 * recent_keystroke - verbose version of key_playback
1226 recent_keystroke(int *cv, char *cs, size_t cslen)
1228 int c;
1230 if((c = key_playback(0)) != -1){
1231 *cv = c;
1232 snprintf(cs, cslen, "%.32s", pretty_command(c));
1233 return(0);
1236 return(-1);
1240 #ifdef _WINDOWS
1242 pcpine_oe_cursor(col, row)
1243 int col;
1244 long row;
1246 return((row == g_mc_row
1247 && col >= g_mc_col
1248 && col < ps_global->ttyo->screen_cols)
1249 ? MSWIN_CURSOR_IBEAM
1250 : MSWIN_CURSOR_ARROW);
1252 #endif