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