*** empty log message ***
[dana/urxvt.git] / src / command.C
1 /*--------------------------------*-C-*---------------------------------*
2  * File:        command.c
3  *----------------------------------------------------------------------*
4  *
5  * All portions of code are copyright by their respective author/s.
6  * Copyright (c) 1992      John Bovey, University of Kent at Canterbury <jdb@ukc.ac.uk>
7  *                              - original version
8  * Copyright (c) 1994      Robert Nation <nation@rocket.sanders.lockheed.com>
9  *                              - extensive modifications
10  * Copyright (c) 1995      Garrett D'Amore <garrett@netcom.com>
11  *                              - vt100 printing
12  * Copyright (c) 1995      Steven Hirsch <hirsch@emba.uvm.edu>
13  *                              - X11 mouse report mode and support for
14  *                                DEC "private mode" save/restore functions.
15  * Copyright (c) 1995      Jakub Jelinek <jj@gnu.ai.mit.edu>
16  *                              - key-related changes to handle Shift+function
17  *                                keys properly.
18  * Copyright (c) 1997      MJ Olesen <olesen@me.queensu.ca>
19  *                              - extensive modifications
20  * Copyright (c) 1997      Raul Garcia Garcia <rgg@tid.es>
21  *                              - modification and cleanups for Solaris 2.x
22  *                                and Linux 1.2.x
23  * Copyright (c) 1997,1998 Oezguer Kesim <kesim@math.fu-berlin.de>
24  * Copyright (c) 1998-2001 Geoff Wing <gcw@pobox.com>
25  *                              - extensive modifications
26  * Copyright (c) 1998      Alfredo K. Kojima <kojima@windowmaker.org>
27  * Copyright (c) 2001      Marius Gedminas
28  *                              - Ctrl/Mod4+Tab works like Meta+Tab (options)
29  * Copyright (c) 2003      Rob McMullen <robm@flipturn.org>
30  * Copyright (c) 2003-2004 Marc Lehmann <pcg@goof.com>
31  *
32  * This program is free software; you can redistribute it and/or modify
33  * it under the terms of the GNU General Public License as published by
34  * the Free Software Foundation; either version 2 of the License, or
35  * (at your option) any later version.
36  *
37  * This program is distributed in the hope that it will be useful,
38  * but WITHOUT ANY WARRANTY; without even the implied warranty of
39  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
40  * GNU General Public License for more details.
41  *
42  * You should have received a copy of the GNU General Public License
43  * along with this program; if not, write to the Free Software
44  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
45  *----------------------------------------------------------------------*/
46
47 /*{{{ includes: */
48 #include "../config.h"          /* NECESSARY */
49 #include "rxvt.h"               /* NECESSARY */
50 #include "version.h"
51 #include "command.h"
52
53 #include <wchar.h>
54
55 /*----------------------------------------------------------------------*/
56
57 /*{{{ Convert the keypress event into a string */
58 void
59 rxvt_term::lookup_key (XKeyEvent &ev)
60 {
61   int ctrl, meta, shft, len;
62   unsigned int newlen;
63   KeySym keysym;
64 #ifdef DEBUG_CMD
65   static int debug_key = 1;     /* accessible by a debugger only */
66 #endif
67   int valid_keysym;
68   unsigned char kbuf[KBUFSZ];
69
70   /*
71    * use Num_Lock to toggle Keypad on/off.  If Num_Lock is off, allow an
72    * escape sequence to toggle the Keypad.
73    *
74    * Always permit `shift' to override the current setting
75    */
76   shft = (ev.state & ShiftMask);
77   ctrl = (ev.state & ControlMask);
78   meta = (ev.state & ModMetaMask);
79
80   if (numlock_state || (ev.state & ModNumLockMask))
81     {
82       numlock_state = (ev.state & ModNumLockMask);
83       PrivMode ((!numlock_state), PrivMode_aplKP);
84     }
85
86   kbuf[0] = 0;
87
88 #ifdef USE_XIM
89   if (Input_Context)
90     {
91       Status status_return;
92
93 #ifdef X_HAVE_UTF8_STRING
94       if (enc_utf8 && 0) // currently disabled, doesn't seem to work, nor is useful
95         len = Xutf8LookupString (Input_Context, &ev, (char *)kbuf,
96                                  KBUFSZ, &keysym, &status_return);
97       else
98 #endif
99         {
100           wchar_t wkbuf[KBUFSZ + 1];
101
102           // the XOpenIM manpage lies about hardcoding the locale
103           // at the point of XOpenIM, so temporarily switch locales
104           if (rs[Rs_imLocale])
105             SET_LOCALE (rs[Rs_imLocale]);
106           // assume wchar_t == unicode or better
107           len = XwcLookupString (Input_Context, &ev, wkbuf,
108                                  KBUFSZ, &keysym, &status_return);
109           if (rs[Rs_imLocale])
110             SET_LOCALE (locale);
111
112           if (status_return == XLookupChars
113               || status_return == XLookupBoth)
114             {
115               /* make sure the user can type ctrl-@, i.e. NUL */
116               if (len == 1 && *wkbuf == 0)
117                 {
118                   kbuf[0] = 0;
119                   len = 1;
120                 }
121               else
122                 {
123                   wkbuf[len] = 0;
124                   len = wcstombs ((char *)kbuf, wkbuf, KBUFSZ);
125                   if (len < 0)
126                     len = 0;
127                 }
128             }
129           else
130             len = 0;
131         }
132
133       valid_keysym = status_return == XLookupKeySym
134                      || status_return == XLookupBoth;
135     }
136   else
137 #endif
138     {
139       len = XLookupString (&ev, (char *)kbuf, KBUFSZ, &keysym, &compose);
140       valid_keysym = keysym != NoSymbol;
141     }
142
143   if (valid_keysym)
144     {
145       if (TermWin.saveLines)
146         {
147 #ifdef UNSHIFTED_SCROLLKEYS
148           if (!ctrl && !meta)
149             {
150 #else
151           if (IS_SCROLL_MOD)
152             {
153 #endif
154               int lnsppg;
155
156 #ifdef PAGING_CONTEXT_LINES
157               lnsppg = TermWin.nrow - PAGING_CONTEXT_LINES;
158 #else
159               lnsppg = TermWin.nrow * 4 / 5;
160 #endif
161               if (keysym == XK_Prior)
162                 {
163                   scr_page (UP, lnsppg);
164                   return;
165                 }
166               else if (keysym == XK_Next)
167                 {
168                   scr_page (DN, lnsppg);
169                   return;
170                 }
171             }
172 #ifdef SCROLL_ON_UPDOWN_KEYS
173           if (IS_SCROLL_MOD)
174             {
175               if (keysym == XK_Up)
176                 {
177                   scr_page (UP, 1);
178                   return;
179                 }
180               else if (keysym == XK_Down)
181                 {
182                   scr_page (DN, 1);
183                   return;
184                 }
185             }
186 #endif
187 #ifdef SCROLL_ON_HOMEEND_KEYS
188           if (IS_SCROLL_MOD)
189             {
190               if (keysym == XK_Home)
191                 {
192                   scr_move_to (0, 1);
193                   return;
194                 }
195               else if (keysym == XK_End)
196                 {
197                   scr_move_to (1, 0);
198                   return;
199                 }
200             }
201 #endif
202         }
203
204       if (shft)
205         {
206           /* Shift + F1 - F10 generates F11 - F20 */
207           if (keysym >= XK_F1 && keysym <= XK_F10)
208             {
209               keysym += (XK_F11 - XK_F1);
210               shft = 0; /* turn off Shift */
211             }
212           else if (!ctrl && !meta && (PrivateModes & PrivMode_ShiftKeys))
213             {
214               switch (keysym)
215                 {
216                     /* normal XTerm key bindings */
217                   case XK_Insert:       /* Shift+Insert = paste mouse selection */
218                     selection_request (ev.time, 0, 0);
219                     return;
220                     /* rxvt extras */
221                   case XK_KP_Add:       /* Shift+KP_Add = bigger font */
222                     change_font (FONT_UP);
223                     return;
224                   case XK_KP_Subtract:  /* Shift+KP_Subtract = smaller font */
225                     change_font (FONT_DN);
226                     return;
227                 }
228             }
229         }
230 #ifdef PRINTPIPE
231       if (keysym == XK_Print)
232         {
233           scr_printscreen (ctrl | shft);
234           return;
235         }
236 #endif
237
238       if (keysym >= 0xFF00 && keysym <= 0xFFFF)
239         {
240 #ifdef KEYSYM_RESOURCE
241           if (! (shft | ctrl) && Keysym_map[keysym & 0xFF] != NULL)
242             {
243               unsigned int    l;
244               const unsigned char *kbuf0;
245
246               kbuf0 = (Keysym_map[keysym & 0xFF]);
247               l = (unsigned int)*kbuf0++;
248
249               /* escape prefix */
250               if (meta
251 # ifdef META8_OPTION
252                   && meta_char == C0_ESC
253 # endif
254                  )
255                 {
256                   const unsigned char ch = C0_ESC;
257                   tt_write (&ch, 1);
258                 }
259
260               tt_write (kbuf0, l);
261               return;
262             }
263           else
264 #endif
265             {
266               newlen = 1;
267               switch (keysym)
268                 {
269 #ifndef NO_BACKSPACE_KEY
270                   case XK_BackSpace:
271                     if (PrivateModes & PrivMode_HaveBackSpace)
272                       {
273                         kbuf[0] = (!! (PrivateModes & PrivMode_BackSpace)
274                                    ^ !!ctrl) ? '\b' : '\177';
275                         kbuf[1] = '\0';
276                       }
277                     else
278                       STRCPY (kbuf, key_backspace);
279                     break;
280 #endif
281 #ifndef NO_DELETE_KEY
282                   case XK_Delete:
283                     STRCPY (kbuf, key_delete);
284                     break;
285 #endif
286                   case XK_Tab:
287                     if (shft)
288                       STRCPY (kbuf, "\033[Z");
289                     else
290                       {
291 #ifdef CTRL_TAB_MAKES_META
292                         if (ctrl)
293                           meta = 1;
294 #endif
295 #ifdef MOD4_TAB_MAKES_META
296                         if (ev.state & Mod4Mask)
297                           meta = 1;
298 #endif
299                         newlen = 0;
300                       }
301                     break;
302
303 #ifdef XK_KP_Left
304                   case XK_KP_Up:                /* \033Ox or standard */
305                   case XK_KP_Down:      /* \033Or or standard */
306                   case XK_KP_Right:     /* \033Ov or standard */
307                   case XK_KP_Left:      /* \033Ot or standard */
308                     if ((PrivateModes & PrivMode_aplKP) ? !shft : shft)
309                       {
310                         STRCPY (kbuf, "\033OZ");
311                         kbuf[2] = "txvr"[keysym - XK_KP_Left];
312                         break;
313                       }
314                     else
315                       /* translate to std. cursor key */
316                       keysym = XK_Left + (keysym - XK_KP_Left);
317                     /* FALLTHROUGH */
318 #endif
319                   case XK_Up:   /* "\033[A" */
320                   case XK_Down: /* "\033[B" */
321                   case XK_Right:        /* "\033[C" */
322                   case XK_Left: /* "\033[D" */
323                     STRCPY (kbuf, "\033[Z");
324                     kbuf[2] = "DACB"[keysym - XK_Left];
325                     /* do Shift first */
326                     if (shft)
327                       kbuf[2] = "dacb"[keysym - XK_Left];
328                     else if (ctrl)
329                       {
330                         kbuf[1] = 'O';
331                         kbuf[2] = "dacb"[keysym - XK_Left];
332                       }
333                     else if (PrivateModes & PrivMode_aplCUR)
334                       kbuf[1] = 'O';
335                     break;
336
337 #ifndef UNSHIFTED_SCROLLKEYS
338 # ifdef XK_KP_Prior
339                   case XK_KP_Prior:
340                     /* allow shift to override */
341                     if ((PrivateModes & PrivMode_aplKP) ? !shft : shft)
342                       {
343                         STRCPY (kbuf, "\033Oy");
344                         break;
345                       }
346                     /* FALLTHROUGH */
347 # endif
348                   case XK_Prior:
349                     STRCPY (kbuf, "\033[5~");
350                     break;
351 # ifdef XK_KP_Next
352                   case XK_KP_Next:
353                     /* allow shift to override */
354                     if ((PrivateModes & PrivMode_aplKP) ? !shft : shft)
355                       {
356                         STRCPY (kbuf, "\033Os");
357                         break;
358                       }
359                     /* FALLTHROUGH */
360 # endif
361                   case XK_Next:
362                     STRCPY (kbuf, "\033[6~");
363                     break;
364 #endif
365                   case XK_KP_Enter:
366                     /* allow shift to override */
367                     if ((PrivateModes & PrivMode_aplKP) ? !shft : shft)
368                       {
369                         STRCPY (kbuf, "\033OM");
370                       }
371                     else
372                       {
373                         kbuf[0] = '\r';
374                         kbuf[1] = '\0';
375                       }
376                     break;
377
378 #ifdef XK_KP_Begin
379                   case XK_KP_Begin:
380                     STRCPY (kbuf, "\033Ou");
381                     break;
382
383                   case XK_KP_Insert:
384                     STRCPY (kbuf, "\033Op");
385                     break;
386
387                   case XK_KP_Delete:
388                     STRCPY (kbuf, "\033On");
389                     break;
390 #endif
391                   case XK_KP_F1:        /* "\033OP" */
392                   case XK_KP_F2:        /* "\033OQ" */
393                   case XK_KP_F3:        /* "\033OR" */
394                   case XK_KP_F4:        /* "\033OS" */
395                     STRCPY (kbuf, "\033OP");
396                     kbuf[2] += (keysym - XK_KP_F1);
397                     break;
398
399                   case XK_KP_Multiply:  /* "\033Oj" : "*" */
400                   case XK_KP_Add:       /* "\033Ok" : "+" */
401                   case XK_KP_Separator: /* "\033Ol" : "," */
402                   case XK_KP_Subtract:  /* "\033Om" : "-" */
403                   case XK_KP_Decimal:   /* "\033On" : "." */
404                   case XK_KP_Divide:    /* "\033Oo" : "/" */
405                   case XK_KP_0:         /* "\033Op" : "0" */
406                   case XK_KP_1:         /* "\033Oq" : "1" */
407                   case XK_KP_2:         /* "\033Or" : "2" */
408                   case XK_KP_3:         /* "\033Os" : "3" */
409                   case XK_KP_4:         /* "\033Ot" : "4" */
410                   case XK_KP_5:         /* "\033Ou" : "5" */
411                   case XK_KP_6:         /* "\033Ov" : "6" */
412                   case XK_KP_7:         /* "\033Ow" : "7" */
413                   case XK_KP_8:         /* "\033Ox" : "8" */
414                   case XK_KP_9:         /* "\033Oy" : "9" */
415                     /* allow shift to override */
416                     if ((PrivateModes & PrivMode_aplKP) ? !shft : shft)
417                       {
418                         STRCPY (kbuf, "\033Oj");
419                         kbuf[2] += (keysym - XK_KP_Multiply);
420                       }
421                     else
422                       {
423                         kbuf[0] = ('*' + (keysym - XK_KP_Multiply));
424                         kbuf[1] = '\0';
425                       }
426                     break;
427
428                   case XK_Find:
429                     STRCPY (kbuf, "\033[1~");
430                     break;
431                   case XK_Insert:
432                     STRCPY (kbuf, "\033[2~");
433                     break;
434 #ifdef DXK_Remove               /* support for DEC remove like key */
435                   case DXK_Remove:
436                     /* FALLTHROUGH */
437 #endif
438                   case XK_Execute:
439                     STRCPY (kbuf, "\033[3~");
440                     break;
441                   case XK_Select:
442                     STRCPY (kbuf, "\033[4~");
443                     break;
444 #ifdef XK_KP_End
445                   case XK_KP_End:
446                     /* allow shift to override */
447                     if ((PrivateModes & PrivMode_aplKP) ? !shft : shft)
448                       {
449                         STRCPY (kbuf, "\033Oq");
450                         break;
451                       }
452                     /* FALLTHROUGH */
453 #endif
454                   case XK_End:
455                     STRCPY (kbuf, KS_END);
456                     break;
457 #ifdef XK_KP_Home
458                   case XK_KP_Home:
459                     /* allow shift to override */
460                     if ((PrivateModes & PrivMode_aplKP) ? !shft : shft)
461                       {
462                         STRCPY (kbuf, "\033Ow");
463                         break;
464                       }
465                     /* FALLTHROUGH */
466 #endif
467                   case XK_Home:
468                     STRCPY (kbuf, KS_HOME);
469                     break;
470
471 #define FKEY(n, fkey)                                                   \
472     sprintf ((char *)kbuf,"\033[%2d~", (int) ((n) + (keysym - fkey)))
473
474                   case XK_F1:   /* "\033[11~" */
475                   case XK_F2:   /* "\033[12~" */
476                   case XK_F3:   /* "\033[13~" */
477                   case XK_F4:   /* "\033[14~" */
478                   case XK_F5:   /* "\033[15~" */
479                     FKEY (11, XK_F1);
480                     break;
481                   case XK_F6:   /* "\033[17~" */
482                   case XK_F7:   /* "\033[18~" */
483                   case XK_F8:   /* "\033[19~" */
484                   case XK_F9:   /* "\033[20~" */
485                   case XK_F10:  /* "\033[21~" */
486                     FKEY (17, XK_F6);
487                     break;
488                   case XK_F11:  /* "\033[23~" */
489                   case XK_F12:  /* "\033[24~" */
490                   case XK_F13:  /* "\033[25~" */
491                   case XK_F14:  /* "\033[26~" */
492                     FKEY (23, XK_F11);
493                     break;
494                   case XK_F15:  /* "\033[28~" */
495                   case XK_F16:  /* "\033[29~" */
496                     FKEY (28, XK_F15);
497                     break;
498                   case XK_Help: /* "\033[28~" */
499                     FKEY (28, XK_Help);
500                     break;
501                   case XK_Menu: /* "\033[29~" */
502                     FKEY (29, XK_Menu);
503                     break;
504                   case XK_F17:  /* "\033[31~" */
505                   case XK_F18:  /* "\033[32~" */
506                   case XK_F19:  /* "\033[33~" */
507                   case XK_F20:  /* "\033[34~" */
508                   case XK_F21:  /* "\033[35~" */
509                   case XK_F22:  /* "\033[36~" */
510                   case XK_F23:  /* "\033[37~" */
511                   case XK_F24:  /* "\033[38~" */
512                   case XK_F25:  /* "\033[39~" */
513                   case XK_F26:  /* "\033[40~" */
514                   case XK_F27:  /* "\033[41~" */
515                   case XK_F28:  /* "\033[42~" */
516                   case XK_F29:  /* "\033[43~" */
517                   case XK_F30:  /* "\033[44~" */
518                   case XK_F31:  /* "\033[45~" */
519                   case XK_F32:  /* "\033[46~" */
520                   case XK_F33:  /* "\033[47~" */
521                   case XK_F34:  /* "\033[48~" */
522                   case XK_F35:  /* "\033[49~" */
523                     FKEY (31, XK_F17);
524                     break;
525 #undef FKEY
526                   default:
527                     newlen = 0;
528                     break;
529                 }
530               if (newlen)
531                 len = STRLEN (kbuf);
532             }
533
534           /*
535            * Pass meta for all function keys, if 'meta' option set
536            */
537 #ifdef META8_OPTION
538           if (meta && (meta_char == 0x80) && len > 0)
539             kbuf[len - 1] |= 0x80;
540 #endif
541
542         }
543       else if (ctrl && keysym == XK_minus)
544         {
545           len = 1;
546           kbuf[0] = '\037';     /* Ctrl-Minus generates ^_ (31) */
547         }
548       else
549         {
550 #ifdef META8_OPTION
551           /* set 8-bit on */
552           if (meta && (meta_char == 0x80))
553             {
554               unsigned char  *ch;
555
556               for (ch = kbuf; ch < kbuf + len; ch++)
557                 *ch |= 0x80;
558
559               meta = 0;
560             }
561 #endif
562           /* nil */ ;
563         }
564     }
565
566   if (len <= 0)
567     return;                     /* not mapped */
568
569   if (Options & Opt_scrollTtyKeypress)
570     if (TermWin.view_start)
571       {
572         TermWin.view_start = 0;
573         want_refresh = 1;
574       }
575
576   /*
577    * these modifications only affect the static keybuffer
578    * pass Shift/Control indicators for function keys ending with `~'
579    *
580    * eg,
581    *   Prior = "ESC[5~"
582    *   Shift+Prior = "ESC[5$"
583    *   Ctrl+Prior = "ESC[5^"
584    *   Ctrl+Shift+Prior = "ESC[5@"
585    * Meta adds an Escape prefix (with META8_OPTION, if meta == <escape>).
586    */
587   if (kbuf[0] == C0_ESC && kbuf[1] == '[' && kbuf[len - 1] == '~')
588     kbuf[len - 1] = (shft ? (ctrl ? '@' : '$') : (ctrl ? '^' : '~'));
589
590   /* escape prefix */
591   if (meta
592 #ifdef META8_OPTION
593       && meta_char == C0_ESC
594 #endif
595      )
596     {
597       const unsigned char ch = C0_ESC;
598       tt_write (&ch, 1);
599     }
600
601 #if defined(DEBUG_CMD)
602   /* Display keyboard buffer contents */
603   unsigned char *p;
604   int i;
605
606   fprintf (stderr, "key 0x%04X [%d]: `", (unsigned int)keysym, len);
607   for (i = 0, p = kbuf; i < len; i++, p++)
608     fprintf (stderr, (*p >= ' ' && *p < '\177' ? "%c" : "\\%03o"), *p);
609   fprintf (stderr, "'\n");
610 #endif                          /* DEBUG_CMD */
611   tt_write (kbuf, (unsigned int)len);
612 }
613 /*}}} */
614
615 #if (MENUBAR_MAX)
616 /*{{{ rxvt_cmd_write (), rxvt_cmd_getc () */
617 /* attempt to `write' count to the input buffer */
618 unsigned int
619 rxvt_term::cmd_write (const unsigned char *str, unsigned int count)
620 {
621   unsigned int    n, s;
622
623   n = cmdbuf_ptr - cmdbuf_base;
624   s = cmdbuf_base + BUFSIZ - 1 - cmdbuf_endp;
625
626   if (n > 0 && s < count)
627     {
628       MEMMOVE (cmdbuf_base, cmdbuf_ptr,
629               (unsigned int) (cmdbuf_endp - cmdbuf_ptr));
630       cmdbuf_ptr = cmdbuf_base;
631       cmdbuf_endp -= n;
632       s += n;
633     }
634
635   if (count > s)
636     {
637       rxvt_print_error ("data loss: cmd_write too large");
638       count = s;
639     }
640
641   for (; count--;)
642     *cmdbuf_endp++ = *str++;
643
644   cmd_parse ();
645
646   return 0;
647 }
648 #endif                          /* MENUBAR_MAX */
649
650 void
651 rxvt_term::flush ()
652 {
653 #ifdef TRANSPARENT
654   if (want_full_refresh)
655     {
656       want_full_refresh = 0;
657       scr_clear ();
658       scr_touch (false);
659       want_refresh = 1;
660     }
661 #endif
662
663   if (want_refresh)
664     {
665       scr_refresh (refresh_type);
666       scrollbar_show (1);
667 #ifdef USE_XIM
668       IMSendSpot ();
669 #endif
670
671     }
672
673   display->flush ();
674 }
675
676 void
677 rxvt_term::check_cb (check_watcher &w)
678 {
679   SET_R (this);
680   SET_LOCALE (locale);
681
682   flush ();
683 }
684
685 #ifdef CURSOR_BLINK
686 void
687 rxvt_term::cursor_blink_cb (time_watcher &w)
688 {
689   hidden_cursor = !hidden_cursor;
690   want_refresh = 1;
691
692   w.start (w.at + BLINK_INTERVAL);
693 }
694 #endif
695
696 #ifdef TEXT_BLINK
697 void
698 rxvt_term::text_blink_cb (time_watcher &w)
699 {
700   if (scr_refresh_rend (RS_Blink, RS_Blink))
701     {
702       hidden_text = !hidden_text;
703       want_refresh = 1;
704       w.start (w.at + TEXT_BLINK_INTERVAL);
705     }
706 }
707 #endif
708
709 bool
710 rxvt_term::pty_fill ()
711 {
712   ssize_t n = cmdbuf_endp - cmdbuf_ptr;
713
714   memmove (cmdbuf_base, cmdbuf_ptr, n);
715   cmdbuf_ptr = cmdbuf_base;
716   cmdbuf_endp = cmdbuf_ptr + n;
717
718   n = read (cmd_fd, cmdbuf_endp, BUFSIZ - n);
719
720   if (n > 0)
721     {
722       cmdbuf_endp += n;
723       return true;
724     }
725   else if (n < 0 && errno != EAGAIN)
726     destroy ();
727   
728   return false;
729 }
730
731 void
732 rxvt_term::pty_cb (io_watcher &w, short revents)
733 {
734   SET_R (this);
735   SET_LOCALE (locale);
736
737   if (revents & EVENT_WRITE)
738     tt_write (0, 0);
739   else if (revents & EVENT_READ)
740     {
741       bool flag = true;
742
743       // loop, but don't allow a single term to monopolize us
744       // the number of loops is fully arbitrary, and thus wrong
745       while (flag && pty_fill ())
746         {
747           if (!seen_input)
748             {
749               seen_input = 1;
750               /* once we know the shell is running, send the screen size.  Again! */
751               tt_winch ();
752             }
753
754           if (cmd_parse ())
755             break;
756         }
757     }
758 }
759
760 bool
761 rxvt_term::cmd_parse ()
762 {
763   bool flag = false;
764   uint32_t ch = NOCHAR;
765
766   for (;;)
767     {
768       if (ch == NOCHAR)
769         ch = next_char ();
770
771       if (ch == NOCHAR) // TODO: improve
772         break;
773
774       if (ch >= ' ' || ch == '\t' || ch == '\n' || ch == '\r')
775         {
776           /* Read a text string from the input buffer */
777           uint32_t buf[BUFSIZ];
778           bool refreshnow = false;
779           int nlines = 0;
780           uint32_t *str = buf;
781
782           *str++ = ch;
783
784           for (;;)
785             {
786               ch = next_char ();
787
788               if (ch == NOCHAR || (ch < ' ' && ch != '\t' && ch != '\n' && ch != '\r'))
789                 break;
790               else
791                 {
792                   *str++ = ch;
793
794                   if (ch == '\n')
795                     {
796                       nlines++;
797                       refresh_count++;
798
799                       if (! (Options & Opt_jumpScroll)
800                           || (refresh_count >= (refresh_limit * (TermWin.nrow - 1))))
801                         {
802                           refreshnow = true;
803                           flag = false;
804                           ch = NOCHAR;
805                           break;
806                         }
807
808                       // scr_add_lines only works for nlines < TermWin.nrow - 1.
809                       if (nlines >= TermWin.nrow - 1)
810                         {
811                           scr_add_lines (buf, nlines, str - buf);
812                           nlines = 0;
813                           str = buf;
814                         }
815                     }
816
817                   if (str >= buf + BUFSIZ)
818                     {
819                       ch = NOCHAR;
820                       break;
821                     }
822                 }
823             }
824
825           scr_add_lines (buf, nlines, str - buf);
826
827           /*
828            * If there have been a lot of new lines, then update the screen
829            * What the heck I'll cheat and only refresh less than every page-full.
830            * the number of pages between refreshes is refresh_limit, which
831            * is incremented here because we must be doing flat-out scrolling.
832            *
833            * refreshing should be correct for small scrolls, because of the
834            * time-out
835            */
836           if (refreshnow)
837             {
838               if ((Options & Opt_jumpScroll) && refresh_limit < REFRESH_PERIOD)
839                 refresh_limit++;
840
841               scr_refresh (refresh_type);
842             }
843
844         }
845       else
846         {
847           switch (ch)
848             {
849               default:
850                 process_nonprinting (ch);
851                 break;
852               case C0_ESC:      /* escape char */
853                 process_escape_seq ();
854                 break;
855                 /*case 0x9b: */ /* CSI */
856                 /*  process_csi_seq (); */
857             }
858
859           ch = NOCHAR;
860         }
861     }
862
863   return flag;
864 }
865
866 // read the next character, currently handles UTF-8
867 // will probably handle all sorts of other stuff in the future
868 uint32_t
869 rxvt_term::next_char ()
870 {
871   while (cmdbuf_ptr < cmdbuf_endp)
872     {
873       // assume 0x20 .. 0x7f to be ascii ALWAYS (all shift-states etc.) uh-oh
874       if ((*cmdbuf_ptr <= 0x7f && 0x20 <= *cmdbuf_ptr)
875           || !*cmdbuf_ptr)
876         return *cmdbuf_ptr++;
877
878       wchar_t wc;
879       size_t len = mbrtowc (&wc, (char *)cmdbuf_ptr, cmdbuf_endp - cmdbuf_ptr, mbstate);
880
881       if (len == (size_t)-2)
882         {
883           // the mbstate stores incomplete sequences. didn't know this :/
884           cmdbuf_ptr = cmdbuf_endp;
885           break;
886         }
887
888       if (len == (size_t)-1)
889         return *cmdbuf_ptr++; // the _occasional_ latin1 character is allowed to slip through
890
891       // assume wchar == unicode
892       cmdbuf_ptr += len;
893       return wc;
894     }
895
896   return NOCHAR;
897 }
898
899 /* rxvt_cmd_getc () - Return next input character */
900 /*
901  * Return the next input character after first passing any keyboard input
902  * to the command.
903  */
904 uint32_t
905 rxvt_term::cmd_getc ()
906 {
907   for (;;)
908     {
909       uint32_t c = next_char ();
910       if (c != NOCHAR)
911         return c;
912
913       // incomplete sequences should occur rarely, still, a better solution
914       // would be preferred. either setjmp/longjmp or better design.
915       fcntl (cmd_fd, F_SETFL, 0);
916       pty_fill ();
917       fcntl (cmd_fd, F_SETFL, O_NONBLOCK);
918     }
919 }
920
921 #ifdef POINTER_BLANK
922 void
923 rxvt_term::pointer_unblank ()
924 {
925   XDefineCursor (display->display, TermWin.vt, TermWin_cursor);
926   recolour_cursor ();
927
928   hidden_pointer = 0;
929
930   if (Options & Opt_pointerBlank)
931     pointer_ev.start (NOW + pointerBlankDelay);
932 }
933
934 void
935 rxvt_term::pointer_blank ()
936 {
937   if (! (Options & Opt_pointerBlank))
938     return;
939
940   XDefineCursor (display->display, TermWin.vt, blank_cursor);
941   XFlush (display->display);
942
943   hidden_pointer = 1;
944 }
945
946 void
947 rxvt_term::pointer_cb (time_watcher &w)
948 {
949   SET_R (this);
950   SET_LOCALE (locale);
951
952   pointer_blank ();
953 }
954 #endif
955
956 void
957 rxvt_term::mouse_report (XButtonEvent &ev)
958 {
959   int button_number, key_state = 0;
960   int x, y;
961
962   x = ev.x;
963   y = ev.y;
964   pixel_position (&x, &y);
965
966   if (MEvent.button == AnyButton)
967     {
968       button_number = 3;
969     }
970   else
971     {
972       button_number = MEvent.button - Button1;
973       /* add 0x3D for wheel events, like xterm does */
974       if (button_number >= 3)
975         button_number += (64 - 3);
976     }
977
978   if (PrivateModes & PrivMode_MouseX10)
979     {
980       /*
981        * do not report ButtonRelease
982        * no state info allowed
983        */
984       key_state = 0;
985       if (button_number == 3)
986         return;
987     }
988   else
989     {
990       /* XTerm mouse reporting needs these values:
991        *   4 = Shift
992        *   8 = Meta
993        *  16 = Control
994        * plus will add in our own Double-Click reporting
995        *  32 = Double Click
996        */
997       key_state = ((MEvent.state & ShiftMask) ? 4 : 0)
998                   + ((MEvent.state & ModMetaMask) ? 8 : 0)
999                   + ((MEvent.state & ControlMask) ? 16 : 0);
1000 #ifdef MOUSE_REPORT_DOUBLECLICK
1001       key_state += ((MEvent.clicks > 1) ? 32 : 0);
1002 #endif
1003
1004     }
1005
1006 #ifdef DEBUG_MOUSEREPORT
1007   fprintf (stderr, "Mouse [");
1008   if (key_state & 16)
1009     fputc ('C', stderr);
1010   if (key_state & 4)
1011     fputc ('S', stderr);
1012   if (key_state & 8)
1013     fputc ('A', stderr);
1014   if (key_state & 32)
1015     fputc ('2', stderr);
1016   fprintf (stderr, "]: <%d>, %d/%d\n",
1017           button_number,
1018           x + 1,
1019           y + 1);
1020 #else
1021   tt_printf ("\033[M%c%c%c",
1022             (32 + button_number + key_state),
1023             (32 + x + 1),
1024             (32 + y + 1));
1025 #endif
1026 }
1027
1028 #ifdef USING_W11LIB
1029 void
1030 rxvt_W11_process_x_event (XEvent *ev)
1031 {
1032   rxvt_t *r = rxvt_get_r ();
1033
1034   x_cb (*ev);
1035 }
1036 #endif
1037
1038 /*{{{ process an X event */
1039 void
1040 rxvt_term::x_cb (XEvent &ev)
1041 {
1042   SET_R (this);
1043   SET_LOCALE (locale);
1044
1045 #if defined(CURSOR_BLINK)
1046   if ((Options & Opt_cursorBlink) && ev.type == KeyPress)
1047     {
1048       if (hidden_cursor)
1049         {
1050           hidden_cursor = 0;
1051           want_refresh = 1;
1052         }
1053
1054       cursor_blink_ev.start (NOW + BLINK_INTERVAL);
1055     }
1056 #endif
1057
1058 #if defined(POINTER_BLANK)
1059   if ((Options & Opt_pointerBlank) && pointerBlankDelay > 0)
1060     {
1061       if (ev.type == MotionNotify
1062           || ev.type == ButtonPress
1063           || ev.type == ButtonRelease)
1064         if (hidden_pointer)
1065           pointer_unblank ();
1066
1067       if (ev.type == KeyPress && hidden_pointer == 0)
1068         pointer_blank ();
1069     }
1070 #endif
1071
1072 #ifdef USE_XIM
1073   if (XFilterEvent (&ev, None))
1074     return;
1075 #endif
1076
1077   Window          unused_root, unused_child;
1078   int             unused_root_x, unused_root_y;
1079   unsigned int    unused_mask;
1080
1081 #ifdef DEBUG_X
1082   const char *const eventnames[] =
1083     {                           /* mason - this matches my system */
1084       "",
1085       "",
1086       "KeyPress",
1087       "KeyRelease",
1088       "ButtonPress",
1089       "ButtonRelease",
1090       "MotionNotify",
1091       "EnterNotify",
1092       "LeaveNotify",
1093       "FocusIn",
1094       "FocusOut",
1095       "KeymapNotify",
1096       "Expose",
1097       "GraphicsExpose",
1098       "NoExpose",
1099       "VisibilityNotify",
1100       "CreateNotify",
1101       "DestroyNotify",
1102       "UnmapNotify",
1103       "MapNotify",
1104       "MapRequest",
1105       "ReparentNotify",
1106       "ConfigureNotify",
1107       "ConfigureRequest",
1108       "GravityNotify",
1109       "ResizeRequest",
1110       "CirculateNotify",
1111       "CirculateRequest",
1112       "PropertyNotify",
1113       "SelectionClear",
1114       "SelectionRequest",
1115       "SelectionNotify",
1116       "ColormapNotify",
1117       "ClientMessage",
1118       "MappingNotify"
1119     };
1120 #endif
1121
1122 #ifdef DEBUG_X
1123   struct timeval  tp;
1124   struct tm      *ltt;
1125   (void)gettimeofday (&tp, NULL);
1126   ltt = localtime (& (tp.tv_sec));
1127   D_X ((stderr, "Event: %-16s %-7s %08lx (%4d-%02d-%02d %02d:%02d:%02d.%.6ld) %s %lu", eventnames[ev.type], (ev.xany.window == TermWin.parent[0] ? "parent" : (ev.xany.window == TermWin.vt ? "vt" : (ev.xany.window == scrollBar.win ? "scroll" : (ev.xany.window == menuBar.win ? "menubar" : "UNKNOWN")))), (ev.xany.window == TermWin.parent[0] ? TermWin.parent[0] : (ev.xany.window == TermWin.vt ? TermWin.vt : (ev.xany.window == scrollBar.win ? scrollBar.win : (ev.xany.window == menuBar.win ? menuBar.win : 0)))), ltt->tm_year + 1900, ltt->tm_mon + 1, ltt->tm_mday, ltt->tm_hour, ltt->tm_min, ltt->tm_sec, tp.tv_usec, ev.xany.send_event ? "S" : " ", ev.xany.serial));
1128 #endif
1129
1130   switch (ev.type)
1131     {
1132       case KeyPress:
1133         lookup_key (ev.xkey);
1134         break;
1135
1136 #if defined(MOUSE_WHEEL) && defined(MOUSE_SLIP_WHEELING)
1137       case KeyRelease:
1138         {
1139           if (! (ev.xkey.state & ControlMask))
1140             mouse_slip_wheel_speed = 0;
1141           else
1142             {
1143               KeySym          ks;
1144
1145               ks = XKeycodeToKeysym (display->display, ev.xkey.keycode, 0);
1146               if (ks == XK_Control_L || ks == XK_Control_R)
1147                 mouse_slip_wheel_speed = 0;
1148             }
1149           break;
1150         }
1151 #endif
1152
1153       case ButtonPress:
1154         button_press (ev.xbutton);
1155         break;
1156
1157       case ButtonRelease:
1158         button_release (ev.xbutton);
1159         break;
1160
1161       case ClientMessage:
1162         if (ev.xclient.format == 32
1163             && (Atom)ev.xclient.data.l[0] == xa[XA_WMDELETEWINDOW])
1164           destroy ();
1165 #ifdef OFFIX_DND
1166         /* OffiX Dnd (drag 'n' drop) protocol */
1167         else if (ev.xclient.message_type == xa[XA_DNDPROTOCOL]
1168                  && (ev.xclient.data.l[0] == DndFile
1169                      || ev.xclient.data.l[0] == DndDir
1170                      || ev.xclient.data.l[0] == DndLink))
1171           {
1172             /* Get Dnd data */
1173             Atom            ActualType;
1174             int             ActualFormat;
1175             unsigned char  *data;
1176             unsigned long   Size, RemainingBytes;
1177
1178             XGetWindowProperty (display->display, display->root,
1179                                xa[XA_DNDSELECTION],
1180                                0L, 1000000L,
1181                                False, AnyPropertyType,
1182                                &ActualType, &ActualFormat,
1183                                &Size, &RemainingBytes,
1184                                &data);
1185             XChangeProperty (display->display, display->root,
1186                             XA_CUT_BUFFER0, XA_STRING,
1187                             8, PropModeReplace,
1188                             data, STRLEN (data));
1189             selection_paste (display->root, XA_CUT_BUFFER0, True);
1190             XSetInputFocus (display->display, display->root, RevertToNone, CurrentTime);
1191           }
1192 #endif                          /* OFFIX_DND */
1193         break;
1194
1195       case MappingNotify:
1196         XRefreshKeyboardMapping (& (ev.xmapping));
1197         break;
1198
1199         /*
1200          * XXX: this is not the _current_ arrangement
1201          * Here's my conclusion:
1202          * If the window is completely unobscured, use bitblt's
1203          * to scroll. Even then, they're only used when doing partial
1204          * screen scrolling. When partially obscured, we have to fill
1205          * in the GraphicsExpose parts, which means that after each refresh,
1206          * we need to wait for the graphics expose or Noexpose events,
1207          * which ought to make things real slow!
1208          */
1209       case VisibilityNotify:
1210         switch (ev.xvisibility.state)
1211           {
1212             case VisibilityUnobscured:
1213               refresh_type = FAST_REFRESH;
1214               break;
1215             case VisibilityPartiallyObscured:
1216               refresh_type = SLOW_REFRESH;
1217               break;
1218             default:
1219               refresh_type = NO_REFRESH;
1220               break;
1221           }
1222         break;
1223
1224       case FocusIn:
1225         if (!TermWin.focus)
1226           {
1227             TermWin.focus = 1;
1228             want_refresh = 1;
1229 #ifdef USE_XIM
1230             if (Input_Context != NULL)
1231               XSetICFocus (Input_Context);
1232 #endif
1233 #ifdef CURSOR_BLINK
1234             if (Options & Opt_cursorBlink)
1235               cursor_blink_ev.start (NOW + BLINK_INTERVAL);
1236 #endif
1237
1238           }
1239         break;
1240
1241       case FocusOut:
1242         if (TermWin.focus)
1243           {
1244             TermWin.focus = 0;
1245             want_refresh = 1;
1246 #ifdef USE_XIM
1247             if (Input_Context != NULL)
1248               XUnsetICFocus (Input_Context);
1249 #endif
1250 #ifdef CURSOR_BLINK
1251             if (Options & Opt_cursorBlink)
1252               cursor_blink_ev.stop ();
1253             hidden_cursor = 0;
1254 #endif
1255
1256           }
1257         break;
1258
1259       case ConfigureNotify:
1260         if (ev.xconfigure.window == TermWin.parent[0])
1261           {
1262             int height, width;
1263
1264             do
1265               { /* Wrap lots of configures into one */
1266                 width = ev.xconfigure.width;
1267                 height = ev.xconfigure.height;
1268                 D_SIZE ((stderr, "Size: ConfigureNotify: %4d x %4d", width, height));
1269               }
1270             while (XCheckTypedWindowEvent (display->display, ev.xconfigure.window, ConfigureNotify, &ev));
1271
1272             if (szHint.width != width || szHint.height != height)
1273               {
1274                 D_SIZE ((stderr, "Size: Resizing from: %4d x %4d", szHint.width, szHint.height));
1275                 resize_all_windows (width, height, 1);
1276               }
1277 #ifdef DEBUG_SIZE
1278             else
1279               {
1280                 D_SIZE ((stderr, "Size: Not resizing"));
1281               }
1282 #endif
1283 #ifdef TRANSPARENT              /* XXX: maybe not needed - leave in for now */
1284             if (Options & Opt_transparent)
1285               {
1286                 check_our_parents ();
1287                 if (am_transparent)
1288                   want_full_refresh = 1;
1289               }
1290 #endif
1291
1292           }
1293         break;
1294
1295       case SelectionClear:
1296         display->set_selection_owner (0);
1297         break;
1298
1299       case SelectionNotify:
1300         if (selection_wait == Sel_normal)
1301           selection_paste (ev.xselection.requestor,
1302                            ev.xselection.property, True);
1303         break;
1304
1305       case SelectionRequest:
1306         selection_send (ev.xselectionrequest);
1307         break;
1308
1309       case UnmapNotify:
1310         TermWin.mapped = 0;
1311 #ifdef TEXT_BLINK
1312         text_blink_ev.stop ();
1313 #endif
1314         break;
1315
1316       case MapNotify:
1317         TermWin.mapped = 1;
1318 #ifdef TEXT_BLINK
1319         text_blink_ev.start (NOW + TEXT_BLINK_INTERVAL);
1320 #endif
1321         break;
1322
1323 #ifdef TRANSPARENT
1324       case ReparentNotify:
1325         rootwin_cb (ev);
1326         break;
1327 #endif                          /* TRANSPARENT */
1328
1329       case GraphicsExpose:
1330       case Expose:
1331         if (ev.xany.window == TermWin.vt)
1332           {
1333 #ifdef NO_SLOW_LINK_SUPPORT
1334             scr_expose (ev.xexpose.x, ev.xexpose.y,
1335                         ev.xexpose.width, ev.xexpose.height, False);
1336 #else
1337             // don't understand this, so commented it out
1338             scr_expose (ev.xexpose.x, ev.xexpose.y,
1339                         ev.xexpose.width, ev.xexpose.height, False);
1340             //scr_expose (ev.xexpose.x, 0,
1341             //              ev.xexpose.width, TermWin.height, False);
1342 #endif
1343             want_refresh = 1;
1344           }
1345         else
1346           {
1347             XEvent unused_event;
1348
1349             while (XCheckTypedWindowEvent (display->display, ev.xany.window,
1350                                           Expose,
1351                                           &unused_event)) ;
1352             while (XCheckTypedWindowEvent (display->display, ev.xany.window,
1353                                           GraphicsExpose,
1354                                           &unused_event)) ;
1355             if (isScrollbarWindow (ev.xany.window))
1356               {
1357                 scrollBar.setIdle ();
1358                 scrollbar_show (0);
1359               }
1360 #ifdef MENUBAR
1361             if (menubar_visible () && isMenuBarWindow (ev.xany.window))
1362               menubar_expose ();
1363 #endif
1364           }
1365         break;
1366
1367       case MotionNotify:
1368 #ifdef POINTER_BLANK
1369         if (hidden_pointer)
1370           pointer_unblank ();
1371 #endif
1372 #if MENUBAR
1373         if (isMenuBarWindow (ev.xany.window))
1374           {
1375             menubar_control (ev.xbutton);
1376             break;
1377           }
1378 #endif
1379         if ((PrivateModes & PrivMode_mouse_report) && ! (bypass_keystate))
1380           break;
1381
1382         if (ev.xany.window == TermWin.vt)
1383           {
1384             if ((ev.xbutton.state & (Button1Mask | Button3Mask)))
1385               {
1386                 while (XCheckTypedWindowEvent (display->display, TermWin.vt, MotionNotify, &ev))
1387                   ;
1388
1389                 XQueryPointer (display->display, TermWin.vt,
1390                               &unused_root, &unused_child,
1391                               &unused_root_x, &unused_root_y,
1392                               & (ev.xbutton.x), & (ev.xbutton.y),
1393                               &unused_mask);
1394 #ifdef MOUSE_THRESHOLD
1395                 /* deal with a `jumpy' mouse */
1396                 if ((ev.xmotion.time - MEvent.time) > MOUSE_THRESHOLD)
1397                   {
1398 #endif
1399                     selection_extend ((ev.xbutton.x), (ev.xbutton.y),
1400                                       (ev.xbutton.state & Button3Mask) ? 2 : 0);
1401 #ifdef SELECTION_SCROLLING
1402                     if (ev.xbutton.y < TermWin.int_bwidth
1403                         || Pixel2Row (ev.xbutton.y) > (TermWin.nrow-1))
1404                       {
1405                         int dist;
1406
1407                         pending_scroll_selection=1;
1408
1409                         /* don't clobber the current delay if we are
1410                          * already in the middle of scrolling.
1411                          */
1412                         if (scroll_selection_delay<=0)
1413                           scroll_selection_delay=SCROLLBAR_CONTINUOUS_DELAY;
1414
1415                         /* save the event params so we can highlight
1416                          * the selection in the pending-scroll loop
1417                          */
1418                         selection_save_x=ev.xbutton.x;
1419                         selection_save_y=ev.xbutton.y;
1420                         selection_save_state=
1421                           (ev.xbutton.state & Button3Mask) ? 2 : 0;
1422
1423                         /* calc number of lines to scroll */
1424                         if (ev.xbutton.y<TermWin.int_bwidth)
1425                           {
1426                             scroll_selection_dir = UP;
1427                             dist = TermWin.int_bwidth - ev.xbutton.y;
1428                           }
1429                         else
1430                           {
1431                             scroll_selection_dir = DN;
1432                             dist = ev.xbutton.y -
1433                                    (TermWin.int_bwidth + TermWin.height);
1434                           }
1435                         scroll_selection_lines= (Pixel2Height (dist)/
1436                                                 SELECTION_SCROLL_LINE_SPEEDUP)+1;
1437                         MIN_IT (scroll_selection_lines,
1438                                SELECTION_SCROLL_MAX_LINES);
1439                       }
1440                     else
1441                       {
1442                         /* we are within the text window, so we
1443                          * shouldn't be scrolling
1444                          */
1445                         pending_scroll_selection = 0;
1446                       }
1447 #endif
1448 #ifdef MOUSE_THRESHOLD
1449
1450                   }
1451 #endif
1452
1453               }
1454           }
1455         else if (isScrollbarWindow (ev.xany.window) && scrollbar_isMotion ())
1456           {
1457             while (XCheckTypedWindowEvent (display->display, scrollBar.win,
1458                                           MotionNotify, &ev)) ;
1459             XQueryPointer (display->display, scrollBar.win,
1460                           &unused_root, &unused_child,
1461                           &unused_root_x, &unused_root_y,
1462                           & (ev.xbutton.x), & (ev.xbutton.y),
1463                           &unused_mask);
1464             scr_move_to (scrollbar_position (ev.xbutton.y) - csrO,
1465                          scrollbar_size ());
1466             scr_refresh (refresh_type);
1467             refresh_limit = 0;
1468             scrollbar_show (1);
1469           }
1470         break;
1471     }
1472 }
1473
1474 void
1475 rxvt_term::rootwin_cb (XEvent &ev)
1476 {
1477   SET_R (this);
1478   SET_LOCALE (locale);
1479
1480   switch (ev.type)
1481     {
1482       case PropertyNotify:
1483         if (ev.xproperty.atom == xa[XA_VT_SELECTION])
1484           {
1485             if (ev.xproperty.state == PropertyNewValue)
1486               selection_property (ev.xproperty.window, ev.xproperty.atom);
1487             break;
1488           }
1489 #ifdef TRANSPARENT
1490         else
1491           {
1492             /*
1493              * if user used some Esetroot compatible prog to set the root bg,
1494              * use the property to determine the pixmap.  We use it later on.
1495              */
1496             if (xa[XA_XROOTPMAPID] == 0)
1497               xa[XA_XROOTPMAPID] = XInternAtom (display->display, "_XROOTPMAP_ID", False);
1498
1499             if (ev.xproperty.atom != xa[XA_XROOTPMAPID])
1500               return;
1501           }
1502
1503         /* FALLTHROUGH */
1504       case ReparentNotify:
1505         if ((Options & Opt_transparent) && check_our_parents ())
1506           if (am_transparent)
1507             want_full_refresh = 1;
1508         break;
1509 #endif
1510     }
1511 }
1512
1513 void
1514 rxvt_term::button_press (XButtonEvent &ev)
1515 {
1516   int reportmode = 0, clickintime;
1517
1518   bypass_keystate = ev.state & (ModMetaMask | ShiftMask);
1519   if (!bypass_keystate)
1520     reportmode = !! (PrivateModes & PrivMode_mouse_report);
1521   /*
1522    * VT window processing of button press
1523    */
1524   if (ev.window == TermWin.vt)
1525     {
1526       clickintime = ev.time - MEvent.time < MULTICLICK_TIME;
1527       if (reportmode)
1528         {
1529           /* mouse report from vt window */
1530           /* save the xbutton state (for ButtonRelease) */
1531           MEvent.state = ev.state;
1532 #ifdef MOUSE_REPORT_DOUBLECLICK
1533           if (ev.button == MEvent.button && clickintime)
1534             {
1535               /* same button, within alloted time */
1536               MEvent.clicks++;
1537               if (MEvent.clicks > 1)
1538                 {
1539                   /* only report double clicks */
1540                   MEvent.clicks = 2;
1541                   mouse_report (ev);
1542
1543                   /* don't report the release */
1544                   MEvent.clicks = 0;
1545                   MEvent.button = AnyButton;
1546                 }
1547             }
1548           else
1549             {
1550               /* different button, or time expired */
1551               MEvent.clicks = 1;
1552               MEvent.button = ev.button;
1553               mouse_report (ev);
1554             }
1555 #else
1556           MEvent.button = ev.button;
1557           mouse_report (ev);
1558 #endif                          /* MOUSE_REPORT_DOUBLECLICK */
1559
1560         }
1561       else
1562         {
1563           if (ev.button != MEvent.button)
1564             MEvent.clicks = 0;
1565           switch (ev.button)
1566             {
1567               case Button1:
1568                 /* allow shift+left click to extend selection */
1569                 if (ev.state & ShiftMask && ! (PrivateModes & PrivMode_mouse_report))
1570                   {
1571                     if (MEvent.button == Button1 && clickintime)
1572                       selection_rotate (ev.x, ev.y);
1573                     else
1574                       selection_extend (ev.x, ev.y, 1);
1575                   }
1576                 else
1577                   {
1578                     if (MEvent.button == Button1 && clickintime)
1579                       MEvent.clicks++;
1580                     else
1581                       MEvent.clicks = 1;
1582
1583                     selection_click (MEvent.clicks, ev.x, ev.y);
1584                   }
1585
1586                 MEvent.button = Button1;
1587                 break;
1588
1589               case Button3:
1590                 if (MEvent.button == Button3 && clickintime)
1591                   selection_rotate (ev.x, ev.y);
1592                 else
1593                   selection_extend (ev.x, ev.y, 1);
1594                 MEvent.button = Button3;
1595                 break;
1596             }
1597         }
1598       MEvent.time = ev.time;
1599       return;
1600     }
1601
1602   /*
1603    * Scrollbar window processing of button press
1604    */
1605   if (isScrollbarWindow (ev.window))
1606     {
1607       scrollBar.setIdle ();
1608       /*
1609        * Rxvt-style scrollbar:
1610        * move up if mouse is above slider
1611        * move dn if mouse is below slider
1612        *
1613        * XTerm-style scrollbar:
1614        * Move display proportional to pointer location
1615        * pointer near top -> scroll one line
1616        * pointer near bot -> scroll full page
1617        */
1618 #ifndef NO_SCROLLBAR_REPORT
1619       if (reportmode)
1620         {
1621           /*
1622            * Mouse report disabled scrollbar:
1623            * arrow buttons - send up/down
1624            * click on scrollbar - send pageup/down
1625            */
1626           if ((scrollBar.style == R_SB_NEXT
1627                && scrollbarnext_upButton (ev.y))
1628               || (scrollBar.style == R_SB_RXVT
1629                   && scrollbarrxvt_upButton (ev.y)))
1630             tt_printf ("\033[A");
1631           else if ((scrollBar.style == R_SB_NEXT
1632                     && scrollbarnext_dnButton (ev.y))
1633                    || (scrollBar.style == R_SB_RXVT
1634                        && scrollbarrxvt_dnButton (ev.y)))
1635             tt_printf ("\033[B");
1636           else
1637             switch (ev.button)
1638               {
1639                 case Button2:
1640                   tt_printf ("\014");
1641                   break;
1642                 case Button1:
1643                   tt_printf ("\033[6~");
1644                   break;
1645                 case Button3:
1646                   tt_printf ("\033[5~");
1647                   break;
1648               }
1649         }
1650       else
1651 #endif                          /* NO_SCROLLBAR_REPORT */
1652
1653         {
1654           char            upordown = 0;
1655
1656           if (scrollBar.style == R_SB_NEXT)
1657             {
1658               if (scrollbarnext_upButton (ev.y))
1659                 upordown = -1;  /* up */
1660               else if (scrollbarnext_dnButton (ev.y))
1661                 upordown = 1;   /* down */
1662             }
1663           else if (scrollBar.style == R_SB_RXVT)
1664             {
1665               if (scrollbarrxvt_upButton (ev.y))
1666                 upordown = -1;  /* up */
1667               else if (scrollbarrxvt_dnButton (ev.y))
1668                 upordown = 1;   /* down */
1669             }
1670           if (upordown)
1671             {
1672 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
1673               scroll_arrow_delay = SCROLLBAR_INITIAL_DELAY;
1674 #endif
1675               if (scr_page (upordown < 0 ? UP : DN, 1))
1676                 {
1677                   if (upordown < 0)
1678                     scrollBar.setUp ();
1679                   else
1680                     scrollBar.setDn ();
1681                 }
1682             }
1683           else
1684             switch (ev.button)
1685               {
1686                 case Button2:
1687                   switch (scrollbar_align)
1688                     {
1689                       case R_SB_ALIGN_TOP:
1690                         csrO = 0;
1691                         break;
1692                       case R_SB_ALIGN_CENTRE:
1693                         csrO = (scrollBar.bot - scrollBar.top) / 2;
1694                         break;
1695                       case R_SB_ALIGN_BOTTOM:
1696                         csrO = scrollBar.bot - scrollBar.top;
1697                         break;
1698                     }
1699                   if (scrollBar.style == R_SB_XTERM
1700                       || scrollbar_above_slider (ev.y)
1701                       || scrollbar_below_slider (ev.y))
1702                     scr_move_to (                                        scrollbar_position (ev.y) - csrO,
1703                                        scrollbar_size ());
1704                   scrollBar.setMotion ();
1705                   break;
1706
1707                 case Button1:
1708                   if (scrollbar_align == R_SB_ALIGN_CENTRE)
1709                     csrO = ev.y - scrollBar.top;
1710                   /* FALLTHROUGH */
1711
1712                 case Button3:
1713                   if (scrollBar.style != R_SB_XTERM)
1714                     {
1715                       if (scrollbar_above_slider (ev.y))
1716 # ifdef RXVT_SCROLL_FULL
1717                         scr_page (UP, TermWin.nrow - 1);
1718 # else
1719                         scr_page (UP, TermWin.nrow / 4);
1720 # endif
1721                       else if (scrollbar_below_slider (ev.y))
1722 # ifdef RXVT_SCROLL_FULL
1723                         scr_page (DN, TermWin.nrow - 1);
1724 # else
1725                         scr_page (DN, TermWin.nrow / 4);
1726 # endif
1727                       else
1728                         scrollBar.setMotion ();
1729                     }
1730                   else
1731                     {
1732                       scr_page ((ev.button == Button1 ? DN : UP),
1733                                 (TermWin.nrow
1734                                  * scrollbar_position (ev.y)
1735                                  / scrollbar_size ()));
1736                     }
1737                   break;
1738               }
1739         }
1740       return;
1741     }
1742 #if MENUBAR
1743   /*
1744    * Menubar window processing of button press
1745    */
1746   if (isMenuBarWindow (ev.window))
1747     menubar_control (ev);
1748 #endif
1749 }
1750
1751 void
1752 rxvt_term::button_release (XButtonEvent &ev)
1753 {
1754   int reportmode = 0;
1755
1756   csrO = 0;             /* reset csr Offset */
1757   if (!bypass_keystate)
1758     reportmode = !! (PrivateModes & PrivMode_mouse_report);
1759
1760   if (scrollbar_isUpDn ())
1761     {
1762       scrollBar.setIdle ();
1763       scrollbar_show (0);
1764 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
1765       refresh_type &= ~SMOOTH_REFRESH;
1766 #endif
1767
1768     }
1769 #ifdef SELECTION_SCROLLING
1770   pending_scroll_selection=0;
1771 #endif
1772   if (ev.window == TermWin.vt)
1773     {
1774       if (reportmode)
1775         {
1776           /* mouse report from vt window */
1777           /* don't report release of wheel "buttons" */
1778           if (ev.button >= 4)
1779             return;
1780 #ifdef MOUSE_REPORT_DOUBLECLICK
1781           /* only report the release of 'slow' single clicks */
1782           if (MEvent.button != AnyButton
1783               && (ev.button != MEvent.button
1784                   || (ev.time - MEvent.time
1785                       > MULTICLICK_TIME / 2)))
1786             {
1787               MEvent.clicks = 0;
1788               MEvent.button = AnyButton;
1789               mouse_report (ev);
1790             }
1791 #else                           /* MOUSE_REPORT_DOUBLECLICK */
1792           MEvent.button = AnyButton;
1793           mouse_report (ev);
1794 #endif                          /* MOUSE_REPORT_DOUBLECLICK */
1795           return;
1796         }
1797       /*
1798        * dumb hack to compensate for the failure of click-and-drag
1799        * when overriding mouse reporting
1800        */
1801       if (PrivateModes & PrivMode_mouse_report
1802           && bypass_keystate
1803           && ev.button == Button1 && MEvent.clicks <= 1)
1804         selection_extend (ev.x, ev.y, 0);
1805
1806       switch (ev.button)
1807         {
1808           case Button1:
1809           case Button3:
1810             selection_make (ev.time);
1811             break;
1812           case Button2:
1813             selection_request (ev.time, ev.x, ev.y);
1814             break;
1815 #ifdef MOUSE_WHEEL
1816           case Button4:
1817           case Button5:
1818             {
1819               int i;
1820               page_dirn v;
1821
1822               v = (ev.button == Button4) ? UP : DN;
1823               if (ev.state & ShiftMask)
1824                 i = 1;
1825               else if ((Options & Opt_mouseWheelScrollPage))
1826                 i = TermWin.nrow - 1;
1827               else
1828                 i = 5;
1829 # ifdef MOUSE_SLIP_WHEELING
1830               if (ev.state & ControlMask)
1831                 {
1832                   mouse_slip_wheel_speed += (v ? -1 : 1);
1833                   mouse_slip_wheel_delay = SCROLLBAR_CONTINUOUS_DELAY;
1834                 }
1835 # endif
1836 # ifdef JUMP_MOUSE_WHEEL
1837               scr_page (v, i);
1838               scr_refresh (SMOOTH_REFRESH);
1839               scrollbar_show (1);
1840 # else
1841               while (i--)
1842                 {
1843                   scr_page (v, 1);
1844                   scr_refresh (SMOOTH_REFRESH);
1845                   scrollbar_show (1);
1846                 }
1847 # endif
1848
1849             }
1850             break;
1851 #endif
1852
1853         }
1854     }
1855 #ifdef MENUBAR
1856   else if (isMenuBarWindow (ev.window))
1857     menubar_control (ev);
1858 #endif
1859 }
1860
1861
1862 #ifdef TRANSPARENT
1863 /*
1864  * Check our parents are still who we think they are.
1865  * Do transparency updates if required
1866  */
1867 int
1868 rxvt_term::check_our_parents ()
1869 {
1870   int             i, pchanged, aformat, have_pixmap, rootdepth;
1871   unsigned long   nitems, bytes_after;
1872   Atom            atype;
1873   unsigned char   *prop = NULL;
1874   Window          root, oldp, *list;
1875   Pixmap          rootpixmap = None;
1876   XWindowAttributes wattr, wrootattr;
1877
1878   pchanged = 0;
1879
1880   if (! (Options & Opt_transparent))
1881     return pchanged;    /* Don't try any more */
1882
1883   XGetWindowAttributes (display->display, display->root, &wrootattr);
1884   rootdepth = wrootattr.depth;
1885
1886   XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
1887   if (rootdepth != wattr.depth)
1888     {
1889       if (am_transparent)
1890         {
1891           pchanged = 1;
1892           XSetWindowBackground (display->display, TermWin.vt,
1893                                PixColors[Color_bg]);
1894           am_transparent = am_pixmap_trans = 0;
1895         }
1896       return pchanged;  /* Don't try any more */
1897     }
1898
1899   /* Get all X ops out of the queue so that our information is up-to-date. */
1900   XSync (display->display, False);
1901
1902   /*
1903    * Make the frame window set by the window manager have
1904    * the root background. Some window managers put multiple nested frame
1905    * windows for each client, so we have to take care about that.
1906    */
1907   i = (xa[XA_XROOTPMAPID] != 0
1908        && (XGetWindowProperty (display->display, display->root, xa[XA_XROOTPMAPID],
1909                               0L, 1L, False, XA_PIXMAP, &atype, &aformat,
1910                               &nitems, &bytes_after, &prop) == Success));
1911   if (!i || prop == NULL)
1912     have_pixmap = 0;
1913   else
1914     {
1915       have_pixmap = 1;
1916       rootpixmap = * ((Pixmap *)prop);
1917       XFree (prop);
1918     }
1919   if (have_pixmap)
1920     {
1921       /*
1922        * Copy display->root pixmap transparency
1923        */
1924       int sx, sy, nx, ny;
1925       unsigned int nw, nh;
1926       Window cr;
1927       XImage *image;
1928       GC gc;
1929       XGCValues gcvalue;
1930
1931       XTranslateCoordinates (display->display, TermWin.parent[0], display->root,
1932                             0, 0, &sx, &sy, &cr);
1933       nw = (unsigned int)szHint.width;
1934       nh = (unsigned int)szHint.height;
1935       nx = ny = 0;
1936
1937       if (sx < 0)
1938         {
1939           nw += sx;
1940           nx = -sx;
1941           sx = 0;
1942         }
1943
1944       if (sy < 0)
1945         {
1946           nh += sy;
1947           ny = -sy;
1948           sy = 0;
1949         }
1950
1951       MIN_IT (nw, (unsigned int) (wrootattr.width - sx));
1952       MIN_IT (nh, (unsigned int) (wrootattr.height - sy));
1953       allowedxerror = -1;
1954       image = XGetImage (display->display, rootpixmap, sx, sy, nw, nh, AllPlanes,
1955                         ZPixmap);
1956       /* XXX: handle BadMatch - usually because we're outside the pixmap */
1957       /* XXX: may need a delay here? */
1958       allowedxerror = 0;
1959       if (image == NULL)
1960         {
1961           if (am_transparent && am_pixmap_trans)
1962             {
1963               pchanged = 1;
1964               if (TermWin.pixmap != None)
1965                 {
1966                   XFreePixmap (display->display, TermWin.pixmap);
1967                   TermWin.pixmap = None;
1968                 }
1969             }
1970           am_pixmap_trans = 0;
1971         }
1972       else
1973         {
1974           if (TermWin.pixmap != None)
1975             XFreePixmap (display->display, TermWin.pixmap);
1976           TermWin.pixmap = XCreatePixmap (display->display, TermWin.vt,
1977                                          (unsigned int)szHint.width,
1978                                          (unsigned int)szHint.height,
1979                                          (unsigned int)image->depth);
1980           gc = XCreateGC (display->display, TermWin.vt, 0UL, &gcvalue);
1981           XPutImage (display->display, TermWin.pixmap, gc, image, 0, 0,
1982                     nx, ny, (unsigned int)image->width,
1983                     (unsigned int)image->height);
1984           XFreeGC (display->display, gc);
1985           XDestroyImage (image);
1986           XSetWindowBackgroundPixmap (display->display, TermWin.vt,
1987                                      TermWin.pixmap);
1988           if (!am_transparent || !am_pixmap_trans)
1989             pchanged = 1;
1990           am_transparent = am_pixmap_trans = 1;
1991         }
1992     }
1993
1994   if (!am_pixmap_trans)
1995     {
1996       unsigned int    n;
1997       /*
1998        * InheritPixmap transparency
1999        */
2000       D_X ((stderr, "InheritPixmap Seeking to  %08lx", display->root));
2001       for (i = 1; i < (int) (sizeof (TermWin.parent) / sizeof (Window));
2002            i++)
2003         {
2004           oldp = TermWin.parent[i];
2005           XQueryTree (display->display, TermWin.parent[i - 1], &root,
2006                      &TermWin.parent[i], &list, &n);
2007           XFree (list);
2008           D_X ((stderr, "InheritPixmap Parent[%d] = %08lx", i, TermWin.parent[i]));
2009           if (TermWin.parent[i] == display->root)
2010             {
2011               if (oldp != None)
2012                 pchanged = 1;
2013               break;
2014             }
2015           if (oldp != TermWin.parent[i])
2016             pchanged = 1;
2017         }
2018
2019       n = 0;
2020
2021       if (pchanged)
2022         {
2023           for (; n < (unsigned int)i; n++)
2024             {
2025               XGetWindowAttributes (display->display, TermWin.parent[n], &wattr);
2026               D_X ((stderr, "InheritPixmap Checking Parent[%d]: %s", n, (wattr.depth == rootdepth && wattr.class != InputOnly) ? "OK" : "FAIL"));
2027               if (wattr.depth != rootdepth || wattr.c_class == InputOnly)
2028                 {
2029                   n = (int) (sizeof (TermWin.parent) / sizeof (Window)) + 1;
2030                   break;
2031                 }
2032             }
2033         }
2034
2035       if (n > (int) (sizeof (TermWin.parent)
2036                     / sizeof (TermWin.parent[0])))
2037         {
2038           D_X ((stderr, "InheritPixmap Turning off"));
2039           XSetWindowBackground (display->display, TermWin.parent[0],
2040                                PixColors[Color_fg]);
2041           XSetWindowBackground (display->display, TermWin.vt,
2042                                PixColors[Color_bg]);
2043           am_transparent = 0;
2044           /* XXX: also turn off Opt_transparent? */
2045         }
2046       else
2047         {
2048           /* wait (an arbitrary period) for the WM to do its thing
2049            * needed for fvwm2.2.2 (and before?) */
2050 # ifdef HAVE_NANOSLEEP
2051           struct timespec rqt;
2052
2053           rqt.tv_sec = 1;
2054           rqt.tv_nsec = 0;
2055           nanosleep (&rqt, NULL);
2056 # else
2057           sleep (1);
2058 # endif
2059           D_X ((stderr, "InheritPixmap Turning on (%d parents)", i - 1));
2060           for (n = 0; n < (unsigned int)i; n++)
2061             XSetWindowBackgroundPixmap (display->display, TermWin.parent[n],
2062                                        ParentRelative);
2063           XSetWindowBackgroundPixmap (display->display, TermWin.vt,
2064                                      ParentRelative);
2065           am_transparent = 1;
2066         }
2067
2068       for (; i < (int) (sizeof (TermWin.parent) / sizeof (Window)); i++)
2069         TermWin.parent[i] = None;
2070     }
2071   return pchanged;
2072 }
2073 #endif
2074
2075 /*}}} */
2076
2077 /*{{{ print pipe */
2078 /*----------------------------------------------------------------------*/
2079 #ifdef PRINTPIPE
2080 FILE *
2081 rxvt_term::popen_printer ()
2082 {
2083   FILE *stream = popen (rs[Rs_print_pipe], "w");
2084
2085   if (stream == NULL)
2086     rxvt_print_error ("can't open printer pipe");
2087   return stream;
2088 }
2089
2090 int
2091 rxvt_term::pclose_printer (FILE *stream)
2092 {
2093   fflush (stream);
2094   /* pclose () reported not to work on SunOS 4.1.3 */
2095 # if defined (__sun__)          /* TODO: RESOLVE THIS */
2096   /* pclose works provided SIGCHLD handler uses waitpid */
2097   return pclose (stream);       /* return fclose (stream); */
2098 # else
2099   return pclose (stream);
2100 # endif
2101 }
2102
2103 /*
2104  * simulate attached vt100 printer
2105  */
2106 void
2107 rxvt_term::process_print_pipe ()
2108 {
2109   int             done;
2110   FILE           *fd;
2111
2112   if ((fd = popen_printer ()) == NULL)
2113     return;
2114
2115   /*
2116    * Send all input to the printer until either ESC[4i or ESC[?4i
2117    * is received.
2118    */
2119   for (done = 0; !done;)
2120     {
2121       unsigned char   buf[8];
2122       unsigned char   ch;
2123       unsigned int    i, len;
2124
2125       if ((ch = cmd_getc ()) != C0_ESC)
2126         {
2127           if (putc (ch, fd) == EOF)
2128             break;              /* done = 1 */
2129         }
2130       else
2131         {
2132           len = 0;
2133           buf[len++] = ch;
2134
2135           if ((buf[len++] = cmd_getc ()) == '[')
2136             {
2137               if ((ch = cmd_getc ()) == '?')
2138                 {
2139                   buf[len++] = '?';
2140                   ch = cmd_getc ();
2141                 }
2142               if ((buf[len++] = ch) == '4')
2143                 {
2144                   if ((buf[len++] = cmd_getc ()) == 'i')
2145                     break;      /* done = 1 */
2146                 }
2147             }
2148           for (i = 0; i < len; i++)
2149             if (putc (buf[i], fd) == EOF)
2150               {
2151                 done = 1;
2152                 break;
2153               }
2154         }
2155     }
2156   pclose_printer (fd);
2157 }
2158 #endif                          /* PRINTPIPE */
2159 /*}}} */
2160
2161 /* *INDENT-OFF* */
2162 enum {
2163   C1_40 = 0x40,
2164   C1_41 , C1_BPH, C1_NBH, C1_44 , C1_NEL, C1_SSA, C1_ESA,
2165   C1_HTS, C1_HTJ, C1_VTS, C1_PLD, C1_PLU, C1_RI , C1_SS2, C1_SS3,
2166   C1_DCS, C1_PU1, C1_PU2, C1_STS, C1_CCH, C1_MW , C1_SPA, C1_EPA,
2167   C1_SOS, C1_59 , C1_SCI, C1_CSI, CS_ST , C1_OSC, C1_PM , C1_APC
2168 };
2169 /* *INDENT-ON* */
2170
2171 /*{{{ process non-printing single characters */
2172 void
2173 rxvt_term::process_nonprinting (unsigned char ch)
2174 {
2175   switch (ch)
2176     {
2177       case C0_ENQ:      /* terminal Status */
2178         if (rs[Rs_answerbackstring])
2179           tt_write (
2180             (const unsigned char *)rs[Rs_answerbackstring],
2181             (unsigned int)STRLEN (rs[Rs_answerbackstring]));
2182         else
2183           tt_write ((unsigned char *)VT100_ANS,
2184                    (unsigned int)STRLEN (VT100_ANS));
2185         break;
2186       case C0_BEL:      /* bell */
2187         scr_bell ();
2188         break;
2189       case C0_BS:               /* backspace */
2190         scr_backspace ();
2191         break;
2192       case C0_HT:               /* tab */
2193         scr_tab (1);
2194         break;
2195       case C0_CR:               /* carriage return */
2196         scr_gotorc (0, 0, R_RELATIVE);
2197         break;
2198       case C0_VT:               /* vertical tab, form feed */
2199       case C0_FF:
2200       case C0_LF:               /* line feed */
2201         scr_index (UP);
2202         break;
2203       case C0_SO:               /* shift out - acs */
2204         scr_charset_choose (1);
2205         break;
2206       case C0_SI:               /* shift in - acs */
2207         scr_charset_choose (0);
2208         break;
2209     }
2210 }
2211 /*}}} */
2212
2213
2214 /*{{{ process VT52 escape sequences */
2215 void
2216 rxvt_term::process_escape_vt52 (unsigned char ch)
2217 {
2218   int row, col;
2219
2220   switch (ch)
2221     {
2222       case 'A':         /* cursor up */
2223         scr_gotorc (-1, 0, R_RELATIVE | C_RELATIVE);
2224         break;
2225       case 'B':         /* cursor down */
2226         scr_gotorc (1, 0, R_RELATIVE | C_RELATIVE);
2227         break;
2228       case 'C':         /* cursor right */
2229         scr_gotorc (0, 1, R_RELATIVE | C_RELATIVE);
2230         break;
2231       case 'D':         /* cursor left */
2232         scr_gotorc (0, -1, R_RELATIVE | C_RELATIVE);
2233         break;
2234       case 'H':         /* cursor home */
2235         scr_gotorc (0, 0, 0);
2236         break;
2237       case 'I':         /* cursor up and scroll down if needed */
2238         scr_index (DN);
2239         break;
2240       case 'J':         /* erase to end of screen */
2241         scr_erase_screen (0);
2242         break;
2243       case 'K':         /* erase to end of line */
2244         scr_erase_line (0);
2245         break;
2246       case 'Y':                 /* move to specified row and col */
2247         /* full command is 'ESC Y row col' where row and col
2248          * are encoded by adding 32 and sending the ascii
2249          * character.  eg. SPACE = 0, '+' = 13, '0' = 18,
2250          * etc. */
2251         row = cmd_getc () - ' ';
2252         col = cmd_getc () - ' ';
2253         scr_gotorc (row, col, 0);
2254         break;
2255       case 'Z':         /* identify the terminal type */
2256         tt_printf ("\033/Z");   /* I am a VT100 emulating a VT52 */
2257         break;
2258       case '<':         /* turn off VT52 mode */
2259         PrivMode (0, PrivMode_vt52);
2260         break;
2261       case 'F':         /* use special graphics character set */
2262       case 'G':           /* use regular character set */
2263         /* unimplemented */
2264         break;
2265       case '=':         /* use alternate keypad mode */
2266       case '>':           /* use regular keypad mode */
2267         /* unimplemented */
2268         break;
2269     }
2270 }
2271 /*}}} */
2272
2273
2274 /*{{{ process escape sequences */
2275 void
2276 rxvt_term::process_escape_seq ()
2277 {
2278   unsigned char   ch = cmd_getc ();
2279
2280   if (PrivateModes & PrivMode_vt52)
2281     {
2282       process_escape_vt52 (ch);
2283       return;
2284     }
2285
2286   switch (ch)
2287     {
2288         /* case 1:        do_tek_mode (); break; */
2289       case '#':
2290         if (cmd_getc () == '8')
2291           scr_E ();
2292         break;
2293       case '(':
2294         scr_charset_set (0, (unsigned int)cmd_getc ());
2295         break;
2296       case ')':
2297         scr_charset_set (1, (unsigned int)cmd_getc ());
2298         break;
2299       case '*':
2300         scr_charset_set (2, (unsigned int)cmd_getc ());
2301         break;
2302       case '+':
2303         scr_charset_set (3, (unsigned int)cmd_getc ());
2304         break;
2305 #ifndef NO_FRILLS
2306       case '6':
2307         scr_backindex ();
2308         break;
2309 #endif
2310       case '7':
2311         scr_cursor (SAVE);
2312         break;
2313       case '8':
2314         scr_cursor (RESTORE);
2315         break;
2316 #ifndef NO_FRILLS
2317       case '9':
2318         scr_forwardindex ();
2319         break;
2320 #endif
2321       case '=':
2322       case '>':
2323         PrivMode ((ch == '='), PrivMode_aplKP);
2324         break;
2325
2326       case C1_40:
2327         cmd_getc ();
2328         break;
2329       case C1_44:
2330         scr_index (UP);
2331         break;
2332
2333         /* 8.3.87: NEXT LINE */
2334       case C1_NEL:              /* ESC E */
2335         {
2336           uint32_t nlcr[] = { '\n', '\r' };
2337           scr_add_lines (nlcr, 1, 2);
2338         }
2339         break;
2340
2341         /* kidnapped escape sequence: Should be 8.3.48 */
2342       case C1_ESA:              /* ESC G */
2343         process_graphics ();
2344         break;
2345
2346         /* 8.3.63: CHARACTER TABULATION SET */
2347       case C1_HTS:              /* ESC H */
2348         scr_set_tab (1);
2349         break;
2350
2351         /* 8.3.105: REVERSE LINE FEED */
2352       case C1_RI:                       /* ESC M */
2353         scr_index (DN);
2354         break;
2355
2356         /* 8.3.142: SINGLE-SHIFT TWO */
2357         /*case C1_SS2: scr_single_shift (2);   break; */
2358
2359         /* 8.3.143: SINGLE-SHIFT THREE */
2360         /*case C1_SS3: scr_single_shift (3);   break; */
2361
2362         /* 8.3.27: DEVICE CONTROL STRING */
2363       case C1_DCS:              /* ESC P */
2364         process_dcs_seq ();
2365         break;
2366
2367         /* 8.3.110: SINGLE CHARACTER INTRODUCER */
2368       case C1_SCI:              /* ESC Z */
2369         tt_write ((const unsigned char *)ESCZ_ANSWER,
2370                  (unsigned int) (sizeof (ESCZ_ANSWER) - 1));
2371         break;                  /* steal obsolete ESC [ c */
2372
2373         /* 8.3.16: CONTROL SEQUENCE INTRODUCER */
2374       case C1_CSI:              /* ESC [ */
2375         process_csi_seq ();
2376         break;
2377
2378         /* 8.3.90: OPERATING SYSTEM COMMAND */
2379       case C1_OSC:              /* ESC ] */
2380         process_osc_seq ();
2381         break;
2382
2383         /* 8.3.106: RESET TO INITIAL STATE */
2384       case 'c':
2385         scr_poweron ();
2386         scrollbar_show (1);
2387         break;
2388
2389         /* 8.3.79: LOCKING-SHIFT TWO (see ISO2022) */
2390       case 'n':
2391         scr_charset_choose (2);
2392         break;
2393
2394         /* 8.3.81: LOCKING-SHIFT THREE (see ISO2022) */
2395       case 'o':
2396         scr_charset_choose (3);
2397         break;
2398     }
2399 }
2400 /*}}} */
2401
2402 /*{{{ process CONTROL SEQUENCE INTRODUCER (CSI) sequences `ESC[' */
2403 /* *INDENT-OFF* */
2404 enum {
2405   CSI_ICH = 0x40,
2406   CSI_CUU, CSI_CUD, CSI_CUF, CSI_CUB, CSI_CNL, CSI_CPL, CSI_CHA,
2407   CSI_CUP, CSI_CHT, CSI_ED , CSI_EL , CSI_IL , CSI_DL , CSI_EF , CSI_EA ,
2408   CSI_DCH, CSI_SEE, CSI_CPR, CSI_SU , CSI_SD , CSI_NP , CSI_PP , CSI_CTC,
2409   CSI_ECH, CSI_CVT, CSI_CBT, CSI_SRS, CSI_PTX, CSI_SDS, CSI_SIMD, CSI_5F,
2410   CSI_HPA, CSI_HPR, CSI_REP, CSI_DA , CSI_VPA, CSI_VPR, CSI_HVP, CSI_TBC,
2411   CSI_SM , CSI_MC , CSI_HPB, CSI_VPB, CSI_RM , CSI_SGR, CSI_DSR, CSI_DAQ,
2412   CSI_70 , CSI_71 , CSI_72 , CSI_73 , CSI_74 , CSI_75 , CSI_76 , CSI_77 ,
2413   CSI_78 , CSI_79 , CSI_7A , CSI_7B , CSI_7C , CSI_7D , CSI_7E , CSI_7F
2414 };
2415
2416 #define make_byte(b7,b6,b5,b4,b3,b2,b1,b0)                      \
2417     (((b7) << 7) | ((b6) << 6) | ((b5) << 5) | ((b4) << 4)      \
2418      | ((b3) << 3) | ((b2) << 2) | ((b1) << 1) | (b0))
2419 #define get_byte_array_bit(array, bit)                          \
2420     (!! ((array)[ (bit) / 8] & (128 >> ((bit) & 7))))
2421
2422 const unsigned char csi_defaults[] =
2423   {
2424     make_byte (1,1,1,1,1,1,1,1),        /* @, A, B, C, D, E, F, G, */
2425     make_byte (1,1,0,0,1,1,0,0),        /* H, I, J, K, L, M, N, O, */
2426     make_byte (1,0,1,1,1,1,1,0),        /* P, Q, R, S, T, U, V, W, */
2427     make_byte (1,1,1,0,0,0,1,0),        /* X, Y, Z, [, \, ], ^, _, */
2428     make_byte (1,1,1,0,1,1,1,0),        /* `, a, b, c, d, e, f, g, */
2429     make_byte (0,0,1,1,0,0,0,0),        /* h, i, j, k, l, m, n, o, */
2430     make_byte (0,0,0,0,0,0,0,0),        /* p, q, r, s, t, u, v, w, */
2431     make_byte (0,0,0,0,0,0,0,0) /* x, y, z, {, |, }, ~,    */
2432   };
2433 /* *INDENT-ON* */
2434
2435 void
2436 rxvt_term::process_csi_seq ()
2437 {
2438   unsigned char   ch, priv, i;
2439   unsigned int    nargs, p;
2440   int             n, ndef;
2441   int             arg[ESC_ARGS];
2442
2443   for (nargs = ESC_ARGS; nargs > 0;)
2444     arg[--nargs] = 0;
2445
2446   priv = 0;
2447   ch = cmd_getc ();
2448   if (ch >= '<' && ch <= '?')
2449     {   /* '<' '=' '>' '?' */
2450       priv = ch;
2451       ch = cmd_getc ();
2452     }
2453   /* read any numerical arguments */
2454   for (n = -1; ch < CSI_ICH; )
2455     {
2456       if (isdigit (ch))
2457         {
2458           if (n < 0)
2459             n = ch - '0';
2460           else
2461             n = n * 10 + ch - '0';
2462         }
2463       else if (ch == ';')
2464         {
2465           if (nargs < ESC_ARGS)
2466             arg[nargs++] = n;
2467           n = -1;
2468         }
2469       else if (ch == '\b')
2470         {
2471           scr_backspace ();
2472         }
2473       else if (ch == C0_ESC)
2474         {
2475           process_escape_seq ();
2476           return;
2477         }
2478       else if (ch < ' ')
2479         {
2480           process_nonprinting (ch);
2481         }
2482       ch = cmd_getc ();
2483     }
2484
2485   if (ch > CSI_7F)
2486     return;
2487
2488   if (nargs < ESC_ARGS)
2489     arg[nargs++] = n;
2490
2491   i = ch - CSI_ICH;
2492   ndef = get_byte_array_bit (csi_defaults, i);
2493   for (p = 0; p < nargs; p++)
2494     if (arg[p] == -1)
2495       arg[p] = ndef;
2496
2497 #ifdef DEBUG_CMD
2498   fprintf (stderr, "CSI ");
2499   for (p = 0; p < nargs; p++)
2500     fprintf (stderr, "%d%s", arg[p], p < nargs - 1 ? ";" : "");
2501   fprintf (stderr, "%c\n", ch);
2502 #endif
2503
2504   /*
2505    * private mode handling
2506    */
2507   if (priv)
2508     {
2509       switch (priv)
2510         {
2511           case '>':
2512             if (ch == CSI_DA)   /* secondary device attributes */
2513               tt_printf ("\033[>%d;%-.8s;0c", 'R', VSTRING);
2514             break;
2515           case '?':
2516             if (ch == 'h' || ch == 'l' || ch == 'r' || ch == 's' || ch == 't')
2517               process_terminal_mode (ch, priv, nargs, arg);
2518             break;
2519         }
2520       return;
2521     }
2522
2523   switch (ch)
2524     {
2525         /*
2526          * ISO/IEC 6429:1992 (E) CSI sequences (defaults in parentheses)
2527          */
2528 #ifdef PRINTPIPE
2529       case CSI_MC:              /* 8.3.83: (0) MEDIA COPY */
2530         switch (arg[0])
2531           {
2532             case 0:                     /* initiate transfer to primary aux device */
2533               scr_printscreen (0);
2534               break;
2535             case 5:                     /* start relay to primary aux device */
2536               process_print_pipe ();
2537               break;
2538           }
2539         break;
2540 #endif
2541
2542       case CSI_CUU:             /* 8.3.22: (1) CURSOR UP */
2543       case CSI_VPR:             /* 8.3.161: (1) LINE POSITION FORWARD */
2544         arg[0] = -arg[0];
2545         /* FALLTHROUGH */
2546       case CSI_CUD:             /* 8.3.19: (1) CURSOR DOWN */
2547       case CSI_VPB:             /* 8.3.160: (1) LINE POSITION BACKWARD */
2548         scr_gotorc (arg[0], 0, RELATIVE);
2549         break;
2550
2551       case CSI_CUB:             /* 8.3.18: (1) CURSOR LEFT */
2552       case CSI_HPB:             /* 8.3.59: (1) CHARACTER POSITION BACKWARD */
2553 #ifdef ISO6429
2554         arg[0] = -arg[0];
2555 #else                           /* emulate common DEC VTs */
2556         arg[0] = arg[0] ? -arg[0] : -1;
2557 #endif
2558         /* FALLTHROUGH */
2559       case CSI_CUF:             /* 8.3.20: (1) CURSOR RIGHT */
2560       case CSI_HPR:             /* 8.3.60: (1) CHARACTER POSITION FORWARD */
2561 #ifdef ISO6429
2562         scr_gotorc (0, arg[0], RELATIVE);
2563 #else                           /* emulate common DEC VTs */
2564         scr_gotorc (0, arg[0] ? arg[0] : 1, RELATIVE);
2565 #endif
2566         break;
2567
2568       case CSI_CPL:             /* 8.3.13: (1) CURSOR PRECEDING LINE */
2569         arg[0] = -arg[0];
2570         /* FALLTHROUGH */
2571       case CSI_CNL:             /* 8.3.12: (1) CURSOR NEXT LINE */
2572         scr_gotorc (arg[0], 0, R_RELATIVE);
2573         break;
2574
2575       case CSI_CHA:             /* 8.3.9: (1) CURSOR CHARACTER ABSOLUTE */
2576       case CSI_HPA:             /* 8.3.58: (1) CURSOR POSITION ABSOLUTE */
2577         scr_gotorc (0, arg[0] - 1, R_RELATIVE);
2578         break;
2579
2580       case CSI_VPA:             /* 8.3.159: (1) LINE POSITION ABSOLUTE */
2581         scr_gotorc (arg[0] - 1, 0, C_RELATIVE);
2582         break;
2583
2584       case CSI_CUP:             /* 8.3.21: (1,1) CURSOR POSITION */
2585       case CSI_HVP:             /* 8.3.64: (1,1) CHARACTER AND LINE POSITION */
2586         scr_gotorc (arg[0] - 1, nargs < 2 ? 0 : (arg[1] - 1), 0);
2587         break;
2588
2589       case CSI_CBT:             /* 8.3.7: (1) CURSOR BACKWARD TABULATION */
2590         arg[0] = -arg[0];
2591         /* FALLTHROUGH */
2592       case CSI_CHT:             /* 8.3.10: (1) CURSOR FORWARD TABULATION */
2593         scr_tab (arg[0]);
2594         break;
2595
2596       case CSI_ED:              /* 8.3.40: (0) ERASE IN PAGE */
2597         scr_erase_screen (arg[0]);
2598         break;
2599
2600       case CSI_EL:              /* 8.3.42: (0) ERASE IN LINE */
2601         scr_erase_line (arg[0]);
2602         break;
2603
2604       case CSI_ICH:             /* 8.3.65: (1) INSERT CHARACTER */
2605         scr_insdel_chars (arg[0], INSERT);
2606         break;
2607
2608       case CSI_IL:              /* 8.3.68: (1) INSERT LINE */
2609         scr_insdel_lines (arg[0], INSERT);
2610         break;
2611
2612       case CSI_DL:              /* 8.3.33: (1) DELETE LINE */
2613         scr_insdel_lines (arg[0], DELETE);
2614         break;
2615
2616       case CSI_ECH:             /* 8.3.39: (1) ERASE CHARACTER */
2617         scr_insdel_chars (arg[0], ERASE);
2618         break;
2619
2620       case CSI_DCH:             /* 8.3.26: (1) DELETE CHARACTER */
2621         scr_insdel_chars (arg[0], DELETE);
2622         break;
2623
2624       case CSI_SD:              /* 8.3.114: (1) SCROLL DOWN */
2625         arg[0] = -arg[0];
2626         /* FALLTHROUGH */
2627       case CSI_SU:              /* 8.3.148: (1) SCROLL UP */
2628         scr_scroll_text (screen.tscroll, screen.bscroll, arg[0], 0);
2629         break;
2630
2631       case CSI_DA:              /* 8.3.24: (0) DEVICE ATTRIBUTES */
2632         tt_write ((const unsigned char *)VT100_ANS,
2633                  (unsigned int) (sizeof (VT100_ANS) - 1));
2634         break;
2635
2636       case CSI_SGR:             /* 8.3.118: (0) SELECT GRAPHIC RENDITION */
2637         process_sgr_mode (nargs, arg);
2638         break;
2639
2640       case CSI_DSR:             /* 8.3.36: (0) DEVICE STATUS REPORT */
2641         switch (arg[0])
2642           {
2643             case 5:                     /* DSR requested */
2644               tt_printf ("\033[0n");
2645               break;
2646             case 6:                     /* CPR requested */
2647               scr_report_position ();
2648               break;
2649 #if defined (ENABLE_DISPLAY_ANSWER)
2650             case 7:                     /* unofficial extension */
2651               tt_printf ("%-.250s\n", rs[Rs_display_name]);
2652               break;
2653 #endif
2654             case 8:                     /* unofficial extension */
2655               xterm_seq (XTerm_title, RESNAME "-" VERSION, CHAR_ST);
2656               break;
2657           }
2658         break;
2659
2660       case CSI_TBC:             /* 8.3.155: (0) TABULATION CLEAR */
2661         switch (arg[0])
2662           {
2663             case 0:                     /* char tab stop cleared at active position */
2664               scr_set_tab (0);
2665               break;
2666               /* case 1: */             /* line tab stop cleared in active line */
2667               /* case 2: */             /* char tab stops cleared in active line */
2668             case 3:                     /* all char tab stops are cleared */
2669               /* case 4: */             /* all line tab stops are cleared */
2670             case 5:                     /* all tab stops are cleared */
2671               scr_set_tab (-1);
2672               break;
2673           }
2674         break;
2675
2676       case CSI_CTC:             /* 8.3.17: (0) CURSOR TABULATION CONTROL */
2677         switch (arg[0])
2678           {
2679             case 0:                     /* char tab stop set at active position */
2680               scr_set_tab (1);
2681               break;            /* = ESC H */
2682               /* case 1: */             /* line tab stop set at active line */
2683             case 2:                     /* char tab stop cleared at active position */
2684               scr_set_tab (0);
2685               break;            /* = ESC [ 0 g */
2686               /* case 3: */             /* line tab stop cleared at active line */
2687               /* case 4: */             /* char tab stops cleared at active line */
2688             case 5:                     /* all char tab stops are cleared */
2689               scr_set_tab (-1);
2690               break;            /* = ESC [ 3 g */
2691               /* case 6: */             /* all line tab stops are cleared */
2692           }
2693         break;
2694
2695       case CSI_RM:              /* 8.3.107: RESET MODE */
2696         if (arg[0] == 4)
2697           scr_insert_mode (0);
2698         break;
2699
2700       case CSI_SM:              /* 8.3.126: SET MODE */
2701         if (arg[0] == 4)
2702           scr_insert_mode (1);
2703         break;
2704
2705         /*
2706          * PRIVATE USE beyond this point.  All CSI_7? sequences here
2707          */
2708       case CSI_72:              /* DECSTBM: set top and bottom margins */
2709         if (nargs == 1)
2710           scr_scroll_region (arg[0] - 1, MAX_ROWS - 1);
2711         else if (nargs == 0 || arg[0] >= arg[1])
2712           scr_scroll_region (0, MAX_ROWS - 1);
2713         else
2714           scr_scroll_region (arg[0] - 1, arg[1] - 1);
2715         break;
2716
2717       case CSI_73:
2718         scr_cursor (SAVE);
2719         break;
2720       case CSI_75:
2721         scr_cursor (RESTORE);
2722         break;
2723
2724 #ifndef NO_FRILLS
2725       case CSI_74:
2726         process_window_ops (arg, nargs);
2727         break;
2728 #endif
2729
2730       case CSI_78:              /* DECREQTPARM */
2731         if (arg[0] == 0 || arg[0] == 1)
2732           tt_printf ("\033[%d;1;1;128;128;1;0x", arg[0] + 2);
2733         /* FALLTHROUGH */
2734
2735       default:
2736         break;
2737     }
2738 }
2739 /*}}} */
2740
2741 #ifndef NO_FRILLS
2742 /* ARGSUSED */
2743 void
2744 rxvt_term::process_window_ops (const int *args, unsigned int nargs)
2745 {
2746   int             x, y;
2747 #if 0
2748   char           *s;
2749 #endif
2750   XWindowAttributes wattr;
2751   Window          wdummy;
2752
2753   if (nargs == 0)
2754     return;
2755   switch (args[0])
2756     {
2757         /*
2758          * commands
2759          */
2760       case 1:                   /* deiconify window */
2761         XMapWindow (display->display, TermWin.parent[0]);
2762         break;
2763       case 2:                   /* iconify window */
2764         XIconifyWindow (display->display, TermWin.parent[0],
2765                        DefaultScreen (display->display));
2766         break;
2767       case 3:                   /* set position (pixels) */
2768         XMoveWindow (display->display, TermWin.parent[0], args[1], args[2]);
2769         break;
2770       case 4:                   /* set size (pixels) */
2771         set_widthheight ((unsigned int)args[2], (unsigned int)args[1]);
2772         break;
2773       case 5:                   /* raise window */
2774         XRaiseWindow (display->display, TermWin.parent[0]);
2775         break;
2776       case 6:                   /* lower window */
2777         XLowerWindow (display->display, TermWin.parent[0]);
2778         break;
2779       case 7:                   /* refresh window */
2780         scr_touch (true);
2781         break;
2782       case 8:                   /* set size (chars) */
2783         set_widthheight ((unsigned int) (args[2] * TermWin.fwidth),
2784                          (unsigned int) (args[1] * TermWin.fheight));
2785         break;
2786       default:
2787         if (args[0] >= 24)      /* set height (chars) */
2788           set_widthheight ((unsigned int)TermWin.width,
2789                            (unsigned int) (args[1] * TermWin.fheight));
2790         break;
2791         /*
2792          * reports - some output format copied from XTerm
2793          */
2794       case 11:                  /* report window state */
2795         XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
2796         tt_printf ("\033[%dt", wattr.map_state == IsViewable ? 1 : 2);
2797         break;
2798       case 13:                  /* report window position */
2799         XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
2800         XTranslateCoordinates (display->display, TermWin.parent[0], wattr.root,
2801                               -wattr.border_width, -wattr.border_width,
2802                               &x, &y, &wdummy);
2803         tt_printf ("\033[3;%d;%dt", x, y);
2804         break;
2805       case 14:                  /* report window size (pixels) */
2806         XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
2807         tt_printf ("\033[4;%d;%dt", wattr.height, wattr.width);
2808         break;
2809       case 18:                  /* report window size (chars) */
2810         tt_printf ("\033[8;%d;%dt", TermWin.nrow, TermWin.ncol);
2811         break;
2812 #if 0 /* XXX: currently disabled due to security concerns */
2813       case 20:                  /* report icon label */
2814         XGetIconName (display->display, TermWin.parent[0], &s);
2815         tt_printf ("\033]L%-.200s\234", s ? s : "");    /* 8bit ST */
2816         break;
2817       case 21:                  /* report window title */
2818         XFetchName (display->display, TermWin.parent[0], &s);
2819         tt_printf ("\033]l%-.200s\234", s ? s : "");    /* 8bit ST */
2820         break;
2821 #endif
2822
2823     }
2824 }
2825 #endif
2826
2827 /*----------------------------------------------------------------------*/
2828 /*
2829  * get input up until STRING TERMINATOR (or BEL)
2830  * ends_how is terminator used.  returned input must be free ()d
2831  */
2832 unsigned char  *
2833 rxvt_term::get_to_st (unsigned char *ends_how)
2834 {
2835   int             seen_esc = 0; /* seen escape? */
2836   unsigned int    n = 0;
2837   unsigned char  *s;
2838   unsigned char   ch, string[STRING_MAX];
2839
2840   for (; (ch = cmd_getc ());)
2841     {
2842       if (ch == C0_BEL
2843           || ch == CHAR_ST
2844           || (ch == 0x5c && seen_esc))  /* 7bit ST */
2845         break;
2846       if (ch == C0_ESC)
2847         {
2848           seen_esc = 1;
2849           continue;
2850         }
2851       else if (ch == '\t')
2852         ch = ' ';       /* translate '\t' to space */
2853       else if (ch < 0x08 || (ch > 0x0d && ch < 0x20))
2854         return NULL;    /* other control character - exit */
2855       if (n < sizeof (string) - 1)
2856         string[n++] = ch;
2857       seen_esc = 0;
2858     }
2859   string[n++] = '\0';
2860   if ((s = (unsigned char *)rxvt_malloc (n)) == NULL)
2861     return NULL;
2862   *ends_how = (ch == 0x5c ? C0_ESC : ch);
2863   STRNCPY (s, string, n);
2864   return s;
2865 }
2866
2867 /*----------------------------------------------------------------------*/
2868 /*
2869  * process DEVICE CONTROL STRING `ESC P ... (ST|BEL)' or `0x90 ... (ST|BEL)'
2870  */
2871 void
2872 rxvt_term::process_dcs_seq ()
2873 {
2874   unsigned char    eh, *s;
2875   /*
2876    * Not handled yet
2877    */
2878   s = get_to_st (&eh);
2879   if (s)
2880     free (s);
2881   return;
2882 }
2883
2884 /*----------------------------------------------------------------------*/
2885 /*
2886  * process OPERATING SYSTEM COMMAND sequence `ESC ] Ps ; Pt (ST|BEL)'
2887  */
2888 void
2889 rxvt_term::process_osc_seq ()
2890 {
2891   unsigned char   ch, eh, *s;
2892   int             arg;
2893
2894   ch = cmd_getc ();
2895   for (arg = 0; isdigit (ch); ch = cmd_getc ())
2896     arg = arg * 10 + (ch - '0');
2897
2898   if (ch == ';')
2899     {
2900       s = get_to_st (&eh);
2901       if (s)
2902         {
2903           /*
2904            * rxvt_menubar_dispatch () violates the constness of the string,
2905            * so do it here
2906            */
2907           if (arg == XTerm_Menu)
2908 #if 0 /* XXX: currently disabled due to security concerns */
2909             menubar_dispatch ((char *)s);
2910 #else
2911             (void)0;
2912 #endif
2913           else
2914             xterm_seq (arg, (char *)s, eh);
2915           free (s);
2916         }
2917     }
2918 }
2919 /*
2920  * XTerm escape sequences: ESC ] Ps;Pt (ST|BEL)
2921  *       0 = change iconName/title
2922  *       1 = change iconName
2923  *       2 = change title
2924  *       4 = change color
2925  *      12 = change text color
2926  *      13 = change mouse foreground color 
2927  *      17 = change highlight character colour
2928  *      18 = change bold character color
2929  *      19 = change underlined character color 
2930  *      46 = change logfile (not implemented)
2931  *      50 = change font
2932  *
2933  * rxvt extensions:
2934  *      10 = menu (may change in future)
2935  *      20 = bg pixmap
2936  *      39 = change default fg color
2937  *      49 = change default bg color
2938  *      55 = dump scrollback buffer and all of screen
2939  *     701 = change locale
2940  *     702 = find font
2941  */
2942 void
2943 rxvt_term::xterm_seq (int op, const char *str, unsigned char resp __attribute__ ((unused)))
2944 {
2945   int             changed = 0;
2946   int             color;
2947   char           *buf, *name;
2948
2949   assert (str != NULL);
2950   switch (op)
2951     {
2952       case XTerm_name:
2953         set_title (str);
2954         /* FALLTHROUGH */
2955       case XTerm_iconName:
2956         set_iconName (str);
2957         break;
2958       case XTerm_title:
2959         set_title (str);
2960         break;
2961       case XTerm_Color:
2962         for (buf = (char *)str; buf && *buf;)
2963           {
2964             if ((name = STRCHR (buf, ';')) == NULL)
2965               break;
2966             *name++ = '\0';
2967             color = atoi (buf);
2968             if (color < 0 || color >= TOTAL_COLORS)
2969               break;
2970             if ((buf = STRCHR (name, ';')) != NULL)
2971               *buf++ = '\0';
2972             set_window_color (color + minCOLOR, name);
2973           }
2974         break;
2975 #ifndef NO_CURSORCOLOR
2976       case XTerm_Color_cursor:
2977         set_window_color (Color_cursor, str);
2978         break;
2979 #endif
2980       case XTerm_Color_pointer:
2981         set_window_color (Color_pointer, str);
2982         break;
2983 #ifndef NO_BOLD_UNDERLINE_REVERSE
2984       case XTerm_Color_BD:
2985         set_window_color (Color_BD, str);
2986         break;
2987       case XTerm_Color_UL:
2988         set_window_color (Color_UL, str);
2989         break;
2990       case XTerm_Color_RV:
2991         set_window_color (Color_RV, str);
2992         break;
2993 #endif
2994
2995       case XTerm_Menu:
2996         /*
2997          * rxvt_menubar_dispatch () violates the constness of the string,
2998          * so DON'T do it here
2999          */
3000         break;
3001       case XTerm_Pixmap:
3002         if (*str != ';')
3003           {
3004 #if XPM_BACKGROUND
3005             scale_pixmap ("");  /* reset to default scaling */
3006             set_bgPixmap (str); /* change pixmap */
3007 #endif
3008             scr_touch (true);
3009           }
3010         while ((str = STRCHR (str, ';')) != NULL)
3011           {
3012             str++;
3013 #if XPM_BACKGROUND
3014             changed += scale_pixmap (str);
3015 #endif
3016
3017           }
3018         if (changed)
3019           {
3020 #ifdef XPM_BACKGROUND
3021             resize_pixmap ();
3022 #endif
3023             scr_touch (true);
3024           }
3025         break;
3026
3027       case XTerm_restoreFG:
3028         set_window_color (Color_fg, str);
3029         break;
3030       case XTerm_restoreBG:
3031         set_window_color (Color_bg, str);
3032         break;
3033       case XTerm_logfile:
3034         break;
3035       case XTerm_font:
3036         change_font (str);
3037         break;
3038 #ifndef NO_FRILLS
3039       case XTerm_locale:
3040         if (str[0] == '?' && !str[1])
3041           tt_printf ("%-.250s\n", locale);
3042         else
3043           {
3044             set_locale (str);
3045 # ifdef USE_XIM
3046             im_cb ();
3047 # endif
3048           }
3049         break;
3050       case XTerm_findfont:
3051         {
3052           int fid = TermWin.fontset->find_font (atoi (str));
3053           tt_printf ("%d %-.250s\n", fid, (*TermWin.fontset)[fid]->name);
3054         }
3055         break;
3056 #endif
3057 #if 0
3058       case XTerm_dumpscreen:    /* no error notices */
3059         {
3060           int             fd;
3061           if ((fd = open (str, O_RDWR | O_CREAT | O_EXCL, 0600)) >= 0)
3062             {
3063               scr_dump (fd);
3064               close (fd);
3065             }
3066         }
3067         break;
3068 #endif
3069
3070     }
3071 }
3072 /*----------------------------------------------------------------------*/
3073
3074 /*{{{ process DEC private mode sequences `ESC [ ? Ps mode' */
3075 /*
3076  * mode can only have the following values:
3077  *      'l' = low
3078  *      'h' = high
3079  *      's' = save
3080  *      'r' = restore
3081  *      't' = toggle
3082  * so no need for fancy checking
3083  */
3084 int
3085 rxvt_term::privcases (int mode, unsigned long bit)
3086 {
3087   int             state;
3088
3089   if (mode == 's')
3090     {
3091       SavedModes |= (PrivateModes & bit);
3092       return -1;
3093     }
3094   else
3095     {
3096       if (mode == 'r')
3097         state = (SavedModes & bit) ? 1 : 0;     /* no overlapping */
3098       else
3099         state = (mode == 't') ? ! (PrivateModes & bit) : mode;
3100       PrivMode (state, bit);
3101     }
3102   return state;
3103 }
3104
3105 /* we're not using priv _yet_ */
3106 void
3107 rxvt_term::process_terminal_mode (int mode, int priv __attribute__ ((unused)), unsigned int nargs, const int *arg)
3108 {
3109   unsigned int    i, j;
3110   int             state;
3111   static const struct
3112     {
3113       const int       argval;
3114       const unsigned long bit;
3115     }
3116   argtopriv[] = {
3117                   { 1, PrivMode_aplCUR },
3118                   { 2, PrivMode_vt52 },
3119                   { 3, PrivMode_132 },
3120                   { 4, PrivMode_smoothScroll },
3121                   { 5, PrivMode_rVideo },
3122                   { 6, PrivMode_relOrigin },
3123                   { 7, PrivMode_Autowrap },
3124                   { 9, PrivMode_MouseX10 },
3125 #ifdef menuBar_esc
3126                   { menuBar_esc, PrivMode_menuBar },
3127 #endif
3128 #ifdef scrollBar_esc
3129                   { scrollBar_esc, PrivMode_scrollBar },
3130 #endif
3131                   { 25, PrivMode_VisibleCursor },
3132                   { 35, PrivMode_ShiftKeys },
3133                   { 40, PrivMode_132OK },
3134                   { 47, PrivMode_Screen },
3135                   { 66, PrivMode_aplKP },
3136 #ifndef NO_BACKSPACE_KEY
3137                   { 67, PrivMode_BackSpace },
3138 #endif
3139                   { 1000, PrivMode_MouseX11 },
3140                   { 1010, PrivMode_TtyOutputInh },
3141                   { 1011, PrivMode_Keypress },
3142                   { 1047, PrivMode_Screen },
3143                   { 1049, PrivMode_Screen }, /* xterm extension, not fully implemented */
3144                 };
3145
3146   if (nargs == 0)
3147     return;
3148
3149   /* make lo/hi boolean */
3150   if (mode == 'l')
3151     mode = 0;           /* reset */
3152   else if (mode == 'h')
3153     mode = 1;           /* set */
3154
3155   for (i = 0; i < nargs; i++)
3156     {
3157       state = -1;
3158
3159       /* basic handling */
3160       for (j = 0; j < (sizeof (argtopriv)/sizeof (argtopriv[0])); j++)
3161         if (argtopriv[j].argval == arg[i])
3162           {
3163             state = privcases (mode, argtopriv[j].bit);
3164             break;
3165           }
3166
3167       /* extra handling for values with state unkept  */
3168       if (state == -1)
3169         switch (arg[i])
3170           {
3171             case 1048:          /* alternative cursor save */
3172               if (mode == 0)
3173                 scr_cursor (RESTORE);
3174               else if (mode == 1)
3175                 scr_cursor (SAVE);
3176               /* FALLTHROUGH */
3177             default:
3178               continue; /* for (;i;) */
3179           }
3180
3181       /* extra handling for values with valid 0 or 1 state */
3182       switch (arg[i])
3183         {
3184             /* case 1:  - application cursor keys */
3185           case 2:                       /* VT52 mode */
3186             /* oddball mode.  should be set regardless of set/reset
3187              * parameter.  Return from VT52 mode with an ESC < from
3188              * within VT52 mode
3189              */
3190             PrivMode (1, PrivMode_vt52);
3191             break;
3192           case 3:                       /* 80/132 */
3193             if (PrivateModes & PrivMode_132OK)
3194               set_widthheight (((state ? 132 : 80) * TermWin.fwidth), TermWin.height);
3195             break;
3196           case 4:                       /* smooth scrolling */
3197             if (state)
3198               Options &= ~Opt_jumpScroll;
3199             else
3200               Options |= Opt_jumpScroll;
3201             break;
3202           case 5:                       /* reverse video */
3203             scr_rvideo_mode (state);
3204             break;
3205           case 6:                       /* relative/absolute origins  */
3206             scr_relative_origin (state);
3207             break;
3208           case 7:                       /* autowrap */
3209             scr_autowrap (state);
3210             break;
3211             /* case 8:  - auto repeat, can't do on a per window basis */
3212           case 9:                       /* X10 mouse reporting */
3213             if (state)          /* orthogonal */
3214               PrivateModes &= ~ (PrivMode_MouseX11);
3215             break;
3216 #ifdef menuBar_esc
3217           case menuBar_esc:
3218 #ifdef MENUBAR
3219             map_menuBar (state);
3220 #endif
3221             break;
3222 #endif
3223 #ifdef scrollBar_esc
3224           case scrollBar_esc:
3225             if (scrollbar_mapping (state))
3226               {
3227                 resize_all_windows (0, 0, 0);
3228                 scr_touch (true);
3229               }
3230             break;
3231 #endif
3232           case 25:              /* visible/invisible cursor */
3233             scr_cursor_visible (state);
3234             break;
3235             /* case 35: - shift keys */
3236             /* case 40: - 80 <--> 132 mode */
3237           case 47:              /* secondary screen */
3238             scr_change_screen (state);
3239             break;
3240             /* case 66: - application key pad */
3241             /* case 67: - backspace key */
3242           case 1000:            /* X11 mouse reporting */
3243             if (state)          /* orthogonal */
3244               PrivateModes &= ~ (PrivMode_MouseX10);
3245             break;
3246 #if 0
3247           case 1001:
3248             break;              /* X11 mouse highlighting */
3249 #endif
3250           case 1010:            /* scroll to bottom on TTY output inhibit */
3251             if (state)
3252               Options &= ~Opt_scrollTtyOutput;
3253             else
3254               Options |= Opt_scrollTtyOutput;
3255             break;
3256           case 1011:            /* scroll to bottom on key press */
3257             if (state)
3258               Options |= Opt_scrollTtyKeypress;
3259             else
3260               Options &= ~Opt_scrollTtyKeypress;
3261             break;
3262           case 1047:            /* secondary screen w/ clearing */
3263           case 1049:            /* better secondary screen w/ clearing, but not fully implemented */
3264             if (current_screen != PRIMARY)
3265               scr_erase_screen (2);
3266             scr_change_screen (state);
3267             /* FALLTHROUGH */
3268           default:
3269             break;
3270         }
3271     }
3272 }
3273 /*}}} */
3274
3275 /*{{{ process sgr sequences */
3276 void
3277 rxvt_term::process_sgr_mode (unsigned int nargs, const int *arg)
3278 {
3279   unsigned int    i;
3280   short           rendset;
3281   int             rendstyle;
3282
3283   if (nargs == 0)
3284     {
3285       scr_rendition (0, ~RS_None);
3286       return;
3287     }
3288   for (i = 0; i < nargs; i++)
3289     {
3290       rendset = -1;
3291       switch (arg[i])
3292         {
3293           case 0:
3294             rendset = 0, rendstyle = ~RS_None;
3295             break;
3296           case 1:
3297             rendset = 1, rendstyle = RS_Bold;
3298             break;
3299           case 4:
3300             rendset = 1, rendstyle = RS_Uline;
3301             break;
3302           case 5:
3303             rendset = 1, rendstyle = RS_Blink;
3304             break;
3305           case 7:
3306             rendset = 1, rendstyle = RS_RVid;
3307             break;
3308           case 22:
3309             rendset = 0, rendstyle = RS_Bold;
3310             break;
3311           case 24:
3312             rendset = 0, rendstyle = RS_Uline;
3313             break;
3314           case 25:
3315             rendset = 0, rendstyle = RS_Blink;
3316             break;
3317           case 27:
3318             rendset = 0, rendstyle = RS_RVid;
3319             break;
3320         }
3321       if (rendset != -1)
3322         {
3323           scr_rendition (rendset, rendstyle);
3324           continue;             /* for (;i;) */
3325         }
3326
3327       switch (arg[i])
3328         {
3329           case 30:
3330           case 31:              /* set fg color */
3331           case 32:
3332           case 33:
3333           case 34:
3334           case 35:
3335           case 36:
3336           case 37:
3337             scr_color ((unsigned int) (minCOLOR + (arg[i] - 30)),
3338                        Color_fg);
3339             break;
3340 #ifdef TTY_256COLOR
3341           case 38:
3342             if (nargs > i + 2 && arg[i + 1] == 5)
3343               {
3344                 scr_color ((unsigned int) (minCOLOR + arg[i + 2]),
3345                            Color_fg);
3346                 i += 2;
3347               }
3348             break;
3349 #endif
3350           case 39:              /* default fg */
3351             scr_color (Color_fg, Color_fg);
3352             break;
3353
3354           case 40:
3355           case 41:              /* set bg color */
3356           case 42:
3357           case 43:
3358           case 44:
3359           case 45:
3360           case 46:
3361           case 47:
3362             scr_color ((unsigned int) (minCOLOR + (arg[i] - 40)),
3363                        Color_bg);
3364             break;
3365 #ifdef TTY_256COLOR
3366           case 48:
3367             if (nargs > i + 2 && arg[i + 1] == 5)
3368               {
3369                 scr_color ((unsigned int) (minCOLOR + arg[i + 2]),
3370                            Color_bg);
3371                 i += 2;
3372               }
3373             break;
3374 #endif
3375           case 49:              /* default bg */
3376             scr_color (Color_bg, Color_bg);
3377             break;
3378
3379 #ifndef NO_BRIGHTCOLOR
3380           case 90:
3381           case 91:              /* set bright fg color */
3382           case 92:
3383           case 93:
3384           case 94:
3385           case 95:
3386           case 96:
3387           case 97:
3388             scr_color ((unsigned int) (minBrightCOLOR + (arg[i] - 90)),
3389                        Color_fg);
3390             break;
3391           case 100:
3392           case 101:             /* set bright bg color */
3393           case 102:
3394           case 103:
3395           case 104:
3396           case 105:
3397           case 106:
3398           case 107:
3399             scr_color ((unsigned int) (minBrightCOLOR + (arg[i] - 100)),
3400                        Color_bg);
3401             break;
3402 #endif
3403
3404         }
3405     }
3406 }
3407 /*}}} */
3408
3409 /*{{{ process Rob Nation's own graphics mode sequences */
3410 void
3411 rxvt_term::process_graphics ()
3412 {
3413   unsigned char   ch, cmd = cmd_getc ();
3414
3415   if (cmd == 'Q')
3416     {           /* query graphics */
3417       tt_printf ("\033G0\n");   /* no graphics */
3418       return;
3419     }
3420   /* swallow other graphics sequences until terminating ':' */
3421   do
3422     ch = cmd_getc ();
3423   while (ch != ':');
3424 }
3425 /*}}} */
3426
3427 /* ------------------------------------------------------------------------- */
3428
3429 /*
3430  * Send printf () formatted output to the command.
3431  * Only use for small amounts of data.
3432  */
3433 void
3434 rxvt_term::tt_printf (const char *fmt,...)
3435 {
3436   va_list arg_ptr;
3437   unsigned char buf[256];
3438
3439   va_start (arg_ptr, fmt);
3440   vsnprintf ((char *)buf, 256, fmt, arg_ptr);
3441   va_end (arg_ptr);
3442   tt_write (buf, STRLEN (buf));
3443 }
3444
3445 /* ---------------------------------------------------------------------- */
3446 /* Write data to the pty as typed by the user, pasted with the mouse,
3447  * or generated by us in response to a query ESC sequence.
3448  */
3449 void
3450 rxvt_term::tt_write (const unsigned char *data, unsigned int len)
3451 {
3452   enum { MAX_PTY_WRITE = 255 }; // minimum MAX_INPUT
3453
3454   if (len)
3455     {
3456       if (v_buflen == 0)
3457         {
3458           ssize_t written = write (cmd_fd, data, min (MAX_PTY_WRITE, len));
3459
3460           if ((unsigned int)written == len)
3461             return;
3462
3463           data += written;
3464           len -= written;
3465         }
3466
3467
3468       v_buffer = (unsigned char *)realloc (v_buffer, v_buflen + len);
3469
3470       memcpy (v_buffer + v_buflen, data, len);
3471       v_buflen += len;
3472     }
3473
3474   for (;;)
3475     {
3476       int written = write (cmd_fd, v_buffer, min (MAX_PTY_WRITE, v_buflen));
3477
3478       if (written > 0)
3479         {
3480           v_buflen -= written;
3481
3482           if (v_buflen == 0)
3483             {
3484               free (v_buffer);
3485               v_buffer = 0;
3486               v_buflen = 0;
3487
3488               pty_ev.set (EVENT_READ);
3489               return;
3490             }
3491
3492           memmove (v_buffer, v_buffer + written, v_buflen);
3493         }
3494       else if (written != -1 || (errno != EAGAIN && errno != EINTR))
3495         // original code just ignores this...
3496         destroy ();
3497       else
3498         {
3499           pty_ev.set (EVENT_READ | EVENT_WRITE);
3500           return;
3501         }
3502     }
3503 }
3504
3505 /*----------------------- end-of-file (C source) -----------------------*/
3506