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