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