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