*** 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             // I don't understand this, so I changed it :)
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, Expose, &unused_event))
1187               ;
1188             while (XCheckTypedWindowEvent (display->display, ev.xany.window, GraphicsExpose, &unused_event))
1189               ;
1190
1191             if (isScrollbarWindow (ev.xany.window))
1192               {
1193                 scrollBar.setIdle ();
1194                 scrollbar_show (0);
1195               }
1196 #ifdef MENUBAR
1197             if (menubar_visible () && isMenuBarWindow (ev.xany.window))
1198               menubar_expose ();
1199 #endif
1200           }
1201         break;
1202
1203       case MotionNotify:
1204 #ifdef POINTER_BLANK
1205         if (hidden_pointer)
1206           pointer_unblank ();
1207 #endif
1208 #if MENUBAR
1209         if (isMenuBarWindow (ev.xany.window))
1210           {
1211             menubar_control (ev.xbutton);
1212             break;
1213           }
1214 #endif
1215         if ((PrivateModes & PrivMode_mouse_report) && ! (bypass_keystate))
1216           break;
1217
1218         if (ev.xany.window == TermWin.vt)
1219           {
1220             if ((ev.xbutton.state & (Button1Mask | Button3Mask)))
1221               {
1222                 while (XCheckTypedWindowEvent (display->display, TermWin.vt, MotionNotify, &ev))
1223                   ;
1224
1225                 XQueryPointer (display->display, TermWin.vt,
1226                               &unused_root, &unused_child,
1227                               &unused_root_x, &unused_root_y,
1228                               & (ev.xbutton.x), & (ev.xbutton.y),
1229                               &unused_mask);
1230 #ifdef MOUSE_THRESHOLD
1231                 /* deal with a `jumpy' mouse */
1232                 if ((ev.xmotion.time - MEvent.time) > MOUSE_THRESHOLD)
1233                   {
1234 #endif
1235                     selection_extend ((ev.xbutton.x), (ev.xbutton.y),
1236                                       (ev.xbutton.state & Button3Mask) ? 2 : 0);
1237 #ifdef SELECTION_SCROLLING
1238                     if (ev.xbutton.y < TermWin.int_bwidth
1239                         || Pixel2Row (ev.xbutton.y) > (TermWin.nrow-1))
1240                       {
1241                         int dist;
1242
1243                         pending_scroll_selection=1;
1244
1245                         /* don't clobber the current delay if we are
1246                          * already in the middle of scrolling.
1247                          */
1248                         if (scroll_selection_delay<=0)
1249                           scroll_selection_delay=SCROLLBAR_CONTINUOUS_DELAY;
1250
1251                         /* save the event params so we can highlight
1252                          * the selection in the pending-scroll loop
1253                          */
1254                         selection_save_x=ev.xbutton.x;
1255                         selection_save_y=ev.xbutton.y;
1256                         selection_save_state=
1257                           (ev.xbutton.state & Button3Mask) ? 2 : 0;
1258
1259                         /* calc number of lines to scroll */
1260                         if (ev.xbutton.y<TermWin.int_bwidth)
1261                           {
1262                             scroll_selection_dir = UP;
1263                             dist = TermWin.int_bwidth - ev.xbutton.y;
1264                           }
1265                         else
1266                           {
1267                             scroll_selection_dir = DN;
1268                             dist = ev.xbutton.y -
1269                                    (TermWin.int_bwidth + TermWin.height);
1270                           }
1271                         scroll_selection_lines= (Pixel2Height (dist)/
1272                                                 SELECTION_SCROLL_LINE_SPEEDUP)+1;
1273                         MIN_IT (scroll_selection_lines,
1274                                SELECTION_SCROLL_MAX_LINES);
1275                       }
1276                     else
1277                       {
1278                         /* we are within the text window, so we
1279                          * shouldn't be scrolling
1280                          */
1281                         pending_scroll_selection = 0;
1282                       }
1283 #endif
1284 #ifdef MOUSE_THRESHOLD
1285
1286                   }
1287 #endif
1288
1289               }
1290           }
1291         else if (isScrollbarWindow (ev.xany.window) && scrollbar_isMotion ())
1292           {
1293             while (XCheckTypedWindowEvent (display->display, scrollBar.win,
1294                                           MotionNotify, &ev)) ;
1295             XQueryPointer (display->display, scrollBar.win,
1296                           &unused_root, &unused_child,
1297                           &unused_root_x, &unused_root_y,
1298                           & (ev.xbutton.x), & (ev.xbutton.y),
1299                           &unused_mask);
1300             scr_move_to (scrollbar_position (ev.xbutton.y) - csrO,
1301                          scrollbar_size ());
1302             scr_refresh (refresh_type);
1303             refresh_limit = 0;
1304             scrollbar_show (1);
1305           }
1306         break;
1307     }
1308 }
1309
1310 void
1311 rxvt_term::rootwin_cb (XEvent &ev)
1312 {
1313   SET_R (this);
1314   SET_LOCALE (locale);
1315
1316   switch (ev.type)
1317     {
1318       case PropertyNotify:
1319         if (ev.xproperty.atom == xa[XA_VT_SELECTION])
1320           {
1321             if (ev.xproperty.state == PropertyNewValue)
1322               selection_property (ev.xproperty.window, ev.xproperty.atom);
1323             break;
1324           }
1325 #ifdef TRANSPARENT
1326         else
1327           {
1328             /*
1329              * if user used some Esetroot compatible prog to set the root bg,
1330              * use the property to determine the pixmap.  We use it later on.
1331              */
1332             if (xa[XA_XROOTPMAPID] == 0)
1333               xa[XA_XROOTPMAPID] = XInternAtom (display->display, "_XROOTPMAP_ID", False);
1334
1335             if (ev.xproperty.atom != xa[XA_XROOTPMAPID])
1336               return;
1337           }
1338
1339         /* FALLTHROUGH */
1340       case ReparentNotify:
1341         if ((Options & Opt_transparent) && check_our_parents () && am_transparent)
1342           want_full_refresh = 1;
1343         break;
1344 #endif
1345     }
1346 }
1347
1348 void
1349 rxvt_term::button_press (XButtonEvent &ev)
1350 {
1351   int reportmode = 0, clickintime;
1352
1353   bypass_keystate = ev.state & (ModMetaMask | ShiftMask);
1354   if (!bypass_keystate)
1355     reportmode = !! (PrivateModes & PrivMode_mouse_report);
1356   /*
1357    * VT window processing of button press
1358    */
1359   if (ev.window == TermWin.vt)
1360     {
1361       clickintime = ev.time - MEvent.time < MULTICLICK_TIME;
1362       if (reportmode)
1363         {
1364           /* mouse report from vt window */
1365           /* save the xbutton state (for ButtonRelease) */
1366           MEvent.state = ev.state;
1367 #ifdef MOUSE_REPORT_DOUBLECLICK
1368           if (ev.button == MEvent.button && clickintime)
1369             {
1370               /* same button, within alloted time */
1371               MEvent.clicks++;
1372               if (MEvent.clicks > 1)
1373                 {
1374                   /* only report double clicks */
1375                   MEvent.clicks = 2;
1376                   mouse_report (ev);
1377
1378                   /* don't report the release */
1379                   MEvent.clicks = 0;
1380                   MEvent.button = AnyButton;
1381                 }
1382             }
1383           else
1384             {
1385               /* different button, or time expired */
1386               MEvent.clicks = 1;
1387               MEvent.button = ev.button;
1388               mouse_report (ev);
1389             }
1390 #else
1391           MEvent.button = ev.button;
1392           mouse_report (ev);
1393 #endif                          /* MOUSE_REPORT_DOUBLECLICK */
1394
1395         }
1396       else
1397         {
1398           if (ev.button != MEvent.button)
1399             MEvent.clicks = 0;
1400           switch (ev.button)
1401             {
1402               case Button1:
1403                 /* allow shift+left click to extend selection */
1404                 if (ev.state & ShiftMask && ! (PrivateModes & PrivMode_mouse_report))
1405                   {
1406                     if (MEvent.button == Button1 && clickintime)
1407                       selection_rotate (ev.x, ev.y);
1408                     else
1409                       selection_extend (ev.x, ev.y, 1);
1410                   }
1411                 else
1412                   {
1413                     if (MEvent.button == Button1 && clickintime)
1414                       MEvent.clicks++;
1415                     else
1416                       MEvent.clicks = 1;
1417
1418                     selection_click (MEvent.clicks, ev.x, ev.y);
1419                   }
1420
1421                 MEvent.button = Button1;
1422                 break;
1423
1424               case Button3:
1425                 if (MEvent.button == Button3 && clickintime)
1426                   selection_rotate (ev.x, ev.y);
1427                 else
1428                   selection_extend (ev.x, ev.y, 1);
1429                 MEvent.button = Button3;
1430                 break;
1431             }
1432         }
1433       MEvent.time = ev.time;
1434       return;
1435     }
1436
1437   /*
1438    * Scrollbar window processing of button press
1439    */
1440   if (isScrollbarWindow (ev.window))
1441     {
1442       scrollBar.setIdle ();
1443       /*
1444        * Rxvt-style scrollbar:
1445        * move up if mouse is above slider
1446        * move dn if mouse is below slider
1447        *
1448        * XTerm-style scrollbar:
1449        * Move display proportional to pointer location
1450        * pointer near top -> scroll one line
1451        * pointer near bot -> scroll full page
1452        */
1453 #ifndef NO_SCROLLBAR_REPORT
1454       if (reportmode)
1455         {
1456           /*
1457            * Mouse report disabled scrollbar:
1458            * arrow buttons - send up/down
1459            * click on scrollbar - send pageup/down
1460            */
1461           if ((scrollBar.style == R_SB_NEXT
1462                && scrollbarnext_upButton (ev.y))
1463               || (scrollBar.style == R_SB_RXVT
1464                   && scrollbarrxvt_upButton (ev.y)))
1465             tt_printf ("\033[A");
1466           else if ((scrollBar.style == R_SB_NEXT
1467                     && scrollbarnext_dnButton (ev.y))
1468                    || (scrollBar.style == R_SB_RXVT
1469                        && scrollbarrxvt_dnButton (ev.y)))
1470             tt_printf ("\033[B");
1471           else
1472             switch (ev.button)
1473               {
1474                 case Button2:
1475                   tt_printf ("\014");
1476                   break;
1477                 case Button1:
1478                   tt_printf ("\033[6~");
1479                   break;
1480                 case Button3:
1481                   tt_printf ("\033[5~");
1482                   break;
1483               }
1484         }
1485       else
1486 #endif                          /* NO_SCROLLBAR_REPORT */
1487
1488         {
1489           char            upordown = 0;
1490
1491           if (scrollBar.style == R_SB_NEXT)
1492             {
1493               if (scrollbarnext_upButton (ev.y))
1494                 upordown = -1;  /* up */
1495               else if (scrollbarnext_dnButton (ev.y))
1496                 upordown = 1;   /* down */
1497             }
1498           else if (scrollBar.style == R_SB_RXVT)
1499             {
1500               if (scrollbarrxvt_upButton (ev.y))
1501                 upordown = -1;  /* up */
1502               else if (scrollbarrxvt_dnButton (ev.y))
1503                 upordown = 1;   /* down */
1504             }
1505           if (upordown)
1506             {
1507 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
1508               scroll_arrow_delay = SCROLLBAR_INITIAL_DELAY;
1509 #endif
1510               if (scr_page (upordown < 0 ? UP : DN, 1))
1511                 {
1512                   if (upordown < 0)
1513                     scrollBar.setUp ();
1514                   else
1515                     scrollBar.setDn ();
1516                 }
1517             }
1518           else
1519             switch (ev.button)
1520               {
1521                 case Button2:
1522                   switch (scrollbar_align)
1523                     {
1524                       case R_SB_ALIGN_TOP:
1525                         csrO = 0;
1526                         break;
1527                       case R_SB_ALIGN_CENTRE:
1528                         csrO = (scrollBar.bot - scrollBar.top) / 2;
1529                         break;
1530                       case R_SB_ALIGN_BOTTOM:
1531                         csrO = scrollBar.bot - scrollBar.top;
1532                         break;
1533                     }
1534                   if (scrollBar.style == R_SB_XTERM
1535                       || scrollbar_above_slider (ev.y)
1536                       || scrollbar_below_slider (ev.y))
1537                     scr_move_to (                                        scrollbar_position (ev.y) - csrO,
1538                                        scrollbar_size ());
1539                   scrollBar.setMotion ();
1540                   break;
1541
1542                 case Button1:
1543                   if (scrollbar_align == R_SB_ALIGN_CENTRE)
1544                     csrO = ev.y - scrollBar.top;
1545                   /* FALLTHROUGH */
1546
1547                 case Button3:
1548                   if (scrollBar.style != R_SB_XTERM)
1549                     {
1550                       if (scrollbar_above_slider (ev.y))
1551 # ifdef RXVT_SCROLL_FULL
1552                         scr_page (UP, TermWin.nrow - 1);
1553 # else
1554                         scr_page (UP, TermWin.nrow / 4);
1555 # endif
1556                       else if (scrollbar_below_slider (ev.y))
1557 # ifdef RXVT_SCROLL_FULL
1558                         scr_page (DN, TermWin.nrow - 1);
1559 # else
1560                         scr_page (DN, TermWin.nrow / 4);
1561 # endif
1562                       else
1563                         scrollBar.setMotion ();
1564                     }
1565                   else
1566                     {
1567                       scr_page ((ev.button == Button1 ? DN : UP),
1568                                 (TermWin.nrow
1569                                  * scrollbar_position (ev.y)
1570                                  / scrollbar_size ()));
1571                     }
1572                   break;
1573               }
1574         }
1575       return;
1576     }
1577 #if MENUBAR
1578   /*
1579    * Menubar window processing of button press
1580    */
1581   if (isMenuBarWindow (ev.window))
1582     menubar_control (ev);
1583 #endif
1584 }
1585
1586 void
1587 rxvt_term::button_release (XButtonEvent &ev)
1588 {
1589   int reportmode = 0;
1590
1591   csrO = 0;             /* reset csr Offset */
1592   if (!bypass_keystate)
1593     reportmode = !! (PrivateModes & PrivMode_mouse_report);
1594
1595   if (scrollbar_isUpDn ())
1596     {
1597       scrollBar.setIdle ();
1598       scrollbar_show (0);
1599 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
1600       refresh_type &= ~SMOOTH_REFRESH;
1601 #endif
1602
1603     }
1604 #ifdef SELECTION_SCROLLING
1605   pending_scroll_selection=0;
1606 #endif
1607   if (ev.window == TermWin.vt)
1608     {
1609       if (reportmode)
1610         {
1611           /* mouse report from vt window */
1612           /* don't report release of wheel "buttons" */
1613           if (ev.button >= 4)
1614             return;
1615 #ifdef MOUSE_REPORT_DOUBLECLICK
1616           /* only report the release of 'slow' single clicks */
1617           if (MEvent.button != AnyButton
1618               && (ev.button != MEvent.button
1619                   || (ev.time - MEvent.time
1620                       > MULTICLICK_TIME / 2)))
1621             {
1622               MEvent.clicks = 0;
1623               MEvent.button = AnyButton;
1624               mouse_report (ev);
1625             }
1626 #else                           /* MOUSE_REPORT_DOUBLECLICK */
1627           MEvent.button = AnyButton;
1628           mouse_report (ev);
1629 #endif                          /* MOUSE_REPORT_DOUBLECLICK */
1630           return;
1631         }
1632       /*
1633        * dumb hack to compensate for the failure of click-and-drag
1634        * when overriding mouse reporting
1635        */
1636       if (PrivateModes & PrivMode_mouse_report
1637           && bypass_keystate
1638           && ev.button == Button1 && MEvent.clicks <= 1)
1639         selection_extend (ev.x, ev.y, 0);
1640
1641       switch (ev.button)
1642         {
1643           case Button1:
1644           case Button3:
1645             selection_make (ev.time);
1646             break;
1647           case Button2:
1648             selection_request (ev.time, ev.x, ev.y);
1649             break;
1650 #ifdef MOUSE_WHEEL
1651           case Button4:
1652           case Button5:
1653             {
1654               int i;
1655               page_dirn v;
1656
1657               v = (ev.button == Button4) ? UP : DN;
1658               if (ev.state & ShiftMask)
1659                 i = 1;
1660               else if ((Options & Opt_mouseWheelScrollPage))
1661                 i = TermWin.nrow - 1;
1662               else
1663                 i = 5;
1664 # ifdef MOUSE_SLIP_WHEELING
1665               if (ev.state & ControlMask)
1666                 {
1667                   mouse_slip_wheel_speed += (v ? -1 : 1);
1668                   mouse_slip_wheel_delay = SCROLLBAR_CONTINUOUS_DELAY;
1669                 }
1670 # endif
1671 # ifdef JUMP_MOUSE_WHEEL
1672               scr_page (v, i);
1673               scr_refresh (SMOOTH_REFRESH);
1674               scrollbar_show (1);
1675 # else
1676               while (i--)
1677                 {
1678                   scr_page (v, 1);
1679                   scr_refresh (SMOOTH_REFRESH);
1680                   scrollbar_show (1);
1681                 }
1682 # endif
1683
1684             }
1685             break;
1686 #endif
1687
1688         }
1689     }
1690 #ifdef MENUBAR
1691   else if (isMenuBarWindow (ev.window))
1692     menubar_control (ev);
1693 #endif
1694 }
1695
1696 #ifdef TRANSPARENT
1697 #if TINTING
1698 /* shading taken from eterm-0.9.2 */
1699 /* RGB 15 */
1700
1701 static void
1702 shade_ximage_15(void *data, int bpl, int w, int h, int rm, int gm, int bm)
1703 {
1704     unsigned char  *ptr;
1705     int             x, y;
1706
1707     ptr = (unsigned char *)data + (w * sizeof(uint16_t));
1708     if ((rm <= 256) && (gm <= 256) && (bm <= 256)) {
1709     /* No saturation */
1710         for (y = h; --y >= 0;) {
1711             for (x = -w; x < 0; x++) {
1712                 int             r, g, b;
1713
1714                 b = ((uint16_t *) ptr)[x];
1715                 r = (b & 0x7c00) * rm;
1716                 g = (b & 0x3e0) * gm;
1717                 b = (b & 0x1f) * bm;
1718                 ((uint16_t *) ptr)[x] = ((r >> 8) & 0x7c00)
1719                     | ((g >> 8) & 0x3e0)
1720                     | ((b >> 8) & 0x1f);
1721             }
1722             ptr += bpl;
1723         }
1724     } else {
1725         for (y = h; --y >= 0;) {
1726             for (x = -w; x < 0; x++) {
1727                 int             r, g, b;
1728
1729                 b = ((uint16_t *) ptr)[x];
1730                 r = (b & 0x7c00) * rm;
1731                 g = (b & 0x3e0) * gm;
1732                 b = (b & 0x1f) * bm;
1733                 r |= (!(r >> 15) - 1);
1734                 g |= (!(g >> 10) - 1);
1735                 b |= (!(b >> 5) - 1);
1736                 ((uint16_t *) ptr)[x] = ((r >> 8) & 0x7c00)
1737                     | ((g >> 8) & 0x3e0)
1738                     | ((b >> 8) & 0x1f);
1739             }
1740             ptr += bpl;
1741         }
1742     }
1743 }
1744
1745 /* RGB 16 */
1746 static void
1747 shade_ximage_16(void *data, int bpl, int w, int h, int rm, int gm, int bm)
1748 {
1749     unsigned char  *ptr;
1750     int             x, y;
1751
1752     ptr = (unsigned char *)data + (w * sizeof(uint16_t));
1753     if ((rm <= 256) && (gm <= 256) && (bm <= 256)) {
1754     /* No saturation */
1755         for (y = h; --y >= 0;) {
1756             for (x = -w; x < 0; x++) {
1757                 int             r, g, b;
1758
1759                 b = ((uint16_t *) ptr)[x];
1760                 r = (b & 0xf800) * rm;
1761                 g = (b & 0x7e0) * gm;
1762                 b = (b & 0x1f) * bm;
1763                 ((uint16_t *) ptr)[x] = ((r >> 8) & 0xf800)
1764                     | ((g >> 8) & 0x7e0)
1765                     | ((b >> 8) & 0x1f);
1766             }
1767             ptr += bpl;
1768         }
1769     } else {
1770         for (y = h; --y >= 0;) {
1771             for (x = -w; x < 0; x++) {
1772                 int             r, g, b;
1773
1774                 b = ((uint16_t *) ptr)[x];
1775                 r = (b & 0xf800) * rm;
1776                 g = (b & 0x7e0) * gm;
1777                 b = (b & 0x1f) * bm;
1778                 r |= (!(r >> 16) - 1);
1779                 g |= (!(g >> 11) - 1);
1780                 b |= (!(b >> 5) - 1);
1781                 ((uint16_t *) ptr)[x] = ((r >> 8) & 0xf800)
1782                     | ((g >> 8) & 0x7e0)
1783                     | ((b >> 8) & 0x1f);
1784             }
1785             ptr += bpl;
1786         }
1787     }
1788 }
1789
1790 /* RGB 24 */
1791 static void
1792 shade_ximage_24(void *data, int bpl, int w, int h, int rm, int gm, int bm)
1793 {
1794     unsigned char  *ptr;
1795     int             x, y;
1796
1797     ptr = (unsigned char *)data + (w * 3);
1798     if ((rm <= 256) && (gm <= 256) && (bm <= 256)) {
1799     /* No saturation */
1800         for (y = h; --y >= 0;) {
1801             for (x = -(w * 3); x < 0; x += 3) {
1802                 int             r, g, b;
1803
1804                 if (byteorder.big_endian()) {
1805                     r = (ptr[x + 0] * rm) >> 8;
1806                     g = (ptr[x + 1] * gm) >> 8;
1807                     b = (ptr[x + 2] * bm) >> 8;
1808                     ptr[x + 0] = r;
1809                     ptr[x + 1] = g;
1810                     ptr[x + 2] = b;
1811                 } else {
1812                     r = (ptr[x + 2] * rm) >> 8;
1813                     g = (ptr[x + 1] * gm) >> 8;
1814                     b = (ptr[x + 0] * bm) >> 8;
1815                     ptr[x + 2] = r;
1816                     ptr[x + 1] = g;
1817                     ptr[x + 0] = b;
1818                 }
1819             }
1820             ptr += bpl;
1821         }
1822     } else {
1823         for (y = h; --y >= 0;) {
1824             for (x = -(w * 3); x < 0; x += 3) {
1825                 int             r, g, b;
1826
1827                 if (byteorder.big_endian()) {
1828                     r = (ptr[x + 0] * rm) >> 8;
1829                     g = (ptr[x + 1] * gm) >> 8;
1830                     b = (ptr[x + 2] * bm) >> 8;
1831
1832                     r |= (!(r >> 8) - 1);
1833                     g |= (!(g >> 8) - 1);
1834                     b |= (!(b >> 8) - 1);
1835
1836                     ptr[x + 0] = r;
1837                     ptr[x + 1] = g;
1838                     ptr[x + 2] = b;
1839                 } else {
1840                     r = (ptr[x + 2] * rm) >> 8;
1841                     g = (ptr[x + 1] * gm) >> 8;
1842                     b = (ptr[x + 0] * bm) >> 8;
1843
1844                     r |= (!(r >> 8) - 1);
1845                     g |= (!(g >> 8) - 1);
1846                     b |= (!(b >> 8) - 1);
1847
1848                     ptr[x + 2] = r;
1849                     ptr[x + 1] = g;
1850                     ptr[x + 0] = b;
1851                 }
1852             }
1853             ptr += bpl;
1854         }
1855     }
1856 }
1857
1858 /* RGB 32 */
1859 static void
1860 shade_ximage_32(void *data, int bpl, int w, int h, int rm, int gm, int bm)
1861 {
1862     unsigned char  *ptr;
1863     int             x, y;
1864
1865     ptr = (unsigned char *)data + (w * 4);
1866     if ((rm <= 256) && (gm <= 256) && (bm <= 256)) {
1867     /* No saturation */
1868         for (y = h; --y >= 0;) {
1869             if (byteorder.big_endian()) {
1870                 for (x = -(w * 4); x < 0; x += 4) {
1871                     int             r, g, b;
1872
1873                     r = (ptr[x + 1] * rm) >> 8;
1874                     g = (ptr[x + 2] * gm) >> 8;
1875                     b = (ptr[x + 3] * bm) >> 8;
1876                     ptr[x + 1] = r;
1877                     ptr[x + 2] = g;
1878                     ptr[x + 3] = b;
1879                 }
1880             } else {
1881                 for (x = -(w * 4); x < 0; x += 4) {
1882                     int             r, g, b;
1883
1884                     r = (ptr[x + 2] * rm) >> 8;
1885                     g = (ptr[x + 1] * gm) >> 8;
1886                     b = (ptr[x + 0] * bm) >> 8;
1887                     ptr[x + 2] = r;
1888                     ptr[x + 1] = g;
1889                     ptr[x + 0] = b;
1890                 }
1891             }
1892             ptr += bpl;
1893         }
1894     } else {
1895         for (y = h; --y >= 0;) {
1896             for (x = -(w * 4); x < 0; x += 4) {
1897                 int             r, g, b;
1898
1899                 if (byteorder.big_endian()) {
1900                     r = (ptr[x + 1] * rm) >> 8;
1901                     g = (ptr[x + 2] * gm) >> 8;
1902                     b = (ptr[x + 3] * bm) >> 8;
1903
1904                     r |= (!(r >> 8) - 1);
1905                     g |= (!(g >> 8) - 1);
1906                     b |= (!(b >> 8) - 1);
1907
1908                     ptr[x + 1] = r;
1909                     ptr[x + 2] = g;
1910                     ptr[x + 3] = b;
1911                 } else {
1912                     r = (ptr[x + 2] * rm) >> 8;
1913                     g = (ptr[x + 1] * gm) >> 8;
1914                     b = (ptr[x + 0] * bm) >> 8;
1915
1916                     r |= (!(r >> 8) - 1);
1917                     g |= (!(g >> 8) - 1);
1918                     b |= (!(b >> 8) - 1);
1919
1920                     ptr[x + 2] = r;
1921                     ptr[x + 1] = g;
1922                     ptr[x + 0] = b;
1923                 }
1924             }
1925             ptr += bpl;
1926         }
1927     }
1928 }
1929
1930 #endif
1931
1932 /*
1933  * Check our parents are still who we think they are.
1934  * Do transparency updates if required
1935  */
1936 int
1937 rxvt_term::check_our_parents ()
1938 {
1939   int i, pchanged, aformat, have_pixmap, rootdepth;
1940   unsigned long nitems, bytes_after;
1941   Atom atype;
1942   unsigned char *prop = NULL;
1943   Window root, oldp, *list;
1944   Pixmap rootpixmap = None;
1945   XWindowAttributes wattr, wrootattr;
1946
1947   pchanged = 0;
1948
1949   if (!(Options & Opt_transparent))
1950     return pchanged;    /* Don't try any more */
1951
1952   XGetWindowAttributes (display->display, display->root, &wrootattr);
1953   rootdepth = wrootattr.depth;
1954
1955   XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
1956
1957   if (rootdepth != wattr.depth)
1958     {
1959       if (am_transparent)
1960         {
1961           pchanged = 1;
1962           XSetWindowBackground (display->display, TermWin.vt, PixColors[Color_bg]);
1963           am_transparent = am_pixmap_trans = 0;
1964         }
1965
1966       return pchanged;  /* Don't try any more */
1967     }
1968
1969   /* Get all X ops out of the queue so that our information is up-to-date. */
1970   XSync (display->display, False);
1971
1972   /*
1973    * Make the frame window set by the window manager have
1974    * the root background. Some window managers put multiple nested frame
1975    * windows for each client, so we have to take care about that.
1976    */
1977   i = (xa[XA_XROOTPMAPID]
1978        && XGetWindowProperty (display->display, display->root, xa[XA_XROOTPMAPID],
1979                               0L, 1L, False, XA_PIXMAP, &atype, &aformat,
1980                               &nitems, &bytes_after, &prop) == Success);
1981
1982   if (!i || prop == NULL)
1983      i = (xa[XA_XSETROOTID]
1984           && XGetWindowProperty (display->display, display->root, xa[XA_XSETROOTID],
1985                                  0L, 1L, False, XA_PIXMAP, &atype, &aformat,
1986                                  &nitems, &bytes_after, &prop) == Success);
1987
1988   if (!i || prop == NULL)
1989     have_pixmap = 0;
1990   else
1991     {
1992       have_pixmap = 1;
1993       rootpixmap = *(Pixmap *)prop;
1994       XFree (prop);
1995     }
1996
1997   if (have_pixmap)
1998     {
1999       /*
2000        * Copy display->root pixmap transparency
2001        */
2002       int sx, sy, nx, ny;
2003       unsigned int nw, nh;
2004       Window cr;
2005       XImage *image;
2006       GC gc;
2007       XGCValues gcvalue;
2008
2009       XTranslateCoordinates (display->display, TermWin.parent[0], display->root,
2010                              0, 0, &sx, &sy, &cr);
2011       nw = (unsigned int)szHint.width;
2012       nh = (unsigned int)szHint.height;
2013       nx = ny = 0;
2014
2015       if (sx < 0)
2016         {
2017           nw += sx;
2018           nx = -sx;
2019           sx = 0;
2020         }
2021
2022       if (sy < 0)
2023         {
2024           nh += sy;
2025           ny = -sy;
2026           sy = 0;
2027         }
2028
2029       MIN_IT (nw, (unsigned int) (wrootattr.width - sx));
2030       MIN_IT (nh, (unsigned int) (wrootattr.height - sy));
2031       allowedxerror = -1;
2032       image = XGetImage (display->display, rootpixmap, sx, sy, nw, nh, AllPlanes, ZPixmap);
2033
2034       /* XXX: handle BadMatch - usually because we're outside the pixmap */
2035       /* XXX: may need a delay here? */
2036       allowedxerror = 0;
2037
2038       if (image == NULL)
2039         {
2040           if (am_transparent && am_pixmap_trans)
2041             {
2042               pchanged = 1;
2043               if (TermWin.pixmap != None)
2044                 {
2045                   XFreePixmap (display->display, TermWin.pixmap);
2046                   TermWin.pixmap = None;
2047                 }
2048             }
2049
2050           am_pixmap_trans = 0;
2051         }
2052       else
2053         {
2054           if (TermWin.pixmap != None)
2055             XFreePixmap (display->display, TermWin.pixmap);
2056
2057 #if TINTING
2058           if (ISSET_PIXCOLOR (Color_tint))
2059             {
2060               unsigned short shade, rm, gm, bm;
2061
2062               PixColors[Color_tint].get (display, rm, gm, bm);
2063
2064               rm >>= 8; gm >>= 8; bm >>= 8; // not 100% correct, but...
2065
2066               /* Determine bitshift and bitmask values */
2067               switch (image->bits_per_pixel)
2068                 {
2069                   case 15: shade_ximage_15 (image->data, image->bytes_per_line, image->width, image->height, rm, gm, bm); break;
2070                   case 16: shade_ximage_16 (image->data, image->bytes_per_line, image->width, image->height, rm, gm, bm); break;
2071                   case 24: shade_ximage_24 (image->data, image->bytes_per_line, image->width, image->height, rm, gm, bm); break;
2072                   case 32: shade_ximage_32 (image->data, image->bytes_per_line, image->width, image->height, rm, gm, bm); break;
2073                 }
2074             }
2075 #endif
2076
2077           TermWin.pixmap = XCreatePixmap (display->display, TermWin.vt,
2078                                           szHint.width, szHint.height, image->depth);
2079           gc = XCreateGC (display->display, TermWin.vt, 0UL, &gcvalue);
2080           XPutImage (display->display, TermWin.pixmap, gc, image, 0, 0,
2081                      nx, ny, image->width, image->height);
2082           XFreeGC (display->display, gc);
2083           XDestroyImage (image);
2084           XSetWindowBackgroundPixmap (display->display, TermWin.vt,
2085                                      TermWin.pixmap);
2086
2087           if (!am_transparent || !am_pixmap_trans)
2088             pchanged = 1;
2089
2090           am_transparent = am_pixmap_trans = 1;
2091         }
2092     }
2093
2094   if (!am_pixmap_trans)
2095     {
2096       unsigned int n;
2097       /*
2098        * InheritPixmap transparency
2099        */
2100       D_X ((stderr, "InheritPixmap Seeking to  %08lx", display->root));
2101       for (i = 1; i < (int) (sizeof (TermWin.parent) / sizeof (Window)); i++)
2102         {
2103           oldp = TermWin.parent[i];
2104           XQueryTree (display->display, TermWin.parent[i - 1], &root,
2105                       &TermWin.parent[i], &list, &n);
2106           XFree (list);
2107           D_X ((stderr, "InheritPixmap Parent[%d] = %08lx", i, TermWin.parent[i]));
2108
2109           if (TermWin.parent[i] == display->root)
2110             {
2111               if (oldp != None)
2112                 pchanged = 1;
2113
2114               break;
2115             }
2116
2117           if (oldp != TermWin.parent[i])
2118             pchanged = 1;
2119         }
2120
2121       n = 0;
2122
2123       if (pchanged)
2124         {
2125           for (; n < (unsigned int)i; n++)
2126             {
2127               XGetWindowAttributes (display->display, TermWin.parent[n], &wattr);
2128               D_X ((stderr, "InheritPixmap Checking Parent[%d]: %s", n, (wattr.depth == rootdepth && wattr.class != InputOnly) ? "OK" : "FAIL"));
2129               if (wattr.depth != rootdepth || wattr.c_class == InputOnly)
2130                 {
2131                   n = (int) (sizeof (TermWin.parent) / sizeof (Window)) + 1;
2132                   break;
2133                 }
2134             }
2135         }
2136
2137       if (n > (int) (sizeof (TermWin.parent) / sizeof (TermWin.parent[0])))
2138         {
2139           D_X ((stderr, "InheritPixmap Turning off"));
2140           XSetWindowBackground (display->display, TermWin.parent[0], PixColors[Color_fg]);
2141           XSetWindowBackground (display->display, TermWin.vt, PixColors[Color_bg]);
2142           am_transparent = 0;
2143           /* XXX: also turn off Opt_transparent? */
2144         }
2145       else
2146         {
2147 #if WAIT_FOR_WM
2148           /* wait (an arbitrary period) for the WM to do its thing
2149            * needed for fvwm2.2.2 (and before?) */
2150           sleep (1);
2151 #endif
2152           D_X ((stderr, "InheritPixmap Turning on (%d parents)", i - 1));
2153           for (n = 0; n < (unsigned int)i; n++)
2154             XSetWindowBackgroundPixmap (display->display, TermWin.parent[n], ParentRelative);
2155
2156           XSetWindowBackgroundPixmap (display->display, TermWin.vt, ParentRelative);
2157           am_transparent = 1;
2158         }
2159
2160       for (; i < (int) (sizeof (TermWin.parent) / sizeof (Window)); i++)
2161         TermWin.parent[i] = None;
2162     }
2163
2164   return pchanged;
2165 }
2166 #endif
2167
2168 /*}}} */
2169
2170 bool
2171 rxvt_term::cmd_parse ()
2172 {
2173   bool flag = false;
2174   unicode_t ch = NOCHAR;
2175   unsigned char *seq_begin; // remember start of esc-sequence here
2176
2177   for (;;)
2178     {
2179       if (ch == NOCHAR)
2180         {
2181           seq_begin = cmdbuf_ptr;
2182           ch = next_char ();
2183         }
2184
2185       if (ch == NOCHAR) // TODO: improve
2186         break;
2187
2188       if (!IS_CONTROL (ch) || ch == C0_LF || ch == C0_CR || ch == C0_HT)
2189         {
2190           /* Read a text string from the input buffer */
2191           unicode_t buf[UBUFSIZ];
2192           bool refreshnow = false;
2193           int nlines = 0;
2194           unicode_t *str = buf;
2195
2196           *str++ = ch;
2197
2198           for (;;)
2199             {
2200               seq_begin = cmdbuf_ptr;
2201               ch = next_char ();
2202
2203               if (ch == NOCHAR || (IS_CONTROL (ch) && ch != C0_LF && ch != C0_CR && ch != C0_HT))
2204                 break;
2205
2206               *str++ = ch;
2207
2208               if (ch == C0_LF)
2209                 {
2210                   nlines++;
2211                   refresh_count++;
2212
2213                   if (! (Options & Opt_jumpScroll)
2214                       || (refresh_count >= (refresh_limit * (TermWin.nrow - 1))))
2215                     {
2216                       refreshnow = true;
2217                       flag = true;
2218                       ch = NOCHAR;
2219                       break;
2220                     }
2221
2222                   // scr_add_lines only works for nlines < TermWin.nrow - 1.
2223                   if (nlines >= TermWin.nrow - 1)
2224                     {
2225                       scr_add_lines (buf, nlines, str - buf);
2226                       nlines = 0;
2227                       str = buf;
2228                     }
2229                 }
2230
2231               if (str >= buf + UBUFSIZ)
2232                 {
2233                   ch = NOCHAR;
2234                   break;
2235                 }
2236             }
2237
2238           scr_add_lines (buf, nlines, str - buf);
2239
2240           /*
2241            * If there have been a lot of new lines, then update the screen
2242            * What the heck I'll cheat and only refresh less than every page-full.
2243            * the number of pages between refreshes is refresh_limit, which
2244            * is incremented here because we must be doing flat-out scrolling.
2245            *
2246            * refreshing should be correct for small scrolls, because of the
2247            * time-out
2248            */
2249           if (refreshnow)
2250             {
2251               if ((Options & Opt_jumpScroll) && refresh_limit < REFRESH_PERIOD)
2252                 refresh_limit++;
2253               else
2254                 scr_refresh (refresh_type);
2255             }
2256
2257         }
2258       else
2259         {
2260           try
2261             {
2262               process_nonprinting (ch);
2263             }
2264           catch (const class out_of_input &o)
2265             {
2266               // we ran out of input, retry later
2267               cmdbuf_ptr = seq_begin;
2268               break;
2269             }
2270
2271           ch = NOCHAR;
2272         }
2273     }
2274
2275   return flag;
2276 }
2277
2278 // read the next character
2279 unicode_t
2280 rxvt_term::next_char ()
2281 {
2282   while (cmdbuf_ptr < cmdbuf_endp)
2283     {
2284       // assume 7-bit to be ascii ALWAYS
2285       if (*cmdbuf_ptr <= 0x7f && *cmdbuf_ptr != 0x1b)
2286         return *cmdbuf_ptr++;
2287
2288       wchar_t wc;
2289       size_t len = mbrtowc (&wc, (char *)cmdbuf_ptr, cmdbuf_endp - cmdbuf_ptr, mbstate);
2290
2291       if (len == (size_t)-2)
2292         {
2293           // the mbstate stores incomplete sequences. didn't know this :/
2294           cmdbuf_ptr = cmdbuf_endp;
2295           break;
2296         }
2297
2298       if (len == (size_t)-1)
2299         return *cmdbuf_ptr++; // the _occasional_ latin1 character is allowed to slip through
2300
2301       // assume wchar == unicode
2302       cmdbuf_ptr += len;
2303       return wc;
2304     }
2305
2306   return NOCHAR;
2307 }
2308
2309 /* rxvt_cmd_getc () - Return next input character */
2310 /*
2311  * Return the next input character after first passing any keyboard input
2312  * to the command.
2313  */
2314 unicode_t
2315 rxvt_term::cmd_getc ()
2316 {
2317   unicode_t c = next_char ();
2318
2319   if (c == NOCHAR)
2320     throw out_of_input;
2321
2322   return c;
2323 }
2324
2325 /*{{{ print pipe */
2326 /*----------------------------------------------------------------------*/
2327 #ifdef PRINTPIPE
2328 FILE *
2329 rxvt_term::popen_printer ()
2330 {
2331   FILE *stream = popen (rs[Rs_print_pipe], "w");
2332
2333   if (stream == NULL)
2334     rxvt_warn ("can't open printer pipe, not printing.\n");
2335
2336   return stream;
2337 }
2338
2339 int
2340 rxvt_term::pclose_printer (FILE *stream)
2341 {
2342   fflush (stream);
2343   return pclose (stream);
2344 }
2345
2346 /*
2347  * simulate attached vt100 printer
2348  */
2349 void
2350 rxvt_term::process_print_pipe ()
2351 {
2352   int done;
2353   FILE *fd;
2354
2355   if ((fd = popen_printer ()) == NULL)
2356     return;
2357
2358   /*
2359    * Send all input to the printer until either ESC[4i or ESC[?4i
2360    * is received.
2361    */
2362   for (done = 0; !done;)
2363     {
2364       unsigned char buf[8];
2365       unicode_t ch;
2366       unsigned int i, len;
2367
2368       if ((ch = cmd_getc ()) != C0_ESC)
2369         {
2370           if (putc (ch, fd) == EOF)
2371             break;              /* done = 1 */
2372         }
2373       else
2374         {
2375           len = 0;
2376           buf[len++] = ch;
2377
2378           if ((buf[len++] = cmd_getc ()) == '[')
2379             {
2380               if ((ch = cmd_getc ()) == '?')
2381                 {
2382                   buf[len++] = '?';
2383                   ch = cmd_getc ();
2384                 }
2385               if ((buf[len++] = ch) == '4')
2386                 {
2387                   if ((buf[len++] = cmd_getc ()) == 'i')
2388                     break;      /* done = 1 */
2389                 }
2390             }
2391           
2392           for (i = 0; i < len; i++)
2393             if (putc (buf[i], fd) == EOF)
2394               {
2395                 done = 1;
2396                 break;
2397               }
2398         }
2399     }
2400
2401   pclose_printer (fd);
2402 }
2403 #endif                          /* PRINTPIPE */
2404 /*}}} */
2405
2406 /* *INDENT-OFF* */
2407 enum {
2408   C1_40 = 0x40,
2409           C1_41 , C1_BPH, C1_NBH, C1_44 , C1_NEL, C1_SSA, C1_ESA,
2410   C1_HTS, C1_HTJ, C1_VTS, C1_PLD, C1_PLU, C1_RI , C1_SS2, C1_SS3,
2411   C1_DCS, C1_PU1, C1_PU2, C1_STS, C1_CCH, C1_MW , C1_SPA, C1_EPA,
2412   C1_SOS, C1_59 , C1_SCI, C1_CSI, CS_ST , C1_OSC, C1_PM , C1_APC,
2413 };
2414 /* *INDENT-ON* */
2415
2416 /*{{{ process non-printing single characters */
2417 void
2418 rxvt_term::process_nonprinting (unicode_t ch)
2419 {
2420   switch (ch)
2421     {
2422       case C0_ESC:
2423         process_escape_seq ();
2424         break;
2425       case C0_ENQ:      /* terminal Status */
2426         if (rs[Rs_answerbackstring])
2427           tt_write ((const unsigned char *)rs[Rs_answerbackstring],
2428                     (unsigned int)STRLEN (rs[Rs_answerbackstring]));
2429         else
2430           tt_write ((unsigned char *)VT100_ANS,
2431                     (unsigned int)STRLEN (VT100_ANS));
2432         break;
2433       case C0_BEL:      /* bell */
2434         scr_bell ();
2435         break;
2436       case C0_BS:               /* backspace */
2437         scr_backspace ();
2438         break;
2439       case C0_HT:               /* tab */
2440         scr_tab (1);
2441         break;
2442       case C0_CR:               /* carriage return */
2443         scr_gotorc (0, 0, R_RELATIVE);
2444         break;
2445       case C0_VT:               /* vertical tab, form feed */
2446       case C0_FF:
2447       case C0_LF:               /* line feed */
2448         scr_index (UP);
2449         break;
2450       case C0_SO:               /* shift out - acs */
2451         scr_charset_choose (1);
2452         break;
2453       case C0_SI:               /* shift in - acs */
2454         scr_charset_choose (0);
2455         break;
2456
2457       // 8-bit controls
2458       case 0x90:        /* DCS */
2459         process_dcs_seq ();
2460         break;
2461       case 0x9b:        /* CSI */
2462         process_csi_seq ();
2463         break;
2464       case 0x9d:        /* CSI */
2465         process_osc_seq ();
2466         break;
2467     }
2468 }
2469 /*}}} */
2470
2471
2472 /*{{{ process VT52 escape sequences */
2473 void
2474 rxvt_term::process_escape_vt52 (unicode_t ch)
2475 {
2476   int row, col;
2477
2478   switch (ch)
2479     {
2480       case 'A':         /* cursor up */
2481         scr_gotorc (-1, 0, R_RELATIVE | C_RELATIVE);
2482         break;
2483       case 'B':         /* cursor down */
2484         scr_gotorc (1, 0, R_RELATIVE | C_RELATIVE);
2485         break;
2486       case 'C':         /* cursor right */
2487         scr_gotorc (0, 1, R_RELATIVE | C_RELATIVE);
2488         break;
2489       case 'D':         /* cursor left */
2490         scr_gotorc (0, -1, R_RELATIVE | C_RELATIVE);
2491         break;
2492       case 'H':         /* cursor home */
2493         scr_gotorc (0, 0, 0);
2494         break;
2495       case 'I':         /* cursor up and scroll down if needed */
2496         scr_index (DN);
2497         break;
2498       case 'J':         /* erase to end of screen */
2499         scr_erase_screen (0);
2500         break;
2501       case 'K':         /* erase to end of line */
2502         scr_erase_line (0);
2503         break;
2504       case 'Y':                 /* move to specified row and col */
2505         /* full command is 'ESC Y row col' where row and col
2506          * are encoded by adding 32 and sending the ascii
2507          * character.  eg. SPACE = 0, '+' = 13, '0' = 18,
2508          * etc. */
2509         row = cmd_getc () - ' ';
2510         col = cmd_getc () - ' ';
2511         scr_gotorc (row, col, 0);
2512         break;
2513       case 'Z':         /* identify the terminal type */
2514         tt_printf ("\033/Z");   /* I am a VT100 emulating a VT52 */
2515         break;
2516       case '<':         /* turn off VT52 mode */
2517         PrivMode (0, PrivMode_vt52);
2518         break;
2519       case 'F':         /* use special graphics character set */
2520       case 'G':           /* use regular character set */
2521         /* unimplemented */
2522         break;
2523       case '=':         /* use alternate keypad mode */
2524       case '>':           /* use regular keypad mode */
2525         /* unimplemented */
2526         break;
2527     }
2528 }
2529 /*}}} */
2530
2531
2532 /*{{{ process escape sequences */
2533 void
2534 rxvt_term::process_escape_seq ()
2535 {
2536   unicode_t ch = cmd_getc ();
2537
2538   if (PrivateModes & PrivMode_vt52)
2539     {
2540       process_escape_vt52 (ch);
2541       return;
2542     }
2543
2544   switch (ch)
2545     {
2546         /* case 1:        do_tek_mode (); break; */
2547       case '#':
2548         if (cmd_getc () == '8')
2549           scr_E ();
2550         break;
2551       case '(':
2552         scr_charset_set (0, (unsigned int)cmd_getc ());
2553         break;
2554       case ')':
2555         scr_charset_set (1, (unsigned int)cmd_getc ());
2556         break;
2557       case '*':
2558         scr_charset_set (2, (unsigned int)cmd_getc ());
2559         break;
2560       case '+':
2561         scr_charset_set (3, (unsigned int)cmd_getc ());
2562         break;
2563 #ifndef NO_FRILLS
2564       case '6':
2565         scr_backindex ();
2566         break;
2567 #endif
2568       case '7':
2569         scr_cursor (SAVE);
2570         break;
2571       case '8':
2572         scr_cursor (RESTORE);
2573         break;
2574 #ifndef NO_FRILLS
2575       case '9':
2576         scr_forwardindex ();
2577         break;
2578 #endif
2579       case '=':
2580       case '>':
2581         PrivMode ((ch == '='), PrivMode_aplKP);
2582         break;
2583
2584       case C1_40:
2585         cmd_getc ();
2586         break;
2587       case C1_44:
2588         scr_index (UP);
2589         break;
2590
2591         /* 8.3.87: NEXT LINE */
2592       case C1_NEL:              /* ESC E */
2593         {
2594           unicode_t nlcr[] = { C0_LF, C0_CR };
2595           scr_add_lines (nlcr, 1, 2);
2596         }
2597         break;
2598
2599         /* kidnapped escape sequence: Should be 8.3.48 */
2600       case C1_ESA:              /* ESC G */
2601         process_graphics ();
2602         break;
2603
2604         /* 8.3.63: CHARACTER TABULATION SET */
2605       case C1_HTS:              /* ESC H */
2606         scr_set_tab (1);
2607         break;
2608
2609         /* 8.3.105: REVERSE LINE FEED */
2610       case C1_RI:                       /* ESC M */
2611         scr_index (DN);
2612         break;
2613
2614         /* 8.3.142: SINGLE-SHIFT TWO */
2615       /*case C1_SS2: scr_single_shift (2);   break; */
2616
2617         /* 8.3.143: SINGLE-SHIFT THREE */
2618       /*case C1_SS3: scr_single_shift (3);   break; */
2619
2620         /* 8.3.27: DEVICE CONTROL STRING */
2621       case C1_DCS:              /* ESC P */
2622         process_dcs_seq ();
2623         break;
2624
2625         /* 8.3.110: SINGLE CHARACTER INTRODUCER */
2626       case C1_SCI:              /* ESC Z */
2627         tt_write ((const unsigned char *)ESCZ_ANSWER,
2628                  (unsigned int) (sizeof (ESCZ_ANSWER) - 1));
2629         break;                  /* steal obsolete ESC [ c */
2630
2631         /* 8.3.16: CONTROL SEQUENCE INTRODUCER */
2632       case C1_CSI:              /* ESC [ */
2633         process_csi_seq ();
2634         break;
2635
2636         /* 8.3.90: OPERATING SYSTEM COMMAND */
2637       case C1_OSC:              /* ESC ] */
2638         process_osc_seq ();
2639         break;
2640
2641         /* 8.3.106: RESET TO INITIAL STATE */
2642       case 'c':
2643         mbstate.reset ();
2644         scr_poweron ();
2645         scrollbar_show (1);
2646         break;
2647
2648         /* 8.3.79: LOCKING-SHIFT TWO (see ISO2022) */
2649       case 'n':
2650         scr_charset_choose (2);
2651         break;
2652
2653         /* 8.3.81: LOCKING-SHIFT THREE (see ISO2022) */
2654       case 'o':
2655         scr_charset_choose (3);
2656         break;
2657     }
2658 }
2659 /*}}} */
2660
2661 /*{{{ process CONTROL SEQUENCE INTRODUCER (CSI) sequences `ESC[' */
2662 /* *INDENT-OFF* */
2663 enum {
2664   CSI_ICH = 0x40,
2665            CSI_CUU, CSI_CUD, CSI_CUF, CSI_CUB, CSI_CNL, CSI_CPL, CSI_CHA,
2666   CSI_CUP, CSI_CHT, CSI_ED , CSI_EL , CSI_IL , CSI_DL , CSI_EF , CSI_EA ,
2667   CSI_DCH, CSI_SEE, CSI_CPR, CSI_SU , CSI_SD , CSI_NP , CSI_PP , CSI_CTC,
2668   CSI_ECH, CSI_CVT, CSI_CBT, CSI_SRS, CSI_PTX, CSI_SDS, CSI_SIMD, CSI_5F,
2669   CSI_HPA, CSI_HPR, CSI_REP, CSI_DA , CSI_VPA, CSI_VPR, CSI_HVP, CSI_TBC,
2670   CSI_SM , CSI_MC , CSI_HPB, CSI_VPB, CSI_RM , CSI_SGR, CSI_DSR, CSI_DAQ,
2671   CSI_70 , CSI_71 , CSI_72 , CSI_73 , CSI_74 , CSI_75 , CSI_76 , CSI_77 ,
2672   CSI_78 , CSI_79 , CSI_7A , CSI_7B , CSI_7C , CSI_7D , CSI_7E , CSI_7F
2673 };
2674
2675 #define make_byte(b7,b6,b5,b4,b3,b2,b1,b0)                      \
2676     (((b7) << 7) | ((b6) << 6) | ((b5) << 5) | ((b4) << 4)      \
2677      | ((b3) << 3) | ((b2) << 2) | ((b1) << 1) | (b0))
2678 #define get_byte_array_bit(array, bit)                          \
2679     (!! ((array)[ (bit) / 8] & (128 >> ((bit) & 7))))
2680
2681 const unsigned char csi_defaults[] =
2682   {
2683     make_byte (1,1,1,1,1,1,1,1),        /* @, A, B, C, D, E, F, G, */
2684     make_byte (1,1,0,0,1,1,0,0),        /* H, I, J, K, L, M, N, O, */
2685     make_byte (1,0,1,1,1,1,1,0),        /* P, Q, R, S, T, U, V, W, */
2686     make_byte (1,1,1,0,0,0,1,0),        /* X, Y, Z, [, \, ], ^, _, */
2687     make_byte (1,1,1,0,1,1,1,0),        /* `, a, b, c, d, e, f, g, */
2688     make_byte (0,0,1,1,0,0,0,0),        /* h, i, j, k, l, m, n, o, */
2689     make_byte (0,0,0,0,0,0,0,0),        /* p, q, r, s, t, u, v, w, */
2690     make_byte (0,0,0,0,0,0,0,0),        /* x, y, z, {, |, }, ~,    */
2691   };
2692 /* *INDENT-ON* */
2693
2694 void
2695 rxvt_term::process_csi_seq ()
2696 {
2697   unicode_t ch, priv, i;
2698   unsigned int nargs, p;
2699   int n, ndef;
2700   int arg[ESC_ARGS];
2701
2702   for (nargs = ESC_ARGS; nargs > 0;)
2703     arg[--nargs] = 0;
2704
2705   priv = 0;
2706   ch = cmd_getc ();
2707   if (ch >= '<' && ch <= '?')
2708     {   /* '<' '=' '>' '?' */
2709       priv = ch;
2710       ch = cmd_getc ();
2711     }
2712
2713   /* read any numerical arguments */
2714   for (n = -1; ch < CSI_ICH; )
2715     {
2716       if (isdigit (ch))
2717         {
2718           if (n < 0)
2719             n = ch - '0';
2720           else
2721             n = n * 10 + ch - '0';
2722         }
2723       else if (ch == ';')
2724         {
2725           if (nargs < ESC_ARGS)
2726             arg[nargs++] = n;
2727           n = -1;
2728         }
2729       else if (IS_CONTROL (ch))
2730         process_nonprinting (ch);
2731
2732       ch = cmd_getc ();
2733     }
2734
2735   if (ch > CSI_7F)
2736     return;
2737
2738   if (nargs < ESC_ARGS)
2739     arg[nargs++] = n;
2740
2741   i = ch - CSI_ICH;
2742   ndef = get_byte_array_bit (csi_defaults, i);
2743   for (p = 0; p < nargs; p++)
2744     if (arg[p] == -1)
2745       arg[p] = ndef;
2746
2747 #ifdef DEBUG_CMD
2748   fprintf (stderr, "CSI ");
2749   for (p = 0; p < nargs; p++)
2750     fprintf (stderr, "%d%s", arg[p], p < nargs - 1 ? ";" : "");
2751   fprintf (stderr, "%c\n", ch);
2752 #endif
2753
2754   /*
2755    * private mode handling
2756    */
2757   if (priv)
2758     {
2759       switch (priv)
2760         {
2761           case '>':
2762             if (ch == CSI_DA)   /* secondary device attributes */
2763               tt_printf ("\033[>%d;%-.8s;0c", 'R', VSTRING);
2764             break;
2765           case '?':
2766             if (ch == 'h' || ch == 'l' || ch == 'r' || ch == 's' || ch == 't')
2767               process_terminal_mode (ch, priv, nargs, arg);
2768             break;
2769         }
2770       return;
2771     }
2772
2773   switch (ch)
2774     {
2775         /*
2776          * ISO/IEC 6429:1992 (E) CSI sequences (defaults in parentheses)
2777          */
2778 #ifdef PRINTPIPE
2779       case CSI_MC:              /* 8.3.83: (0) MEDIA COPY */
2780         switch (arg[0])
2781           {
2782             case 0:                     /* initiate transfer to primary aux device */
2783               scr_printscreen (0);
2784               break;
2785             case 5:                     /* start relay to primary aux device */
2786               process_print_pipe ();
2787               break;
2788           }
2789         break;
2790 #endif
2791
2792       case CSI_CUU:             /* 8.3.22: (1) CURSOR UP */
2793       case CSI_VPR:             /* 8.3.161: (1) LINE POSITION FORWARD */
2794         arg[0] = -arg[0];
2795         /* FALLTHROUGH */
2796       case CSI_CUD:             /* 8.3.19: (1) CURSOR DOWN */
2797       case CSI_VPB:             /* 8.3.160: (1) LINE POSITION BACKWARD */
2798         scr_gotorc (arg[0], 0, RELATIVE);
2799         break;
2800
2801       case CSI_CUB:             /* 8.3.18: (1) CURSOR LEFT */
2802       case CSI_HPB:             /* 8.3.59: (1) CHARACTER POSITION BACKWARD */
2803 #ifdef ISO6429
2804         arg[0] = -arg[0];
2805 #else                           /* emulate common DEC VTs */
2806         arg[0] = arg[0] ? -arg[0] : -1;
2807 #endif
2808         /* FALLTHROUGH */
2809       case CSI_CUF:             /* 8.3.20: (1) CURSOR RIGHT */
2810       case CSI_HPR:             /* 8.3.60: (1) CHARACTER POSITION FORWARD */
2811 #ifdef ISO6429
2812         scr_gotorc (0, arg[0], RELATIVE);
2813 #else                           /* emulate common DEC VTs */
2814         scr_gotorc (0, arg[0] ? arg[0] : 1, RELATIVE);
2815 #endif
2816         break;
2817
2818       case CSI_CPL:             /* 8.3.13: (1) CURSOR PRECEDING LINE */
2819         arg[0] = -arg[0];
2820         /* FALLTHROUGH */
2821       case CSI_CNL:             /* 8.3.12: (1) CURSOR NEXT LINE */
2822         scr_gotorc (arg[0], 0, R_RELATIVE);
2823         break;
2824
2825       case CSI_CHA:             /* 8.3.9: (1) CURSOR CHARACTER ABSOLUTE */
2826       case CSI_HPA:             /* 8.3.58: (1) CURSOR POSITION ABSOLUTE */
2827         scr_gotorc (0, arg[0] - 1, R_RELATIVE);
2828         break;
2829
2830       case CSI_VPA:             /* 8.3.159: (1) LINE POSITION ABSOLUTE */
2831         scr_gotorc (arg[0] - 1, 0, C_RELATIVE);
2832         break;
2833
2834       case CSI_CUP:             /* 8.3.21: (1,1) CURSOR POSITION */
2835       case CSI_HVP:             /* 8.3.64: (1,1) CHARACTER AND LINE POSITION */
2836         scr_gotorc (arg[0] - 1, nargs < 2 ? 0 : (arg[1] - 1), 0);
2837         break;
2838
2839       case CSI_CBT:             /* 8.3.7: (1) CURSOR BACKWARD TABULATION */
2840         arg[0] = -arg[0];
2841         /* FALLTHROUGH */
2842       case CSI_CHT:             /* 8.3.10: (1) CURSOR FORWARD TABULATION */
2843         scr_tab (arg[0]);
2844         break;
2845
2846       case CSI_ED:              /* 8.3.40: (0) ERASE IN PAGE */
2847         scr_erase_screen (arg[0]);
2848         break;
2849
2850       case CSI_EL:              /* 8.3.42: (0) ERASE IN LINE */
2851         scr_erase_line (arg[0]);
2852         break;
2853
2854       case CSI_ICH:             /* 8.3.65: (1) INSERT CHARACTER */
2855         scr_insdel_chars (arg[0], INSERT);
2856         break;
2857
2858       case CSI_IL:              /* 8.3.68: (1) INSERT LINE */
2859         scr_insdel_lines (arg[0], INSERT);
2860         break;
2861
2862       case CSI_DL:              /* 8.3.33: (1) DELETE LINE */
2863         scr_insdel_lines (arg[0], DELETE);
2864         break;
2865
2866       case CSI_ECH:             /* 8.3.39: (1) ERASE CHARACTER */
2867         scr_insdel_chars (arg[0], ERASE);
2868         break;
2869
2870       case CSI_DCH:             /* 8.3.26: (1) DELETE CHARACTER */
2871         scr_insdel_chars (arg[0], DELETE);
2872         break;
2873
2874       case CSI_SD:              /* 8.3.114: (1) SCROLL DOWN */
2875         arg[0] = -arg[0];
2876         /* FALLTHROUGH */
2877       case CSI_SU:              /* 8.3.148: (1) SCROLL UP */
2878         scr_scroll_text (screen.tscroll, screen.bscroll, arg[0], 0);
2879         break;
2880
2881       case CSI_DA:              /* 8.3.24: (0) DEVICE ATTRIBUTES */
2882         tt_write ((const unsigned char *)VT100_ANS,
2883                  (unsigned int) (sizeof (VT100_ANS) - 1));
2884         break;
2885
2886       case CSI_SGR:             /* 8.3.118: (0) SELECT GRAPHIC RENDITION */
2887         process_sgr_mode (nargs, arg);
2888         break;
2889
2890       case CSI_DSR:             /* 8.3.36: (0) DEVICE STATUS REPORT */
2891         switch (arg[0])
2892           {
2893             case 5:                     /* DSR requested */
2894               tt_printf ("\033[0n");
2895               break;
2896             case 6:                     /* CPR requested */
2897               scr_report_position ();
2898               break;
2899             case 7:                     /* unofficial extension */
2900               if (Options & Opt_insecure)
2901                 tt_printf ("%-.250s\n", rs[Rs_display_name]);
2902               break;
2903             case 8:                     /* unofficial extension */
2904               process_xterm_seq (XTerm_title, RESNAME "-" VERSION, CHAR_ST);
2905               break;
2906           }
2907         break;
2908
2909       case CSI_TBC:             /* 8.3.155: (0) TABULATION CLEAR */
2910         switch (arg[0])
2911           {
2912             case 0:                     /* char tab stop cleared at active position */
2913               scr_set_tab (0);
2914               break;
2915               /* case 1: */             /* line tab stop cleared in active line */
2916               /* case 2: */             /* char tab stops cleared in active line */
2917             case 3:                     /* all char tab stops are cleared */
2918               /* case 4: */             /* all line tab stops are cleared */
2919             case 5:                     /* all tab stops are cleared */
2920               scr_set_tab (-1);
2921               break;
2922           }
2923         break;
2924
2925       case CSI_CTC:             /* 8.3.17: (0) CURSOR TABULATION CONTROL */
2926         switch (arg[0])
2927           {
2928             case 0:                     /* char tab stop set at active position */
2929               scr_set_tab (1);
2930               break;            /* = ESC H */
2931               /* case 1: */             /* line tab stop set at active line */
2932             case 2:                     /* char tab stop cleared at active position */
2933               scr_set_tab (0);
2934               break;            /* = ESC [ 0 g */
2935               /* case 3: */             /* line tab stop cleared at active line */
2936               /* case 4: */             /* char tab stops cleared at active line */
2937             case 5:                     /* all char tab stops are cleared */
2938               scr_set_tab (-1);
2939               break;            /* = ESC [ 3 g */
2940               /* case 6: */             /* all line tab stops are cleared */
2941           }
2942         break;
2943
2944       case CSI_RM:              /* 8.3.107: RESET MODE */
2945         if (arg[0] == 4)
2946           scr_insert_mode (0);
2947         break;
2948
2949       case CSI_SM:              /* 8.3.126: SET MODE */
2950         if (arg[0] == 4)
2951           scr_insert_mode (1);
2952         break;
2953
2954         /*
2955          * PRIVATE USE beyond this point.  All CSI_7? sequences here
2956          */
2957       case CSI_72:              /* DECSTBM: set top and bottom margins */
2958         if (nargs == 1)
2959           scr_scroll_region (arg[0] - 1, MAX_ROWS - 1);
2960         else if (nargs == 0 || arg[0] >= arg[1])
2961           scr_scroll_region (0, MAX_ROWS - 1);
2962         else
2963           scr_scroll_region (arg[0] - 1, arg[1] - 1);
2964         break;
2965
2966       case CSI_73:
2967         scr_cursor (SAVE);
2968         break;
2969       case CSI_75:
2970         scr_cursor (RESTORE);
2971         break;
2972
2973 #ifndef NO_FRILLS
2974       case CSI_74:
2975         process_window_ops (arg, nargs);
2976         break;
2977 #endif
2978
2979       case CSI_78:              /* DECREQTPARM */
2980         if (arg[0] == 0 || arg[0] == 1)
2981           tt_printf ("\033[%d;1;1;128;128;1;0x", arg[0] + 2);
2982         break;
2983
2984       default:
2985         break;
2986     }
2987 }
2988 /*}}} */
2989
2990 #ifndef NO_FRILLS
2991 /* ARGSUSED */
2992 void
2993 rxvt_term::process_window_ops (const int *args, unsigned int nargs)
2994 {
2995   int x, y;
2996   XWindowAttributes wattr;
2997   Window wdummy;
2998
2999   if (nargs == 0)
3000     return;
3001
3002   switch (args[0])
3003     {
3004       /*
3005        * commands
3006        */
3007       case 1:                   /* deiconify window */
3008         XMapWindow (display->display, TermWin.parent[0]);
3009         break;
3010       case 2:                   /* iconify window */
3011         XIconifyWindow (display->display, TermWin.parent[0],
3012                        DefaultScreen (display->display));
3013         break;
3014       case 3:                   /* set position (pixels) */
3015         XMoveWindow (display->display, TermWin.parent[0], args[1], args[2]);
3016         break;
3017       case 4:                   /* set size (pixels) */
3018         set_widthheight ((unsigned int)args[2], (unsigned int)args[1]);
3019         break;
3020       case 5:                   /* raise window */
3021         XRaiseWindow (display->display, TermWin.parent[0]);
3022         break;
3023       case 6:                   /* lower window */
3024         XLowerWindow (display->display, TermWin.parent[0]);
3025         break;
3026       case 7:                   /* refresh window */
3027         scr_touch (true);
3028         break;
3029       case 8:                   /* set size (chars) */
3030         set_widthheight ((unsigned int) (args[2] * TermWin.fwidth),
3031                          (unsigned int) (args[1] * TermWin.fheight));
3032         break;
3033
3034       //case 9: NYI, TODO, restore maximized window or maximize window
3035       default:
3036         if (args[0] >= 24)      /* set height (chars) */
3037           set_widthheight ((unsigned int)TermWin.width,
3038                            (unsigned int) (args[1] * TermWin.fheight));
3039         break;
3040
3041
3042       /*
3043        * reports - some output format copied from XTerm
3044        */
3045       case 11:                  /* report window state */
3046         XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
3047         tt_printf ("\033[%dt", wattr.map_state == IsViewable ? 1 : 2);
3048         break;
3049       case 13:                  /* report window position */
3050         XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
3051         XTranslateCoordinates (display->display, TermWin.parent[0], wattr.root,
3052                                -wattr.border_width, -wattr.border_width,
3053                                &x, &y, &wdummy);
3054         tt_printf ("\033[3;%d;%dt", x, y);
3055         break;
3056       case 14:                  /* report window size (pixels) */
3057         XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
3058         tt_printf ("\033[4;%d;%dt", wattr.height, wattr.width);
3059         break;
3060       case 18:                  /* report text area size (chars) */
3061         tt_printf ("\033[8;%d;%dt", TermWin.nrow, TermWin.ncol);
3062         break;
3063       case 19:                  /* report window size (chars) */
3064         tt_printf ("\033[9;%d;%dt", TermWin.nrow, TermWin.ncol);
3065         break;
3066       case 20:                  /* report icon label */
3067         if (Options & Opt_insecure)
3068           {
3069             char *s;
3070             XGetIconName (display->display, TermWin.parent[0], &s);
3071             tt_printf ("\033]L%-.200s\234", s ? s : "");        /* 8bit ST */
3072           }
3073         break;
3074       case 21:                  /* report window title */
3075         if (Options & Opt_insecure)
3076           {
3077             char *s;
3078             XFetchName (display->display, TermWin.parent[0], &s);
3079             tt_printf ("\033]l%-.200s\234", s ? s : "");        /* 8bit ST */
3080           }
3081         break;
3082     }
3083 }
3084 #endif
3085
3086 /*----------------------------------------------------------------------*/
3087 /*
3088  * get input up until STRING TERMINATOR (or BEL)
3089  * ends_how is terminator used. returned input must be free()'d
3090  */
3091 unsigned char *
3092 rxvt_term::get_to_st (unicode_t &ends_how)
3093 {
3094   int seen_esc = 0;     /* seen escape? */
3095   unsigned int n = 0;
3096   unsigned char *s;
3097   unicode_t ch;
3098   unsigned char string[STRING_MAX];
3099
3100   while ((ch = cmd_getc ()))
3101     {
3102       if (ch == C0_BEL || ch == CHAR_ST)
3103         break;
3104
3105       if (seen_esc)
3106         if (ch == 0x5c) /* 7bit ST */
3107           break;
3108         else
3109           return NULL;
3110
3111       if (ch == C0_ESC)
3112         {
3113           seen_esc = 1;
3114           continue;
3115         }
3116       else if (ch == C0_HT)
3117         ch = ' ';       /* translate '\t' to space */
3118       else if (ch < 0x20 && (ch != C0_LF && ch != C0_CR))
3119         return NULL;    /* other control character - exit */
3120
3121       if (n >= sizeof (string) - 1)
3122         // stop at some sane length
3123         return NULL;
3124
3125       string[n++] = ch;
3126       seen_esc = 0;
3127     }
3128
3129   string[n++] = '\0';
3130
3131   if ((s = (unsigned char *)rxvt_malloc (n)) == NULL)
3132     return NULL;
3133
3134   ends_how = (ch == 0x5c ? C0_ESC : ch);
3135   STRNCPY (s, string, n);
3136   return s;
3137 }
3138
3139 /*----------------------------------------------------------------------*/
3140 /*
3141  * process DEVICE CONTROL STRING `ESC P ... (ST|BEL)' or `0x90 ... (ST|BEL)'
3142  */
3143 void
3144 rxvt_term::process_dcs_seq ()
3145 {
3146   unsigned char *s;
3147   unicode_t eh;
3148
3149   /*
3150    * Not handled yet
3151    */
3152   s = get_to_st (eh);
3153   if (s)
3154     free (s);
3155
3156   return;
3157 }
3158
3159 /*----------------------------------------------------------------------*/
3160 /*
3161  * process OPERATING SYSTEM COMMAND sequence `ESC ] Ps ; Pt (ST|BEL)'
3162  */
3163 void
3164 rxvt_term::process_osc_seq ()
3165 {
3166   unicode_t ch, eh;
3167   int arg;
3168
3169   ch = cmd_getc ();
3170   for (arg = 0; isdigit (ch); ch = cmd_getc ())
3171     arg = arg * 10 + (ch - '0');
3172
3173   if (ch == ';')
3174     {
3175       unsigned char *s = get_to_st (eh);
3176
3177       if (s)
3178         {
3179           process_xterm_seq (arg, (char *)s, eh);
3180           free (s);
3181         }
3182     }
3183 }
3184
3185 void
3186 rxvt_term::process_color_seq (int report, int color, const char *str, unsigned char resp)
3187 {
3188   if (str[0] == '?' && !str[1])
3189     {
3190       if (Options & Opt_insecure)
3191         {
3192           unsigned short r, g, b;
3193           PixColors[color].get (display, r, g, b);
3194           tt_printf ("\033]%d;rgb:%04x/%04x/%04x%c", report, r, g, b, resp);
3195         }
3196     }
3197   else
3198     set_window_color (color, str);
3199 }
3200
3201 /*
3202  * XTerm escape sequences: ESC ] Ps;Pt (ST|BEL)
3203  *       0 = change iconName/title
3204  *       1 = change iconName
3205  *       2 = change title
3206  *       4 = change color
3207  *      10 = change fg color
3208  *      11 = change bg color
3209  *      12 = change text color
3210  *      13 = change mouse foreground color 
3211  *      17 = change highlight character colour
3212  *      18 = change bold character color
3213  *      19 = change underlined character color 
3214  *      46 = change logfile (not implemented)
3215  *      50 = change font
3216  *
3217  * rxvt extensions:
3218  *      20 = bg pixmap
3219  *      39 = change default fg color
3220  *      49 = change default bg color
3221  *      55 = dump scrollback buffer and all of screen
3222  *     701 = change locale
3223  *     702 = find font
3224  *     703 = menu
3225  */
3226 void
3227 rxvt_term::process_xterm_seq (int op, const char *str, unsigned char resp)
3228 {
3229   int changed = 0;
3230   int color;
3231   char *buf, *name;
3232   bool query = str[0] == '?' && !str[1];
3233
3234   assert (str != NULL);
3235   switch (op)
3236     {
3237       case XTerm_name:
3238         set_title (str);
3239         /* FALLTHROUGH */
3240       case XTerm_iconName:
3241         set_icon_name (str);
3242         break;
3243       case XTerm_title:
3244         set_title (str);
3245         break;
3246       case XTerm_property:
3247         if (str[0] == '?')
3248           {
3249             Atom prop = XInternAtom (display->display, str + 1, True);
3250             Atom actual_type;
3251             int actual_format;
3252             unsigned long nitems;
3253             unsigned long bytes_after;
3254             unsigned char *value = 0;
3255             const char *str = "";
3256
3257             if (prop
3258                 && XGetWindowProperty (display->display, TermWin.parent[0],
3259                                        prop, 0, 1<<16, 0, AnyPropertyType,
3260                                        &actual_type, &actual_format,
3261                                        &nitems, &bytes_after, &value) == Success
3262                 && actual_type != None
3263                 && actual_format == 8)
3264               str = (const char *)(value);
3265
3266             tt_printf ("\033]%d;%s%c", XTerm_property, str, resp);
3267
3268             XFree (value);
3269           }
3270         else
3271           {
3272             char *eq = strchr (str, '='); // constness lost, but verified to be ok
3273
3274             if (eq)
3275               {
3276                 *eq = 0;
3277                 XChangeProperty (display->display, TermWin.parent[0],
3278                                  display->atom (str), XA_STRING, 8,
3279                                  PropModeReplace, (unsigned char *)eq + 1,
3280                                  strlen (eq + 1));
3281               }
3282             else
3283               XDeleteProperty (display->display, TermWin.parent[0],
3284                                display->atom (str));
3285           }
3286         break;
3287
3288       case XTerm_Color:
3289         for (buf = (char *)str; buf && *buf;)
3290           {
3291             if ((name = STRCHR (buf, ';')) == NULL)
3292               break;
3293
3294             *name++ = '\0';
3295             color = atoi (buf);
3296
3297             if (color < 0 || color >= TOTAL_COLORS)
3298               break;
3299
3300             if ((buf = STRCHR (name, ';')) != NULL)
3301               *buf++ = '\0';
3302
3303             if (name[0] == '?' && !name[1])
3304               {
3305                 if (Options & Opt_insecure)
3306                   {
3307                     unsigned short r, g, b;
3308                     PixColors[color + minCOLOR].get (display, r, g, b);
3309                     tt_printf ("\033]%d;%d;rgb:%04x/%04x/%04x%c", XTerm_Color, color, r, g, b, resp);
3310                   }
3311               }
3312             else
3313               set_window_color (color + minCOLOR, name);
3314           }
3315         break;
3316       case XTerm_Color00:
3317         process_color_seq (XTerm_Color00, Color_fg, str, resp);
3318         break;
3319       case XTerm_Color01:
3320         process_color_seq (XTerm_Color00, Color_bg, str, resp);
3321         break;
3322 #ifndef NO_CURSORCOLOR
3323       case XTerm_Color_cursor:
3324         process_color_seq (XTerm_Color_cursor, Color_cursor, str, resp);
3325         break;
3326 #endif
3327       case XTerm_Color_pointer_fg:
3328         process_color_seq (XTerm_Color_pointer_fg, Color_pointer_fg, str, resp);
3329         break;
3330       case XTerm_Color_pointer_bg:
3331         process_color_seq (XTerm_Color_pointer_bg, Color_pointer_bg, str, resp);
3332         break;
3333 #ifndef NO_BOLD_UNDERLINE_REVERSE
3334       case XTerm_Color_BD:
3335         process_color_seq (XTerm_Color_BD, Color_BD, str, resp);
3336         break;
3337       case XTerm_Color_UL:
3338         process_color_seq (XTerm_Color_UL, Color_UL, str, resp);
3339         break;
3340       case XTerm_Color_RV:
3341         process_color_seq (XTerm_Color_RV, Color_RV, str, resp);
3342         break;
3343 #endif
3344
3345       case XTerm_Pixmap:
3346         if (*str != ';')
3347           {
3348 #if XPM_BACKGROUND
3349             scale_pixmap ("");  /* reset to default scaling */
3350             set_bgPixmap (str); /* change pixmap */
3351 #endif
3352             scr_touch (true);
3353           }
3354         while ((str = STRCHR (str, ';')) != NULL)
3355           {
3356             str++;
3357 #if XPM_BACKGROUND
3358             changed += scale_pixmap (str);
3359 #endif
3360
3361           }
3362         if (changed)
3363           {
3364 #ifdef XPM_BACKGROUND
3365             resize_pixmap ();
3366 #endif
3367             scr_touch (true);
3368           }
3369         break;
3370
3371       case XTerm_restoreFG:
3372         set_window_color (Color_fg, str);
3373         break;
3374       case XTerm_restoreBG:
3375         set_window_color (Color_bg, str);
3376         break;
3377
3378       case XTerm_logfile:
3379         // TODO, when secure mode?
3380         break;
3381
3382       case XTerm_font:
3383         if (query)
3384           {
3385             if (Options & Opt_insecure)
3386               tt_printf ("\33]%d;%-.250s%c", XTerm_font,
3387                          TermWin.fontset->fontdesc
3388                            ? TermWin.fontset->fontdesc
3389                            : "",
3390                          resp);
3391           }
3392         else
3393           change_font (str);
3394         break;
3395
3396 #ifndef NO_FRILLS
3397       case XTerm_locale:
3398         if (query)
3399           {
3400             if (Options & Opt_insecure)
3401               tt_printf ("\33]%d;%-.250s%c", XTerm_locale, locale, resp);
3402           }
3403         else
3404           {
3405             set_locale (str);
3406 # ifdef USE_XIM
3407             im_cb ();
3408 # endif
3409           }
3410         break;
3411
3412       case XTerm_findfont:
3413         if (Options & Opt_insecure)
3414           {
3415             int fid = TermWin.fontset->find_font (atoi (str));
3416             tt_printf ("\33]%d;%d;%-.250s%c", XTerm_findfont,
3417                        fid, (*TermWin.fontset)[fid]->name, resp);
3418           }
3419         break;
3420 #endif
3421
3422 #ifdef MENUBAR
3423      case XTerm_Menu:
3424        if (Options & Opt_insecure)
3425          menubar_dispatch (const_cast<char *>(str)); // casting away constness is checked
3426        break;
3427 #endif
3428 #if 0
3429       case XTerm_dumpscreen:    /* no error notices */
3430         {
3431           int fd;
3432           if ((fd = open (str, O_RDWR | O_CREAT | O_EXCL, 0600)) >= 0)
3433             {
3434               scr_dump (fd);
3435               close (fd);
3436             }
3437         }
3438         break;
3439 #endif
3440     }
3441 }
3442 /*----------------------------------------------------------------------*/
3443
3444 /*{{{ process DEC private mode sequences `ESC [ ? Ps mode' */
3445 /*
3446  * mode can only have the following values:
3447  *      'l' = low
3448  *      'h' = high
3449  *      's' = save
3450  *      'r' = restore
3451  *      't' = toggle
3452  * so no need for fancy checking
3453  */
3454 int
3455 rxvt_term::privcases (int mode, unsigned long bit)
3456 {
3457   int state;
3458
3459   if (mode == 's')
3460     {
3461       SavedModes |= (PrivateModes & bit);
3462       return -1;
3463     }
3464   else
3465     {
3466       if (mode == 'r')
3467         state = (SavedModes & bit) ? 1 : 0;     /* no overlapping */
3468       else
3469         state = (mode == 't') ? ! (PrivateModes & bit) : mode;
3470       PrivMode (state, bit);
3471     }
3472
3473   return state;
3474 }
3475
3476 /* we're not using priv _yet_ */
3477 void
3478 rxvt_term::process_terminal_mode (int mode, int priv __attribute__ ((unused)), unsigned int nargs, const int *arg)
3479 {
3480   unsigned int i, j;
3481   int state;
3482
3483   static const struct
3484     {
3485       const int       argval;
3486       const unsigned long bit;
3487     }
3488
3489   argtopriv[] = {
3490                   { 1, PrivMode_aplCUR },
3491                   { 2, PrivMode_vt52 },
3492                   { 3, PrivMode_132 },
3493                   { 4, PrivMode_smoothScroll },
3494                   { 5, PrivMode_rVideo },
3495                   { 6, PrivMode_relOrigin },
3496                   { 7, PrivMode_Autowrap },
3497                   { 9, PrivMode_MouseX10 },
3498 #ifdef menuBar_esc
3499                   { menuBar_esc, PrivMode_menuBar },
3500 #endif
3501 #ifdef scrollBar_esc
3502                   { scrollBar_esc, PrivMode_scrollBar },
3503 #endif
3504                  // 18, 19 printing-related
3505                   { 25, PrivMode_VisibleCursor },
3506                  // 30 show scrollbar rxvt. extension
3507                   { 35, PrivMode_ShiftKeys }, // rxvt extension
3508                   { 40, PrivMode_132OK },
3509                  // 41 xterm more fixes NYI
3510                  // 45 margin bell NYI
3511                  // 46 start logging
3512                   { 47, PrivMode_Screen },
3513                   { 66, PrivMode_aplKP },
3514 #ifndef NO_BACKSPACE_KEY
3515                   { 67, PrivMode_BackSpace },
3516 #endif
3517                   { 1000, PrivMode_MouseX11 },
3518                  // 1001 Use Hilite Mouse Tracking. NYI, TODO
3519                  // 1002 Use Cell Motion Mouse Tracking. NYI, TODO
3520                  // 1003 Use All Motion Mouse Tracking. NYI, TODO
3521                   { 1010, PrivMode_TtyOutputInh }, // rxvt extension
3522                   { 1011, PrivMode_Keypress }, // rxvt extension
3523                  // 1035 enable modifiers for alt, numlock NYI
3524                  // 1036 send ESC for meta keys NYI
3525                  // 1037 send DEL for keypad delete NYI
3526                   { 1047, PrivMode_Screen },
3527                  // 1048 save and restore cursor
3528                   { 1049, PrivMode_Screen }, /* xterm extension, clear screen on ti rather than te */
3529                  // 1051, 1052, 1060, 1061 keyboard emulation NYI
3530                 };
3531
3532   if (nargs == 0)
3533     return;
3534
3535   /* make lo/hi boolean */
3536   if (mode == 'l')
3537     mode = 0;           /* reset */
3538   else if (mode == 'h')
3539     mode = 1;           /* set */
3540
3541   for (i = 0; i < nargs; i++)
3542     {
3543       state = -1;
3544
3545       /* basic handling */
3546       for (j = 0; j < (sizeof (argtopriv)/sizeof (argtopriv[0])); j++)
3547         if (argtopriv[j].argval == arg[i])
3548           {
3549             state = privcases (mode, argtopriv[j].bit);
3550             break;
3551           }
3552
3553       /* extra handling for values with state unkept  */
3554       switch (arg[i])
3555         {
3556           case 1048:            /* alternative cursor save */
3557             if (Options & Opt_secondaryScreen)
3558               if (mode == 0)
3559                 scr_cursor (RESTORE);
3560               else if (mode == 1)
3561                 scr_cursor (SAVE);
3562             break;
3563         }
3564
3565       if (state >= 0)
3566         /* extra handling for values with valid 0 or 1 state */
3567         switch (arg[i])
3568           {
3569               /* case 1:        - application cursor keys */
3570             case 2:                     /* VT52 mode */
3571               /* oddball mode.  should be set regardless of set/reset
3572                * parameter.  Return from VT52 mode with an ESC < from
3573                * within VT52 mode
3574                */
3575               PrivMode (1, PrivMode_vt52);
3576               break;
3577             case 3:                     /* 80/132 */
3578               if (PrivateModes & PrivMode_132OK)
3579                 set_widthheight (((state ? 132 : 80) * TermWin.fwidth), TermWin.height);
3580               break;
3581             case 4:                     /* smooth scrolling */
3582               if (state)
3583                 Options &= ~Opt_jumpScroll;
3584               else
3585                 Options |= Opt_jumpScroll;
3586               break;
3587             case 5:                     /* reverse video */
3588               scr_rvideo_mode (state);
3589               break;
3590             case 6:                     /* relative/absolute origins  */
3591               scr_relative_origin (state);
3592               break;
3593             case 7:                     /* autowrap */
3594               scr_autowrap (state);
3595               break;
3596             /* case 8:  - auto repeat, can't do on a per window basis */
3597             case 9:                     /* X10 mouse reporting */
3598               if (state)                /* orthogonal */
3599                 PrivateModes &= ~PrivMode_MouseX11;
3600               break;
3601 #ifdef menuBar_esc
3602             case menuBar_esc:
3603 #ifdef MENUBAR
3604               map_menuBar (state);
3605 #endif
3606               break;
3607 #endif
3608 #ifdef scrollBar_esc
3609             case scrollBar_esc:
3610               if (scrollbar_mapping (state))
3611                 {
3612                   resize_all_windows (0, 0, 0);
3613                   scr_touch (true);
3614                 }
3615               break;
3616 #endif
3617             case 25:            /* visible/invisible cursor */
3618               scr_cursor_visible (state);
3619               break;
3620             /* case 35: - shift keys */
3621             /* case 40: - 80 <--> 132 mode */
3622             case 47:            /* secondary screen */
3623               scr_change_screen (state);
3624               break;
3625             /* case 66: - application key pad */
3626             /* case 67: - backspace key */
3627             case 1000:          /* X11 mouse reporting */
3628               if (state)                /* orthogonal */
3629                 PrivateModes &= ~PrivMode_MouseX10;
3630               break;
3631 #if 0
3632             case 1001:
3633               break;            /* X11 mouse highlighting */
3634 #endif
3635             case 1010:          /* scroll to bottom on TTY output inhibit */
3636               if (state)
3637                 Options &= ~Opt_scrollTtyOutput;
3638               else
3639                 Options |= Opt_scrollTtyOutput;
3640               break;
3641             case 1011:          /* scroll to bottom on key press */
3642               if (state)
3643                 Options |= Opt_scrollTtyKeypress;
3644               else
3645                 Options &= ~Opt_scrollTtyKeypress;
3646               break;
3647             case 1047:          /* secondary screen w/ clearing last */
3648               if (Options & Opt_secondaryScreen)
3649                 if (current_screen != PRIMARY)
3650                   scr_erase_screen (2);
3651               scr_change_screen (state);
3652               break;
3653             case 1049:          /* secondary screen w/ clearing first */
3654               scr_change_screen (state);
3655               if (Options & Opt_secondaryScreen)
3656                 if (current_screen != PRIMARY)
3657                   scr_erase_screen (2);
3658               break;
3659             default:
3660               break;
3661           }
3662     }
3663 }
3664 /*}}} */
3665
3666 /*{{{ process sgr sequences */
3667 void
3668 rxvt_term::process_sgr_mode (unsigned int nargs, const int *arg)
3669 {
3670   unsigned int i;
3671   short rendset;
3672   int rendstyle;
3673
3674   if (nargs == 0)
3675     {
3676       scr_rendition (0, ~RS_None);
3677       return;
3678     }
3679
3680   for (i = 0; i < nargs; i++)
3681     {
3682       rendset = -1;
3683       switch (arg[i])
3684         {
3685           case 0:
3686             rendset = 0, rendstyle = ~RS_None;
3687             break;
3688           case 1:
3689             rendset = 1, rendstyle = RS_Bold;
3690             break;
3691           case 4:
3692             rendset = 1, rendstyle = RS_Uline;
3693             break;
3694           case 5:
3695             rendset = 1, rendstyle = RS_Blink;
3696             break;
3697           //case 6: // scoansi light background
3698           case 7:
3699             rendset = 1, rendstyle = RS_RVid;
3700             break;
3701           case 8:
3702             // invisible. NYI
3703             break;
3704           //case 10: // scoansi acs off
3705           //case 11: // scoansi acs on
3706           //case 12: // scoansi acs on, |0x80
3707           case 21: // disable bold, blink and invis (some terminals use this)
3708             rendset = 0, rendstyle = RS_Bold | RS_Blink;
3709             break;
3710           case 22:
3711             rendset = 0, rendstyle = RS_Bold;
3712             break;
3713           case 24:
3714             rendset = 0, rendstyle = RS_Uline;
3715             break;
3716           case 25:
3717             rendset = 0, rendstyle = RS_Blink;
3718             break;
3719           case 27:
3720             rendset = 0, rendstyle = RS_RVid;
3721             break;
3722           case 28:
3723             // visible. NYI
3724             break;
3725         }
3726
3727       if (rendset != -1)
3728         {
3729           scr_rendition (rendset, rendstyle);
3730           continue;             /* for (;i;) */
3731         }
3732
3733       switch (arg[i])
3734         {
3735           case 30:
3736           case 31:              /* set fg color */
3737           case 32:
3738           case 33:
3739           case 34:
3740           case 35:
3741           case 36:
3742           case 37:
3743             scr_color ((unsigned int) (minCOLOR + (arg[i] - 30)), Color_fg);
3744             break;
3745 #ifdef TTY_256COLOR
3746           case 38:
3747             if (nargs > i + 2 && arg[i + 1] == 5)
3748               {
3749                 scr_color ((unsigned int) (minCOLOR + arg[i + 2]), Color_fg);
3750                 i += 2;
3751               }
3752             break;
3753 #endif
3754           case 39:              /* default fg */
3755             scr_color (Color_fg, Color_fg);
3756             break;
3757
3758           case 40:
3759           case 41:              /* set bg color */
3760           case 42:
3761           case 43:
3762           case 44:
3763           case 45:
3764           case 46:
3765           case 47:
3766             scr_color ((unsigned int) (minCOLOR + (arg[i] - 40)), Color_bg);
3767             break;
3768 #ifdef TTY_256COLOR
3769           case 48:
3770             if (nargs > i + 2 && arg[i + 1] == 5)
3771               {
3772                 scr_color ((unsigned int) (minCOLOR + arg[i + 2]), Color_bg);
3773                 i += 2;
3774               }
3775             break;
3776 #endif
3777           case 49:              /* default bg */
3778             scr_color (Color_bg, Color_bg);
3779             break;
3780
3781 #ifndef NO_BRIGHTCOLOR
3782           case 90:
3783           case 91:              /* set bright fg color */
3784           case 92:
3785           case 93:
3786           case 94:
3787           case 95:
3788           case 96:
3789           case 97:
3790             scr_color ((unsigned int) (minBrightCOLOR + (arg[i] - 90)),
3791                        Color_fg);
3792             break;
3793           case 100:
3794           case 101:             /* set bright bg color */
3795           case 102:
3796           case 103:
3797           case 104:
3798           case 105:
3799           case 106:
3800           case 107:
3801             scr_color ((unsigned int) (minBrightCOLOR + (arg[i] - 100)),
3802                        Color_bg);
3803             break;
3804 #endif
3805
3806         }
3807     }
3808 }
3809 /*}}} */
3810
3811 /*{{{ (do not) process Rob Nation's own graphics mode sequences */
3812 void
3813 rxvt_term::process_graphics ()
3814 {
3815   unicode_t ch, cmd = cmd_getc ();
3816
3817   if (cmd == 'Q')
3818     {           /* query graphics */
3819       tt_printf ("\033G0\n");   /* no graphics */
3820       return;
3821     }
3822   /* swallow other graphics sequences until terminating ':' */
3823   do
3824     ch = cmd_getc ();
3825   while (ch != ':');
3826 }
3827 /*}}} */
3828
3829 /* ------------------------------------------------------------------------- */
3830
3831 /*
3832  * Send printf () formatted output to the command.
3833  * Only use for small amounts of data.
3834  */
3835 void
3836 rxvt_term::tt_printf (const char *fmt,...)
3837 {
3838   va_list arg_ptr;
3839   unsigned char buf[256];
3840
3841   va_start (arg_ptr, fmt);
3842   vsnprintf ((char *)buf, 256, fmt, arg_ptr);
3843   va_end (arg_ptr);
3844   tt_write (buf, STRLEN (buf));
3845 }
3846
3847 /* ---------------------------------------------------------------------- */
3848 /* Write data to the pty as typed by the user, pasted with the mouse,
3849  * or generated by us in response to a query ESC sequence.
3850  */
3851 void
3852 rxvt_term::tt_write (const unsigned char *data, unsigned int len)
3853 {
3854   enum { MAX_PTY_WRITE = 255 }; // minimum MAX_INPUT
3855
3856   if (len)
3857     {
3858       if (v_buflen == 0)
3859         {
3860           ssize_t written = write (cmd_fd, data, min (MAX_PTY_WRITE, len));
3861
3862           if ((unsigned int)written == len)
3863             return;
3864
3865           data += written;
3866           len -= written;
3867         }
3868
3869
3870       v_buffer = (unsigned char *)realloc (v_buffer, v_buflen + len);
3871
3872       memcpy (v_buffer + v_buflen, data, len);
3873       v_buflen += len;
3874     }
3875
3876   for (;;)
3877     {
3878       int written = write (cmd_fd, v_buffer, min (MAX_PTY_WRITE, v_buflen));
3879
3880       if (written > 0)
3881         {
3882           v_buflen -= written;
3883
3884           if (v_buflen == 0)
3885             {
3886               free (v_buffer);
3887               v_buffer = 0;
3888               v_buflen = 0;
3889
3890               pty_ev.set (EVENT_READ);
3891               return;
3892             }
3893
3894           memmove (v_buffer, v_buffer + written, v_buflen);
3895         }
3896       else if (written != -1 || (errno != EAGAIN && errno != EINTR))
3897         // original code just ignores this...
3898         destroy ();
3899       else
3900         {
3901           pty_ev.set (EVENT_READ | EVENT_WRITE);
3902           return;
3903         }
3904     }
3905 }
3906
3907 /*----------------------- end-of-file (C source) -----------------------*/
3908