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