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