*** empty log message ***
[dana/urxvt.git] / src / command.C
1 /*--------------------------------*-C-*---------------------------------*
2  * File:        command.C
3  *----------------------------------------------------------------------*
4  *
5  * All portions of code are copyright by their respective author/s.
6  * Copyright (c) 1992      John Bovey, University of Kent at Canterbury <jdb@ukc.ac.uk>
7  *                              - original version
8  * Copyright (c) 1994      Robert Nation <nation@rocket.sanders.lockheed.com>
9  *                              - extensive modifications
10  * Copyright (c) 1995      Garrett D'Amore <garrett@netcom.com>
11  *                              - vt100 printing
12  * Copyright (c) 1995      Steven Hirsch <hirsch@emba.uvm.edu>
13  *                              - X11 mouse report mode and support for
14  *                                DEC "private mode" save/restore functions.
15  * Copyright (c) 1995      Jakub Jelinek <jj@gnu.ai.mit.edu>
16  *                              - key-related changes to handle Shift+function
17  *                                keys properly.
18  * Copyright (c) 1997      MJ Olesen <olesen@me.queensu.ca>
19  *                              - extensive modifications
20  * Copyright (c) 1997      Raul Garcia Garcia <rgg@tid.es>
21  *                              - modification and cleanups for Solaris 2.x
22  *                                and Linux 1.2.x
23  * Copyright (c) 1997,1998 Oezguer Kesim <kesim@math.fu-berlin.de>
24  * Copyright (c) 1998-2001 Geoff Wing <gcw@pobox.com>
25  *                              - extensive modifications
26  * Copyright (c) 1998      Alfredo K. Kojima <kojima@windowmaker.org>
27  * Copyright (c) 2001      Marius Gedminas
28  *                              - Ctrl/Mod4+Tab works like Meta+Tab (options)
29  * Copyright (c) 2003      Rob McMullen <robm@flipturn.org>
30  * Copyright (c) 2003-2004 Marc Lehmann <pcg@goof.com>
31  *
32  * This program is free software; you can redistribute it and/or modify
33  * it under the terms of the GNU General Public License as published by
34  * the Free Software Foundation; either version 2 of the License, or
35  * (at your option) any later version.
36  *
37  * This program is distributed in the hope that it will be useful,
38  * but WITHOUT ANY WARRANTY; without even the implied warranty of
39  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
40  * GNU General Public License for more details.
41  *
42  * You should have received a copy of the GNU General Public License
43  * along with this program; if not, write to the Free Software
44  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
45  *----------------------------------------------------------------------*/
46
47 /*{{{ includes: */
48 #include "../config.h"          /* NECESSARY */
49 #include "rxvt.h"               /* NECESSARY */
50 #include "version.h"
51 #include "command.h"
52
53 #include <wchar.h>
54
55 /*----------------------------------------------------------------------*/
56
57 #define IS_CONTROL(ch) !((ch) & 0xffffff60UL)
58
59 // exception thrown when the command parser runs out of input data
60 class out_of_input { } out_of_input;
61
62 #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 #ifdef TRANSPARENT
916   if (want_full_refresh)
917     {
918       want_full_refresh = 0;
919       scr_clear ();
920       scr_touch (false);
921     }
922 #endif
923
924   if (want_refresh)
925     {
926       scr_refresh (refresh_type);
927       scrollbar_show (1);
928 #ifdef USE_XIM
929       IMSendSpot ();
930 #endif
931     }
932
933   display->flush ();
934
935   flush_ev.stop ();
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 #ifdef USE_XIM
1222   if (XFilterEvent (&ev, None))
1223     return;
1224 #endif
1225
1226   Window          unused_root, unused_child;
1227   int             unused_root_x, unused_root_y;
1228   unsigned int    unused_mask;
1229
1230   switch (ev.type)
1231     {
1232       case KeyPress:
1233 #if ISO_14755
1234         if (!(iso14755buf & ISO_14755_52))
1235 #endif
1236           lookup_key (ev.xkey);
1237
1238         break;
1239
1240       case KeyRelease:
1241         {
1242 #if (MOUSE_WHEEL && MOUSE_SLIP_WHEELING) || ISO_14755
1243           KeySym ks;
1244
1245           ks = XLookupKeysym (&ev.xkey, ev.xkey.state & ShiftMask ? 1 : 0); // sorry, only shift supported :/
1246 #endif
1247
1248 #if ENABLE_FRILLS || ISO_14755
1249           // ISO 14755 support
1250           if (iso14755buf)
1251             if (iso14755buf & ISO_14755_52)
1252               {
1253 # if ENABLE_OVERLAY
1254                 scr_overlay_off ();
1255 # endif
1256 # if ISO_14755
1257                 // iso14755 part 5.2 handling: release time
1258                 // first: controls
1259                 if ((ev.xkey.state & ControlMask)
1260                      && ((ks >= 0x40 && ks <= 0x5f)
1261                          || (ks >= 0x61 && ks <= 0x7f)))
1262                   {
1263                     iso14755buf = ISO_14755_51 | 0x2400 | (ks & 0x1f);
1264                     commit_iso14755 ();
1265                     return; // case-break;
1266                   }
1267
1268                 for (unsigned short *i = iso14755_symtab; i[0]; i+= 2)
1269                   if (i[0] == ks)
1270                     {
1271                       iso14755buf = ISO_14755_51 | i[1];
1272                       commit_iso14755 ();
1273                       return; // case-break;
1274                     }
1275
1276                 scr_bell ();
1277 # endif
1278                 iso14755buf = 0;
1279                 break;
1280               }
1281             else if ((ev.xkey.state & (ShiftMask | ControlMask)) != (ShiftMask | ControlMask))
1282               {
1283 # if ENABLE_OVERLAY
1284                 scr_overlay_off ();
1285 # endif
1286                 if (iso14755buf & ISO_14755_51)
1287                   commit_iso14755 ();
1288 #if ISO_14755
1289                 else if (iso14755buf & ISO_14755_STARTED)
1290                   {
1291                     iso14755buf = ISO_14755_52; // iso14755 part 5.2: remember empty begin/end pair
1292
1293                     scr_overlay_new (0, -1, sizeof ("KEYCAP PICTURE INSERT MODE") - 1, 1);
1294                     scr_overlay_set (0, 0, "KEYCAP PICTURE INSERT MODE");
1295                   }
1296 # endif
1297                 else
1298                   iso14755buf = 0;
1299               }
1300 #endif
1301
1302 #if defined(MOUSE_WHEEL) && defined(MOUSE_SLIP_WHEELING)
1303           if (!(ev.xkey.state & ControlMask))
1304             slip_wheel_ev.stop ();
1305           else if (ks == XK_Control_L || ks == XK_Control_R)
1306             mouse_slip_wheel_speed = 0;
1307 #endif
1308           break;
1309         }
1310
1311       case ButtonPress:
1312         button_press (ev.xbutton);
1313         break;
1314
1315       case ButtonRelease:
1316         button_release (ev.xbutton);
1317         break;
1318
1319       case ClientMessage:
1320         if (ev.xclient.format == 32
1321             && (Atom)ev.xclient.data.l[0] == xa[XA_WMDELETEWINDOW])
1322           destroy ();
1323 #ifdef OFFIX_DND
1324         /* OffiX Dnd (drag 'n' drop) protocol */
1325         else if (ev.xclient.message_type == xa[XA_DNDPROTOCOL]
1326                  && (ev.xclient.data.l[0] == DndFile
1327                      || ev.xclient.data.l[0] == DndDir
1328                      || ev.xclient.data.l[0] == DndLink))
1329           {
1330             /* Get Dnd data */
1331             Atom ActualType;
1332             int ActualFormat;
1333             unsigned char *data;
1334             unsigned long Size, RemainingBytes;
1335
1336             XGetWindowProperty (display->display, display->root,
1337                                xa[XA_DNDSELECTION],
1338                                0L, 1000000L,
1339                                False, AnyPropertyType,
1340                                &ActualType, &ActualFormat,
1341                                &Size, &RemainingBytes,
1342                                &data);
1343             XChangeProperty (display->display, display->root,
1344                             XA_CUT_BUFFER0, XA_STRING,
1345                             8, PropModeReplace,
1346                             data, strlen (data));
1347             XFree (data);
1348             selection_paste (display->root, XA_CUT_BUFFER0, True);
1349             XSetInputFocus (display->display, display->root, RevertToNone, CurrentTime);
1350           }
1351 #endif                          /* OFFIX_DND */
1352         break;
1353
1354       case MappingNotify:
1355         XRefreshKeyboardMapping (&ev.xmapping);
1356         break;
1357
1358         /*
1359          * XXX: this is not the _current_ arrangement
1360          * Here's my conclusion:
1361          * If the window is completely unobscured, use bitblt's
1362          * to scroll. Even then, they're only used when doing partial
1363          * screen scrolling. When partially obscured, we have to fill
1364          * in the GraphicsExpose parts, which means that after each refresh,
1365          * we need to wait for the graphics expose or Noexpose events,
1366          * which ought to make things real slow!
1367          */
1368       case VisibilityNotify:
1369         switch (ev.xvisibility.state)
1370           {
1371             case VisibilityUnobscured:
1372               refresh_type = FAST_REFRESH;
1373               break;
1374             case VisibilityPartiallyObscured:
1375               refresh_type = SLOW_REFRESH;
1376               break;
1377             default:
1378               refresh_type = NO_REFRESH;
1379               break;
1380           }
1381         break;
1382
1383       case FocusIn:
1384         if (!TermWin.focus)
1385           {
1386             TermWin.focus = 1;
1387             want_refresh = 1;
1388 #ifdef USE_XIM
1389             if (Input_Context != NULL)
1390               {
1391                 IMSetStatusPosition ();
1392                 XSetICFocus (Input_Context);
1393               }
1394 #endif
1395 #ifdef CURSOR_BLINK
1396             if (options & Opt_cursorBlink)
1397               cursor_blink_ev.start (NOW + BLINK_INTERVAL);
1398 #endif
1399 #ifdef OFF_FOCUS_FADING
1400             if (rs[Rs_fade])
1401               {
1402                 pix_colors = pix_colors_focused;
1403                 scr_recolour ();
1404               }
1405 #endif
1406
1407           }
1408         break;
1409
1410       case FocusOut:
1411         if (TermWin.focus)
1412           {
1413             TermWin.focus = 0;
1414             want_refresh = 1;
1415
1416 #if ENABLE_FRILLS || ISO_14755
1417             iso14755buf = 0;
1418 #endif
1419 #if ENABLE_OVERLAY
1420             scr_overlay_off ();
1421 #endif
1422 #ifdef USE_XIM
1423             if (Input_Context != NULL)
1424               XUnsetICFocus (Input_Context);
1425 #endif
1426 #ifdef CURSOR_BLINK
1427             if (options & Opt_cursorBlink)
1428               cursor_blink_ev.stop ();
1429             hidden_cursor = 0;
1430 #endif
1431 #ifdef OFF_FOCUS_FADING
1432             if (rs[Rs_fade])
1433               {
1434                 pix_colors = pix_colors_unfocused;
1435                 scr_recolour ();
1436               }
1437 #endif
1438           }
1439         break;
1440
1441       case ConfigureNotify:
1442         if (ev.xconfigure.window == TermWin.parent[0])
1443           {
1444             int height, width;
1445
1446             do
1447               { /* Wrap lots of configures into one */
1448                 width = ev.xconfigure.width;
1449                 height = ev.xconfigure.height;
1450                 D_SIZE ((stderr, "Size: ConfigureNotify: %4d x %4d", width, height));
1451               }
1452             while (XCheckTypedWindowEvent (display->display, ev.xconfigure.window, ConfigureNotify, &ev));
1453
1454             if (szHint.width != width || szHint.height != height)
1455               {
1456                 seen_resize = 1;
1457                 resize_all_windows (width, height, 1);
1458               }
1459
1460 #ifdef TRANSPARENT              /* XXX: maybe not needed - leave in for now */
1461             if (options & Opt_transparent)
1462               {
1463                 check_our_parents ();
1464                 if (am_transparent)
1465                   want_refresh = want_full_refresh = 1;
1466               }
1467 #endif
1468           }
1469         break;
1470
1471       case PropertyNotify:
1472         if (ev.xproperty.atom == xa[XA_VT_SELECTION]
1473             && ev.xproperty.state == PropertyNewValue)
1474           selection_property (ev.xproperty.window, ev.xproperty.atom);
1475
1476         break;
1477
1478       case SelectionClear:
1479         selection_clear ();
1480         break;
1481
1482       case SelectionNotify:
1483         if (selection_wait == Sel_normal)
1484           selection_paste (ev.xselection.requestor,
1485                            ev.xselection.property, True);
1486         break;
1487
1488       case SelectionRequest:
1489         selection_send (ev.xselectionrequest);
1490         break;
1491
1492       case UnmapNotify:
1493         TermWin.mapped = 0;
1494 #ifdef TEXT_BLINK
1495         text_blink_ev.stop ();
1496 #endif
1497         break;
1498
1499       case MapNotify:
1500         TermWin.mapped = 1;
1501 #ifdef TEXT_BLINK
1502         text_blink_ev.start (NOW + TEXT_BLINK_INTERVAL);
1503 #endif
1504         break;
1505
1506 #ifdef TRANSPARENT
1507       case ReparentNotify:
1508         rootwin_cb (ev);
1509         break;
1510 #endif                          /* TRANSPARENT */
1511
1512       case GraphicsExpose:
1513       case Expose:
1514         if (ev.xany.window == TermWin.vt)
1515           {
1516             do
1517               scr_expose (ev.xexpose.x, ev.xexpose.y,
1518                           ev.xexpose.width, ev.xexpose.height, False);
1519             while (XCheckTypedWindowEvent (display->display, TermWin.vt, ev.xany.type, &ev));
1520
1521             ev.xany.type = ev.xany.type == Expose ? GraphicsExpose : Expose;
1522
1523             while (XCheckTypedWindowEvent (display->display, TermWin.vt, ev.xany.type, &ev))
1524               scr_expose (ev.xexpose.x, ev.xexpose.y,
1525                           ev.xexpose.width, ev.xexpose.height, False);
1526
1527             scr_refresh (refresh_type);
1528           }
1529         else
1530           {
1531             XEvent unused_event;
1532
1533             while (XCheckTypedWindowEvent (display->display, ev.xany.window, Expose, &unused_event))
1534               ;
1535             while (XCheckTypedWindowEvent (display->display, ev.xany.window, GraphicsExpose, &unused_event))
1536               ;
1537
1538             if (isScrollbarWindow (ev.xany.window))
1539               {
1540                 scrollBar.setIdle ();
1541                 scrollbar_show (0);
1542               }
1543 #ifdef MENUBAR
1544             if (menubar_visible () && isMenuBarWindow (ev.xany.window))
1545               menubar_expose ();
1546 #endif
1547
1548 #ifdef TRANSPARENT
1549             if (am_transparent && ev.xany.window == TermWin.parent[0])
1550               XClearWindow (display->display, ev.xany.window);
1551 #endif
1552           }
1553         break;
1554
1555       case MotionNotify:
1556 #ifdef POINTER_BLANK
1557         if (hidden_pointer)
1558           pointer_unblank ();
1559 #endif
1560 #if MENUBAR
1561         if (isMenuBarWindow (ev.xany.window))
1562           {
1563             menubar_control (ev.xbutton);
1564             break;
1565           }
1566 #endif
1567         if ((priv_modes & PrivMode_mouse_report) && ! (bypass_keystate))
1568           break;
1569
1570         if (ev.xany.window == TermWin.vt)
1571           {
1572             if (ev.xbutton.state & (Button1Mask | Button3Mask))
1573               {
1574                 while (XCheckTypedWindowEvent (display->display, TermWin.vt, MotionNotify, &ev))
1575                   ;
1576
1577                 XQueryPointer (display->display, TermWin.vt,
1578                                &unused_root, &unused_child,
1579                                &unused_root_x, &unused_root_y,
1580                                & (ev.xbutton.x), & (ev.xbutton.y),
1581                                &unused_mask);
1582 #ifdef MOUSE_THRESHOLD
1583                 /* deal with a `jumpy' mouse */
1584                 if ((ev.xmotion.time - MEvent.time) > MOUSE_THRESHOLD)
1585                   {
1586 #endif
1587 #if ISO_14755
1588                     // 5.4
1589                     if (iso14755buf & (ISO_14755_STARTED | ISO_14755_54))
1590                       {
1591                         iso14755_54 (ev.xbutton.x, ev.xbutton.y);
1592                         break;
1593                       }
1594 #endif
1595                     selection_extend (ev.xbutton.x, ev.xbutton.y,
1596                                       (ev.xbutton.state & Button3Mask) ? 2 : 0);
1597 #ifdef SELECTION_SCROLLING
1598                     if (ev.xbutton.y < TermWin.int_bwidth
1599                         || Pixel2Row (ev.xbutton.y) > (TermWin.nrow-1))
1600                       {
1601                         int dist;
1602
1603                         /* don't clobber the current delay if we are
1604                          * already in the middle of scrolling.
1605                          */
1606                         if (!sel_scroll_ev.active)
1607                           sel_scroll_ev.start (NOW + SCROLLBAR_INITIAL_DELAY);
1608
1609                         /* save the event params so we can highlight
1610                          * the selection in the pending-scroll loop
1611                          */
1612                         selection_save_x = ev.xbutton.x;
1613                         selection_save_y = ev.xbutton.y;
1614                         selection_save_state = (ev.xbutton.state & Button3Mask) ? 2 : 0;
1615
1616                         /* calc number of lines to scroll */
1617                         if (ev.xbutton.y<TermWin.int_bwidth)
1618                           {
1619                             scroll_selection_dir = UP;
1620                             dist = TermWin.int_bwidth - ev.xbutton.y;
1621                           }
1622                         else
1623                           {
1624                             scroll_selection_dir = DN;
1625                             dist = ev.xbutton.y - (TermWin.int_bwidth + TermWin.height);
1626                           }
1627
1628                         scroll_selection_lines = Pixel2Height (dist)
1629                                                  / SELECTION_SCROLL_LINE_SPEEDUP
1630                                                  + 1;
1631                         MIN_IT (scroll_selection_lines,
1632                                 SELECTION_SCROLL_MAX_LINES);
1633                       }
1634                     else
1635                       {
1636                         /* we are within the text window, so we
1637                          * shouldn't be scrolling
1638                          */
1639                         if (sel_scroll_ev.active)
1640                           sel_scroll_ev.stop();
1641                       }
1642 #endif
1643 #ifdef MOUSE_THRESHOLD
1644                   }
1645 #endif
1646               }
1647           }
1648         else if (isScrollbarWindow (ev.xany.window) && scrollbar_isMotion ())
1649           {
1650             while (XCheckTypedWindowEvent (display->display, scrollBar.win,
1651                                            MotionNotify, &ev)) ;
1652             XQueryPointer (display->display, scrollBar.win,
1653                           &unused_root, &unused_child,
1654                           &unused_root_x, &unused_root_y,
1655                           & (ev.xbutton.x), & (ev.xbutton.y),
1656                           &unused_mask);
1657             scr_move_to (scrollbar_position (ev.xbutton.y) - csrO,
1658                          scrollbar_size ());
1659             scr_refresh (refresh_type);
1660             refresh_limit = 0;
1661             scrollbar_show (1);
1662           }
1663         break;
1664     }
1665 }
1666
1667 #if TRANSPARENT
1668 void
1669 rxvt_term::rootwin_cb (XEvent &ev)
1670 {
1671   SET_R (this);
1672   SET_LOCALE (locale);
1673
1674   switch (ev.type)
1675     {
1676       case PropertyNotify:
1677         /*
1678          * if user used some Esetroot compatible prog to set the root bg,
1679          * use the property to determine the pixmap.  We use it later on.
1680          */
1681         if (xa[XA_XROOTPMAPID] == 0)
1682           xa[XA_XROOTPMAPID] = XInternAtom (display->display, "_XROOTPMAP_ID", False);
1683
1684         if (ev.xproperty.atom != xa[XA_XROOTPMAPID])
1685           return;
1686
1687         /* FALLTHROUGH */
1688       case ReparentNotify:
1689         if ((options & Opt_transparent) && check_our_parents () && am_transparent)
1690           want_refresh = want_full_refresh = 1;
1691         break;
1692     }
1693 }
1694 #endif
1695
1696 void
1697 rxvt_term::button_press (XButtonEvent &ev)
1698 {
1699   int reportmode = 0, clickintime;
1700
1701   bypass_keystate = ev.state & (ModMetaMask | ShiftMask);
1702   if (!bypass_keystate)
1703     reportmode = !! (priv_modes & PrivMode_mouse_report);
1704
1705   /*
1706    * VT window processing of button press
1707    */
1708   if (ev.window == TermWin.vt)
1709     {
1710 #if ISO_14755
1711       // 5.4
1712       if (iso14755buf & (ISO_14755_STARTED | ISO_14755_54))
1713         {
1714           iso14755_54 (ev.x, ev.y);
1715           return;
1716         }
1717 #endif
1718
1719       clickintime = ev.time - MEvent.time < MULTICLICK_TIME;
1720       if (reportmode)
1721         {
1722           /* mouse report from vt window */
1723           /* save the xbutton state (for ButtonRelease) */
1724           MEvent.state = ev.state;
1725 #ifdef MOUSE_REPORT_DOUBLECLICK
1726           if (ev.button == MEvent.button && clickintime)
1727             {
1728               /* same button, within alloted time */
1729               MEvent.clicks++;
1730               if (MEvent.clicks > 1)
1731                 {
1732                   /* only report double clicks */
1733                   MEvent.clicks = 2;
1734                   mouse_report (ev);
1735
1736                   /* don't report the release */
1737                   MEvent.clicks = 0;
1738                   MEvent.button = AnyButton;
1739                 }
1740             }
1741           else
1742             {
1743               /* different button, or time expired */
1744               MEvent.clicks = 1;
1745               MEvent.button = ev.button;
1746               mouse_report (ev);
1747             }
1748 #else
1749           MEvent.button = ev.button;
1750           mouse_report (ev);
1751 #endif                          /* MOUSE_REPORT_DOUBLECLICK */
1752
1753         }
1754       else
1755         {
1756           if (ev.button != MEvent.button)
1757             MEvent.clicks = 0;
1758           switch (ev.button)
1759             {
1760               case Button1:
1761                 /* allow shift+left click to extend selection */
1762                 if (ev.state & ShiftMask && ! (priv_modes & PrivMode_mouse_report))
1763                   {
1764                     if (MEvent.button == Button1 && clickintime)
1765                       selection_rotate (ev.x, ev.y);
1766                     else
1767                       selection_extend (ev.x, ev.y, 1);
1768                   }
1769                 else
1770                   {
1771                     if (MEvent.button == Button1 && clickintime)
1772                       MEvent.clicks++;
1773                     else
1774                       MEvent.clicks = 1;
1775
1776                     selection_click (MEvent.clicks, ev.x, ev.y);
1777                   }
1778
1779                 MEvent.button = Button1;
1780                 break;
1781
1782               case Button3:
1783                 if (MEvent.button == Button3 && clickintime)
1784                   selection_rotate (ev.x, ev.y);
1785                 else
1786                   selection_extend (ev.x, ev.y, 1);
1787
1788                 MEvent.button = Button3;
1789                 break;
1790             }
1791         }
1792
1793       MEvent.time = ev.time;
1794       return;
1795     }
1796
1797   /*
1798    * Scrollbar window processing of button press
1799    */
1800   if (isScrollbarWindow (ev.window))
1801     {
1802       scrollBar.setIdle ();
1803       /*
1804        * Rxvt-style scrollbar:
1805        * move up if mouse is above slider
1806        * move dn if mouse is below slider
1807        *
1808        * XTerm-style scrollbar:
1809        * Move display proportional to pointer location
1810        * pointer near top -> scroll one line
1811        * pointer near bot -> scroll full page
1812        */
1813 #ifndef NO_SCROLLBAR_REPORT
1814       if (reportmode)
1815         {
1816           /*
1817            * Mouse report disabled scrollbar:
1818            * arrow buttons - send up/down
1819            * click on scrollbar - send pageup/down
1820            */
1821           if ((scrollBar.style == R_SB_NEXT
1822                && scrollbarnext_upButton (ev.y))
1823               || (scrollBar.style == R_SB_RXVT
1824                   && scrollbarrxvt_upButton (ev.y)))
1825             tt_printf ("\033[A");
1826           else if ((scrollBar.style == R_SB_NEXT
1827                     && scrollbarnext_dnButton (ev.y))
1828                    || (scrollBar.style == R_SB_RXVT
1829                        && scrollbarrxvt_dnButton (ev.y)))
1830             tt_printf ("\033[B");
1831           else
1832             switch (ev.button)
1833               {
1834                 case Button2:
1835                   tt_printf ("\014");
1836                   break;
1837                 case Button1:
1838                   tt_printf ("\033[6~");
1839                   break;
1840                 case Button3:
1841                   tt_printf ("\033[5~");
1842                   break;
1843               }
1844         }
1845       else
1846 #endif                          /* NO_SCROLLBAR_REPORT */
1847
1848         {
1849           char            upordown = 0;
1850
1851           if (scrollBar.style == R_SB_NEXT)
1852             {
1853               if (scrollbarnext_upButton (ev.y))
1854                 upordown = -1;  /* up */
1855               else if (scrollbarnext_dnButton (ev.y))
1856                 upordown = 1;   /* down */
1857             }
1858           else if (scrollBar.style == R_SB_RXVT)
1859             {
1860               if (scrollbarrxvt_upButton (ev.y))
1861                 upordown = -1;  /* up */
1862               else if (scrollbarrxvt_dnButton (ev.y))
1863                 upordown = 1;   /* down */
1864             }
1865           if (upordown)
1866             {
1867 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
1868               cont_scroll_ev.start (NOW + SCROLLBAR_INITIAL_DELAY);
1869 #endif
1870               if (scr_page (upordown < 0 ? UP : DN, 1))
1871                 {
1872                   if (upordown < 0)
1873                     scrollBar.setUp ();
1874                   else
1875                     scrollBar.setDn ();
1876                 }
1877             }
1878           else
1879             switch (ev.button)
1880               {
1881                 case Button2:
1882                   switch (scrollbar_align)
1883                     {
1884                       case R_SB_ALIGN_TOP:
1885                         csrO = 0;
1886                         break;
1887                       case R_SB_ALIGN_CENTRE:
1888                         csrO = (scrollBar.bot - scrollBar.top) / 2;
1889                         break;
1890                       case R_SB_ALIGN_BOTTOM:
1891                         csrO = scrollBar.bot - scrollBar.top;
1892                         break;
1893                     }
1894
1895                   if (scrollBar.style == R_SB_XTERM
1896                       || scrollbar_above_slider (ev.y)
1897                       || scrollbar_below_slider (ev.y))
1898                     scr_move_to (scrollbar_position (ev.y) - csrO, scrollbar_size ());
1899
1900                   scrollBar.setMotion ();
1901                   break;
1902
1903                 case Button1:
1904                   if (scrollbar_align == R_SB_ALIGN_CENTRE)
1905                     csrO = ev.y - scrollBar.top;
1906                   /* FALLTHROUGH */
1907
1908                 case Button3:
1909                   if (scrollBar.style != R_SB_XTERM)
1910                     {
1911                       if (scrollbar_above_slider (ev.y))
1912 # ifdef RXVT_SCROLL_FULL
1913                         scr_page (UP, TermWin.nrow - 1);
1914 # else
1915                         scr_page (UP, TermWin.nrow / 4);
1916 # endif
1917                       else if (scrollbar_below_slider (ev.y))
1918 # ifdef RXVT_SCROLL_FULL
1919                         scr_page (DN, TermWin.nrow - 1);
1920 # else
1921                         scr_page (DN, TermWin.nrow / 4);
1922 # endif
1923                       else
1924                         scrollBar.setMotion ();
1925                     }
1926                   else
1927                     {
1928                       scr_page ((ev.button == Button1 ? DN : UP),
1929                                 (TermWin.nrow
1930                                  * scrollbar_position (ev.y)
1931                                  / scrollbar_size ()));
1932                     }
1933
1934                   break;
1935               }
1936         }
1937       return;
1938     }
1939 #if MENUBAR
1940   /*
1941    * Menubar window processing of button press
1942    */
1943   if (isMenuBarWindow (ev.window))
1944     menubar_control (ev);
1945 #endif
1946 }
1947
1948 void
1949 rxvt_term::button_release (XButtonEvent &ev)
1950 {
1951   int reportmode = 0;
1952
1953   csrO = 0;             /* reset csr Offset */
1954   if (!bypass_keystate)
1955     reportmode = !! (priv_modes & PrivMode_mouse_report);
1956
1957   if (scrollbar_isUpDn ())
1958     {
1959       scrollBar.setIdle ();
1960       scrollbar_show (0);
1961 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
1962       refresh_type &= ~SMOOTH_REFRESH;
1963 #endif
1964     }
1965
1966 #ifdef SELECTION_SCROLLING
1967   if (sel_scroll_ev.active)
1968     sel_scroll_ev.stop();
1969 #endif
1970
1971   if (ev.window == TermWin.vt)
1972     {
1973 #if ISO_14755
1974       // 5.4
1975       if (iso14755buf & (ISO_14755_STARTED | ISO_14755_54))
1976         return;
1977 #endif
1978       if (reportmode)
1979         {
1980           /* mouse report from vt window */
1981           /* don't report release of wheel "buttons" */
1982           if (ev.button >= 4)
1983             return;
1984 #ifdef MOUSE_REPORT_DOUBLECLICK
1985           /* only report the release of 'slow' single clicks */
1986           if (MEvent.button != AnyButton
1987               && (ev.button != MEvent.button
1988                   || (ev.time - MEvent.time
1989                       > MULTICLICK_TIME / 2)))
1990             {
1991               MEvent.clicks = 0;
1992               MEvent.button = AnyButton;
1993               mouse_report (ev);
1994             }
1995 #else                           /* MOUSE_REPORT_DOUBLECLICK */
1996           MEvent.button = AnyButton;
1997           mouse_report (ev);
1998 #endif                          /* MOUSE_REPORT_DOUBLECLICK */
1999           return;
2000         }
2001
2002       /*
2003        * dumb hack to compensate for the failure of click-and-drag
2004        * when overriding mouse reporting
2005        */
2006       if (priv_modes & PrivMode_mouse_report
2007           && bypass_keystate
2008           && ev.button == Button1 && MEvent.clicks <= 1)
2009         selection_extend (ev.x, ev.y, 0);
2010
2011       switch (ev.button)
2012         {
2013           case Button1:
2014           case Button3:
2015             selection_make (ev.time);
2016             break;
2017           case Button2:
2018             selection_request (ev.time, ev.x, ev.y);
2019             break;
2020 #ifdef MOUSE_WHEEL
2021           case Button4:
2022           case Button5:
2023             {
2024               int i;
2025               page_dirn v;
2026
2027               v = ev.button == Button4 ? UP : DN;
2028
2029               if (ev.state & ShiftMask)
2030                 i = 1;
2031               else if (options & Opt_mouseWheelScrollPage)
2032                 i = TermWin.nrow - 1;
2033               else
2034                 i = 5;
2035
2036 # ifdef MOUSE_SLIP_WHEELING
2037               if (ev.state & ControlMask)
2038                 {
2039                   mouse_slip_wheel_speed += v ? -1 : 1;
2040                   if (mouse_slip_wheel_speed < -TermWin.nrow) mouse_slip_wheel_speed = -TermWin.nrow;
2041                   if (mouse_slip_wheel_speed > +TermWin.nrow) mouse_slip_wheel_speed = +TermWin.nrow;
2042
2043                   if (slip_wheel_ev.at < NOW)
2044                     slip_wheel_ev.at = NOW + SCROLLBAR_CONTINUOUS_DELAY;
2045
2046                   slip_wheel_ev.start ();
2047                 }
2048               else
2049                 {
2050 # endif
2051 # ifdef JUMP_MOUSE_WHEEL
2052                   scr_page (v, i);
2053                   scr_refresh (SMOOTH_REFRESH);
2054                   scrollbar_show (1);
2055 # else
2056                   while (i--)
2057                     {
2058                       scr_page (v, 1);
2059                       scr_refresh (SMOOTH_REFRESH);
2060                       scrollbar_show (1);
2061                     }
2062 # endif
2063 # ifdef MOUSE_SLIP_WHEELING
2064                 }
2065 #endif
2066             }
2067             break;
2068 #endif
2069         }
2070     }
2071 #ifdef MENUBAR
2072   else if (isMenuBarWindow (ev.window))
2073     menubar_control (ev);
2074 #endif
2075 }
2076
2077 #ifdef TRANSPARENT
2078 #if TINTING
2079 /* taken from aterm-0.4.2 */
2080
2081 typedef uint32_t RUINT32T;
2082
2083 void ShadeXImage(rxvt_display *display, XImage* srcImage, int shade, int rm, int gm, int bm)
2084 {
2085   int sh_r, sh_g, sh_b;
2086   RUINT32T mask_r, mask_g, mask_b;
2087   RUINT32T *lookup, *lookup_r, *lookup_g, *lookup_b;
2088   unsigned int lower_lim_r, lower_lim_g, lower_lim_b;
2089   unsigned int upper_lim_r, upper_lim_g, upper_lim_b;
2090   int i;
2091
2092   Visual* visual = display->visual;
2093
2094   if( visual->c_class != TrueColor || srcImage->format != ZPixmap ) return ;
2095
2096   /* for convenience */
2097   mask_r = visual->red_mask;
2098   mask_g = visual->green_mask;
2099   mask_b = visual->blue_mask;
2100
2101   /* boring lookup table pre-initialization */
2102   switch (srcImage->bits_per_pixel) {
2103     case 15:
2104       if ((mask_r != 0x7c00) ||
2105           (mask_g != 0x03e0) ||
2106           (mask_b != 0x001f))
2107         return;
2108         lookup = (RUINT32T *) malloc (sizeof (RUINT32T)*(32+32+32));
2109         lookup_r = lookup;
2110         lookup_g = lookup+32;
2111         lookup_b = lookup+32+32;
2112         sh_r = 10;
2113         sh_g = 5;
2114         sh_b = 0;
2115       break;
2116     case 16:
2117       if ((mask_r != 0xf800) ||
2118           (mask_g != 0x07e0) ||
2119           (mask_b != 0x001f))
2120         return;
2121         lookup = (RUINT32T *) malloc (sizeof (RUINT32T)*(32+64+32));
2122         lookup_r = lookup;
2123         lookup_g = lookup+32;
2124         lookup_b = lookup+32+64;
2125         sh_r = 11;
2126         sh_g = 5;
2127         sh_b = 0;
2128       break;
2129     case 24:
2130       if ((mask_r != 0xff0000) ||
2131           (mask_g != 0x00ff00) ||
2132           (mask_b != 0x0000ff))
2133         return;
2134         lookup = (RUINT32T *) malloc (sizeof (RUINT32T)*(256+256+256));
2135         lookup_r = lookup;
2136         lookup_g = lookup+256;
2137         lookup_b = lookup+256+256;
2138         sh_r = 16;
2139         sh_g = 8;
2140         sh_b = 0;
2141       break;
2142     case 32:
2143       if ((mask_r != 0xff0000) ||
2144           (mask_g != 0x00ff00) ||
2145           (mask_b != 0x0000ff))
2146         return;
2147         lookup = (RUINT32T *) malloc (sizeof (RUINT32T)*(256+256+256));
2148         lookup_r = lookup;
2149         lookup_g = lookup+256;
2150         lookup_b = lookup+256+256;
2151         sh_r = 16;
2152         sh_g = 8;
2153         sh_b = 0;
2154       break;
2155     default:
2156       return; /* we do not support this color depth */
2157   }
2158
2159   /* prepare limits for color transformation (each channel is handled separately) */
2160   if (shade < 0) {
2161     shade = -shade;
2162     if (shade < 0) shade = 0;
2163     if (shade > 100) shade = 100;
2164
2165     lower_lim_r = 65535-rm;
2166     lower_lim_g = 65535-gm;
2167     lower_lim_b = 65535-bm;
2168
2169     lower_lim_r = 65535-(unsigned int)(((RUINT32T)lower_lim_r)*((RUINT32T)shade)/100);
2170     lower_lim_g = 65535-(unsigned int)(((RUINT32T)lower_lim_g)*((RUINT32T)shade)/100);
2171     lower_lim_b = 65535-(unsigned int)(((RUINT32T)lower_lim_b)*((RUINT32T)shade)/100);
2172
2173     upper_lim_r = upper_lim_g = upper_lim_b = 65535;
2174   } else {
2175     if (shade < 0) shade = 0;
2176     if (shade > 100) shade = 100;
2177
2178     lower_lim_r = lower_lim_g = lower_lim_b = 0;
2179
2180     upper_lim_r = (unsigned int)((((RUINT32T)rm)*((RUINT32T)shade))/100);
2181     upper_lim_g = (unsigned int)((((RUINT32T)gm)*((RUINT32T)shade))/100);
2182     upper_lim_b = (unsigned int)((((RUINT32T)bm)*((RUINT32T)shade))/100);
2183   }
2184
2185   /* switch red and blue bytes if necessary, we need it for some weird XServers like XFree86 3.3.3.1 */
2186   if ((srcImage->bits_per_pixel == 24) && (mask_r >= 0xFF0000 ))
2187   {
2188     unsigned int tmp;
2189
2190     tmp = lower_lim_r;
2191     lower_lim_r = lower_lim_b;
2192     lower_lim_b = tmp;
2193
2194     tmp = upper_lim_r;
2195     upper_lim_r = upper_lim_b;
2196     upper_lim_b = tmp;
2197   }
2198
2199   /* fill our lookup tables */
2200   for (i = 0; i <= mask_r>>sh_r; i++)
2201   {
2202     RUINT32T tmp;
2203     tmp = ((RUINT32T)i)*((RUINT32T)(upper_lim_r-lower_lim_r));
2204     tmp += ((RUINT32T)(mask_r>>sh_r))*((RUINT32T)lower_lim_r);
2205     lookup_r[i] = (tmp/65535)<<sh_r;
2206   }
2207   for (i = 0; i <= mask_g>>sh_g; i++)
2208   {
2209     RUINT32T tmp;
2210     tmp = ((RUINT32T)i)*((RUINT32T)(upper_lim_g-lower_lim_g));
2211     tmp += ((RUINT32T)(mask_g>>sh_g))*((RUINT32T)lower_lim_g);
2212     lookup_g[i] = (tmp/65535)<<sh_g;
2213   }
2214   for (i = 0; i <= mask_b>>sh_b; i++)
2215   {
2216     RUINT32T tmp;
2217     tmp = ((RUINT32T)i)*((RUINT32T)(upper_lim_b-lower_lim_b));
2218     tmp += ((RUINT32T)(mask_b>>sh_b))*((RUINT32T)lower_lim_b);
2219     lookup_b[i] = (tmp/65535)<<sh_b;
2220   }
2221
2222   /* apply table to input image (replacing colors by newly calculated ones) */
2223   switch (srcImage->bits_per_pixel)
2224   {
2225     case 15:
2226     {
2227       unsigned short *p1, *pf, *p, *pl;
2228       p1 = (unsigned short *) srcImage->data;
2229       pf = (unsigned short *) (srcImage->data + srcImage->height * srcImage->bytes_per_line);
2230       while (p1 < pf)
2231       {
2232         p = p1;
2233         pl = p1 + srcImage->width;
2234         for (; p < pl; p++)
2235         {
2236           *p = lookup_r[(*p & 0x7c00)>>10] |
2237                lookup_g[(*p & 0x03e0)>> 5] |
2238                lookup_b[(*p & 0x001f)];
2239         }
2240         p1 = (unsigned short *) ((char *) p1 + srcImage->bytes_per_line);
2241       }
2242       break;
2243     }
2244     case 16:
2245     {
2246       unsigned short *p1, *pf, *p, *pl;
2247       p1 = (unsigned short *) srcImage->data;
2248       pf = (unsigned short *) (srcImage->data + srcImage->height * srcImage->bytes_per_line);
2249       while (p1 < pf)
2250       {
2251         p = p1;
2252         pl = p1 + srcImage->width;
2253         for (; p < pl; p++)
2254         {
2255           *p = lookup_r[(*p & 0xf800)>>11] |
2256                lookup_g[(*p & 0x07e0)>> 5] |
2257                lookup_b[(*p & 0x001f)];
2258         }
2259         p1 = (unsigned short *) ((char *) p1 + srcImage->bytes_per_line);
2260       }
2261       break;
2262     }
2263     case 24:
2264     {
2265       unsigned char *p1, *pf, *p, *pl;
2266       p1 = (unsigned char *) srcImage->data;
2267       pf = (unsigned char *) (srcImage->data + srcImage->height * srcImage->bytes_per_line);
2268       while (p1 < pf)
2269       {
2270         p = p1;
2271         pl = p1 + srcImage->width * 3;
2272         for (; p < pl; p += 3)
2273         {
2274           p[0] = lookup_r[(p[0] & 0xff0000)>>16];
2275           p[1] = lookup_r[(p[1] & 0x00ff00)>> 8];
2276           p[2] = lookup_r[(p[2] & 0x0000ff)];
2277         }
2278         p1 = (unsigned char *) ((char *) p1 + srcImage->bytes_per_line);
2279       }
2280       break;
2281     }
2282     case 32:
2283     {
2284       RUINT32T *p1, *pf, *p, *pl;
2285       p1 = (RUINT32T *) srcImage->data;
2286       pf = (RUINT32T *) (srcImage->data + srcImage->height * srcImage->bytes_per_line);
2287
2288       while (p1 < pf)
2289       {
2290         p = p1;
2291         pl = p1 + srcImage->width;
2292         for (; p < pl; p++)
2293         {
2294           *p = lookup_r[(*p & 0xff0000)>>16] |
2295                lookup_g[(*p & 0x00ff00)>> 8] |
2296                lookup_b[(*p & 0x0000ff)] |
2297                (*p & ~0xffffff);
2298         }
2299         p1 = (RUINT32T *) ((char *) p1 + srcImage->bytes_per_line);
2300       }
2301       break;
2302     }
2303   }
2304
2305   free (lookup);
2306 }
2307 #endif
2308
2309 /*
2310  * Check our parents are still who we think they are.
2311  * Do transparency updates if required
2312  */
2313 int
2314 rxvt_term::check_our_parents ()
2315 {
2316   int i, pchanged, aformat, have_pixmap, rootdepth;
2317   unsigned long nitems, bytes_after;
2318   Atom atype;
2319   unsigned char *prop = NULL;
2320   Window root, oldp, *list;
2321   Pixmap rootpixmap = None;
2322   XWindowAttributes wattr, wrootattr;
2323
2324   pchanged = 0;
2325
2326   if (!(options & Opt_transparent))
2327     return pchanged;    /* Don't try any more */
2328
2329   XGetWindowAttributes (display->display, display->root, &wrootattr);
2330   rootdepth = wrootattr.depth;
2331
2332   XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
2333
2334   if (rootdepth != wattr.depth)
2335     {
2336       if (am_transparent)
2337         {
2338           pchanged = 1;
2339           XSetWindowBackground (display->display, TermWin.vt, pix_colors_focused[Color_bg]);
2340           am_transparent = am_pixmap_trans = 0;
2341         }
2342
2343       return pchanged;  /* Don't try any more */
2344     }
2345
2346   /* Get all X ops out of the queue so that our information is up-to-date. */
2347   XSync (display->display, False);
2348
2349   /*
2350    * Make the frame window set by the window manager have
2351    * the root background. Some window managers put multiple nested frame
2352    * windows for each client, so we have to take care about that.
2353    */
2354   i = (xa[XA_XROOTPMAPID]
2355        && XGetWindowProperty (display->display, display->root, xa[XA_XROOTPMAPID],
2356                               0L, 1L, False, XA_PIXMAP, &atype, &aformat,
2357                               &nitems, &bytes_after, &prop) == Success);
2358
2359   if (!i || prop == NULL)
2360      i = (xa[XA_XSETROOTID]
2361           && XGetWindowProperty (display->display, display->root, xa[XA_XSETROOTID],
2362                                  0L, 1L, False, XA_PIXMAP, &atype, &aformat,
2363                                  &nitems, &bytes_after, &prop) == Success);
2364
2365   if (!i || prop == NULL
2366 #if TINTING
2367       || !rs[Rs_color + Color_tint]
2368 #endif
2369       )
2370     have_pixmap = 0;
2371   else
2372     {
2373       have_pixmap = 1;
2374       rootpixmap = *(Pixmap *)prop;
2375       XFree (prop);
2376     }
2377
2378   if (have_pixmap)
2379     {
2380       /*
2381        * Copy display->root pixmap transparency
2382        */
2383       int sx, sy, nx, ny;
2384       unsigned int nw, nh;
2385       Window cr;
2386       XImage *image;
2387       GC gc;
2388       XGCValues gcvalue;
2389
2390       XTranslateCoordinates (display->display, TermWin.parent[0], display->root,
2391                              0, 0, &sx, &sy, &cr);
2392       nw = (unsigned int)szHint.width;
2393       nh = (unsigned int)szHint.height;
2394       nx = ny = 0;
2395
2396       if (sx < 0)
2397         {
2398           nw += sx;
2399           nx = -sx;
2400           sx = 0;
2401         }
2402
2403       if (sy < 0)
2404         {
2405           nh += sy;
2406           ny = -sy;
2407           sy = 0;
2408         }
2409
2410       MIN_IT (nw, (unsigned int) (wrootattr.width - sx));
2411       MIN_IT (nh, (unsigned int) (wrootattr.height - sy));
2412       allowedxerror = -1;
2413       image = XGetImage (display->display, rootpixmap, sx, sy, nw, nh, AllPlanes, ZPixmap);
2414
2415       /* XXX: handle BadMatch - usually because we're outside the pixmap */
2416       /* XXX: may need a delay here? */
2417       allowedxerror = 0;
2418
2419       if (image == NULL)
2420         {
2421           if (am_transparent && am_pixmap_trans)
2422             {
2423               pchanged = 1;
2424               if (TermWin.pixmap != None)
2425                 {
2426                   XFreePixmap (display->display, TermWin.pixmap);
2427                   TermWin.pixmap = None;
2428                 }
2429             }
2430
2431           am_pixmap_trans = 0;
2432         }
2433       else
2434         {
2435           if (TermWin.pixmap != None)
2436             XFreePixmap (display->display, TermWin.pixmap);
2437
2438 #if TINTING
2439           if (ISSET_PIXCOLOR (Color_tint))
2440             {
2441               unsigned short rm, gm, bm;
2442               int shade = rs[Rs_shade] ? atoi (rs[Rs_shade]) : 100;
2443
2444               pix_colors_focused[Color_tint].get (display, rm, gm, bm);
2445
2446               ShadeXImage (display, image, shade, rm, gm, bm);
2447             }
2448 #endif
2449
2450           TermWin.pixmap = XCreatePixmap (display->display, TermWin.vt,
2451                                           szHint.width, szHint.height, image->depth);
2452           gc = XCreateGC (display->display, TermWin.vt, 0UL, &gcvalue);
2453           XPutImage (display->display, TermWin.pixmap, gc, image, 0, 0,
2454                      nx, ny, image->width, image->height);
2455           XFreeGC (display->display, gc);
2456           XDestroyImage (image);
2457           XSetWindowBackgroundPixmap (display->display, TermWin.parent[0], TermWin.pixmap);
2458           XClearWindow (display->display, TermWin.parent[0]);
2459
2460           if (!am_transparent || !am_pixmap_trans)
2461             pchanged = 1;
2462
2463           am_transparent = am_pixmap_trans = 1;
2464         }
2465     }
2466
2467   if (!am_pixmap_trans)
2468     {
2469       unsigned int n;
2470       /*
2471        * InheritPixmap transparency
2472        */
2473       for (i = 1; i < (int) (sizeof (TermWin.parent) / sizeof (Window)); i++)
2474         {
2475           oldp = TermWin.parent[i];
2476           XQueryTree (display->display, TermWin.parent[i - 1], &root,
2477                       &TermWin.parent[i], &list, &n);
2478           XFree (list);
2479
2480           if (TermWin.parent[i] == display->root)
2481             {
2482               if (oldp != None)
2483                 pchanged = 1;
2484
2485               break;
2486             }
2487
2488           if (oldp != TermWin.parent[i])
2489             pchanged = 1;
2490         }
2491
2492       n = 0;
2493
2494       if (pchanged)
2495         {
2496           for (; n < (unsigned int)i; n++)
2497             {
2498               XGetWindowAttributes (display->display, TermWin.parent[n], &wattr);
2499               if (wattr.depth != rootdepth || wattr.c_class == InputOnly)
2500                 {
2501                   n = (int) (sizeof (TermWin.parent) / sizeof (Window)) + 1;
2502                   break;
2503                 }
2504             }
2505         }
2506
2507       if (n > (int) (sizeof (TermWin.parent) / sizeof (TermWin.parent[0])))
2508         {
2509           XSetWindowBackground (display->display, TermWin.parent[0], pix_colors_focused[Color_fg]);
2510           XSetWindowBackground (display->display, TermWin.vt, pix_colors_focused[Color_bg]);
2511           am_transparent = 0;
2512           /* XXX: also turn off Opt_transparent? */
2513         }
2514       else
2515         {
2516 #if WAIT_FOR_WM
2517           /* wait (an arbitrary period) for the WM to do its thing
2518            * needed for fvwm2.2.2 (and before?) */
2519           sleep (1);
2520 #endif
2521           for (n = 0; n < (unsigned int)i; n++)
2522             {
2523               XSetWindowBackgroundPixmap (display->display, TermWin.parent[n], ParentRelative);
2524               XClearWindow (display->display, TermWin.parent[n]);
2525             }
2526
2527           XSetWindowBackgroundPixmap (display->display, TermWin.vt, ParentRelative);
2528           am_transparent = 1;
2529         }
2530
2531       for (; i < (int) (sizeof (TermWin.parent) / sizeof (Window)); i++)
2532         TermWin.parent[i] = None;
2533     }
2534
2535   if (scrollBar.win)
2536     {
2537       XSetWindowBackgroundPixmap (display->display, scrollBar.win, ParentRelative);
2538       scrollBar.setIdle ();
2539       scrollbar_show (0);
2540     }
2541
2542   return pchanged;
2543 }
2544 #endif
2545
2546 /*}}} */
2547
2548 bool
2549 rxvt_term::cmd_parse ()
2550 {
2551   bool flag = false;
2552   unicode_t ch = NOCHAR;
2553   unsigned char *seq_begin; // remember start of esc-sequence here
2554
2555   for (;;)
2556     {
2557       if (ch == NOCHAR)
2558         {
2559           seq_begin = cmdbuf_ptr;
2560           ch = next_char ();
2561         }
2562
2563       if (ch == NOCHAR) // TODO: improve
2564         break;
2565
2566       if (!IS_CONTROL (ch) || ch == C0_LF || ch == C0_CR || ch == C0_HT)
2567         {
2568           if (!seen_input)
2569             {
2570               seen_input = 1;
2571               // many badly-written programs (e.g. jed) contain a race condition:
2572               // they first read the screensize and then install a SIGWINCH handler.
2573               // some window managers resize the window early, and these programs
2574               // then sometimes get the size wrong.
2575               // unfortunately other programs are even more buggy and dislike
2576               // being sent SIGWINCH, so only do it when we were in fact being
2577               // resized.
2578               if (seen_resize)
2579                 kill (-cmd_pid, SIGWINCH);
2580             }
2581
2582           /* Read a text string from the input buffer */
2583           unicode_t buf[UBUFSIZ];
2584           bool refreshnow = false;
2585           int nlines = 0;
2586           unicode_t *str = buf;
2587
2588           for (;;)
2589             {
2590               if (ch == NOCHAR || (IS_CONTROL (ch) && ch != C0_LF && ch != C0_CR && ch != C0_HT))
2591                 break;
2592
2593               *str++ = ch;
2594
2595               if (ch == C0_LF)
2596                 {
2597                   nlines++;
2598                   refresh_count++;
2599
2600                   if (!(options & Opt_jumpScroll)
2601                       || (refresh_count >= refresh_limit * (TermWin.nrow - 1)))
2602                     {
2603                       refreshnow = true;
2604                       ch = NOCHAR;
2605                       break;
2606                     }
2607
2608                   // scr_add_lines only works for nlines <= TermWin.nrow - 1.
2609                   if (nlines >= TermWin.nrow - 1)
2610                     {
2611                       scr_add_lines (buf, nlines, str - buf);
2612                       nlines = 0;
2613                       str = buf;
2614                     }
2615                 }
2616
2617               if (str >= buf + UBUFSIZ)
2618                 {
2619                   ch = NOCHAR;
2620                   break;
2621                 }
2622
2623               seq_begin = cmdbuf_ptr;
2624               ch = next_char ();
2625             }
2626
2627           scr_add_lines (buf, nlines, str - buf);
2628
2629           /*
2630            * If there have been a lot of new lines, then update the screen
2631            * What the heck I'll cheat and only refresh less than every page-full.
2632            * the number of pages between refreshes is refresh_limit, which
2633            * is incremented here because we must be doing flat-out scrolling.
2634            */
2635           if (refreshnow)
2636             {
2637               if ((options & Opt_jumpScroll) && refresh_limit < REFRESH_PERIOD)
2638                 refresh_limit++;
2639               else
2640                 {
2641                   flag = true;
2642                   scr_refresh (refresh_type);
2643                 }
2644             }
2645
2646         }
2647       else
2648         {
2649           try
2650             {
2651               process_nonprinting (ch);
2652             }
2653           catch (const class out_of_input &o)
2654             {
2655               // we ran out of input, retry later
2656               cmdbuf_ptr = seq_begin;
2657               break;
2658             }
2659
2660           ch = NOCHAR;
2661         }
2662     }
2663
2664   return flag;
2665 }
2666
2667 // read the next octet
2668 unicode_t
2669 rxvt_term::next_octet ()
2670 {
2671   return cmdbuf_ptr < cmdbuf_endp
2672          ? *cmdbuf_ptr++
2673          : NOCHAR;
2674 }
2675
2676 // read the next character
2677 unicode_t
2678 rxvt_term::next_char ()
2679 {
2680   while (cmdbuf_ptr < cmdbuf_endp)
2681     {
2682       // assume 7-bit to be ascii ALWAYS
2683       if (*cmdbuf_ptr <= 0x7f && *cmdbuf_ptr != 0x1b)
2684         return *cmdbuf_ptr++;
2685
2686       wchar_t wc;
2687       size_t len = mbrtowc (&wc, (char *)cmdbuf_ptr, cmdbuf_endp - cmdbuf_ptr, mbstate);
2688
2689       if (len == (size_t)-2)
2690         {
2691           // the mbstate stores incomplete sequences. didn't know this :/
2692           cmdbuf_ptr = cmdbuf_endp;
2693           break;
2694         }
2695
2696       if (len == (size_t)-1)
2697         return *cmdbuf_ptr++; // the _occasional_ latin1 character is allowed to slip through
2698
2699       // assume wchar == unicode
2700       cmdbuf_ptr += len;
2701       return wc;
2702     }
2703
2704   return NOCHAR;
2705 }
2706
2707 /* rxvt_cmd_getc () - Return next input character */
2708 /*
2709  * Return the next input character after first passing any keyboard input
2710  * to the command.
2711  */
2712 unicode_t
2713 rxvt_term::cmd_getc ()
2714 {
2715   unicode_t c = next_char ();
2716
2717   if (c == NOCHAR)
2718     throw out_of_input;
2719
2720   return c;
2721 }
2722
2723 unicode_t
2724 rxvt_term::cmd_get8 ()
2725 {
2726   unicode_t c = next_octet ();
2727
2728   if (c == NOCHAR)
2729     throw out_of_input;
2730
2731   return c;
2732 }
2733
2734 /*{{{ print pipe */
2735 /*----------------------------------------------------------------------*/
2736 #ifdef PRINTPIPE
2737 FILE *
2738 rxvt_term::popen_printer ()
2739 {
2740   FILE *stream = popen (rs[Rs_print_pipe], "w");
2741
2742   if (stream == NULL)
2743     rxvt_warn ("can't open printer pipe, not printing.\n");
2744
2745   return stream;
2746 }
2747
2748 int
2749 rxvt_term::pclose_printer (FILE *stream)
2750 {
2751   fflush (stream);
2752   return pclose (stream);
2753 }
2754
2755 /*
2756  * simulate attached vt100 printer
2757  */
2758 void
2759 rxvt_term::process_print_pipe ()
2760 {
2761   int done;
2762   FILE *fd;
2763
2764   if ((fd = popen_printer ()) == NULL)
2765     return;
2766
2767   /*
2768    * Send all input to the printer until either ESC[4i or ESC[?4i
2769    * is received.
2770    */
2771   for (done = 0; !done;)
2772     {
2773       unsigned char buf[8];
2774       unicode_t ch;
2775       unsigned int i, len;
2776
2777       if ((ch = cmd_getc ()) != C0_ESC)
2778         {
2779           if (putc (ch, fd) == EOF)
2780             break;              /* done = 1 */
2781         }
2782       else
2783         {
2784           len = 0;
2785           buf[len++] = ch;
2786
2787           if ((buf[len++] = cmd_getc ()) == '[')
2788             {
2789               if ((ch = cmd_getc ()) == '?')
2790                 {
2791                   buf[len++] = '?';
2792                   ch = cmd_getc ();
2793                 }
2794               if ((buf[len++] = ch) == '4')
2795                 {
2796                   if ((buf[len++] = cmd_getc ()) == 'i')
2797                     break;      /* done = 1 */
2798                 }
2799             }
2800           
2801           for (i = 0; i < len; i++)
2802             if (putc (buf[i], fd) == EOF)
2803               {
2804                 done = 1;
2805                 break;
2806               }
2807         }
2808     }
2809
2810   pclose_printer (fd);
2811 }
2812 #endif                          /* PRINTPIPE */
2813 /*}}} */
2814
2815 /* *INDENT-OFF* */
2816 enum {
2817   C1_40 = 0x40,
2818           C1_41 , C1_BPH, C1_NBH, C1_44 , C1_NEL, C1_SSA, C1_ESA,
2819   C1_HTS, C1_HTJ, C1_VTS, C1_PLD, C1_PLU, C1_RI , C1_SS2, C1_SS3,
2820   C1_DCS, C1_PU1, C1_PU2, C1_STS, C1_CCH, C1_MW , C1_SPA, C1_EPA,
2821   C1_SOS, C1_59 , C1_SCI, C1_CSI, CS_ST , C1_OSC, C1_PM , C1_APC,
2822 };
2823 /* *INDENT-ON* */
2824
2825 /*{{{ process non-printing single characters */
2826 void
2827 rxvt_term::process_nonprinting (unicode_t ch)
2828 {
2829   switch (ch)
2830     {
2831       case C0_ESC:
2832         process_escape_seq ();
2833         break;
2834       case C0_ENQ:      /* terminal Status */
2835         if (rs[Rs_answerbackstring])
2836           tt_write ((const unsigned char *)rs[Rs_answerbackstring],
2837                     (unsigned int)strlen (rs[Rs_answerbackstring]));
2838         else
2839           tt_write ((unsigned char *)VT100_ANS,
2840                     (unsigned int)strlen (VT100_ANS));
2841         break;
2842       case C0_BEL:      /* bell */
2843         scr_bell ();
2844         break;
2845       case C0_BS:               /* backspace */
2846         scr_backspace ();
2847         break;
2848       case C0_HT:               /* tab */
2849         scr_tab (1);
2850         break;
2851       case C0_CR:               /* carriage return */
2852         scr_gotorc (0, 0, R_RELATIVE);
2853         break;
2854       case C0_VT:               /* vertical tab, form feed */
2855       case C0_FF:
2856       case C0_LF:               /* line feed */
2857         scr_index (UP);
2858         break;
2859       case C0_SO:               /* shift out - acs */
2860         scr_charset_choose (1);
2861         break;
2862       case C0_SI:               /* shift in - acs */
2863         scr_charset_choose (0);
2864         break;
2865
2866 #ifdef EIGHT_BIT_CONTROLS
2867       // 8-bit controls
2868       case 0x90:        /* DCS */
2869         process_dcs_seq ();
2870         break;
2871       case 0x9b:        /* CSI */
2872         process_csi_seq ();
2873         break;
2874       case 0x9d:        /* CSI */
2875         process_osc_seq ();
2876         break;
2877 #endif
2878     }
2879 }
2880 /*}}} */
2881
2882
2883 /*{{{ process VT52 escape sequences */
2884 void
2885 rxvt_term::process_escape_vt52 (unicode_t ch)
2886 {
2887   int row, col;
2888
2889   switch (ch)
2890     {
2891       case 'A':         /* cursor up */
2892         scr_gotorc (-1, 0, R_RELATIVE | C_RELATIVE);
2893         break;
2894       case 'B':         /* cursor down */
2895         scr_gotorc (1, 0, R_RELATIVE | C_RELATIVE);
2896         break;
2897       case 'C':         /* cursor right */
2898         scr_gotorc (0, 1, R_RELATIVE | C_RELATIVE);
2899         break;
2900       case 'D':         /* cursor left */
2901         scr_gotorc (0, -1, R_RELATIVE | C_RELATIVE);
2902         break;
2903       case 'H':         /* cursor home */
2904         scr_gotorc (0, 0, 0);
2905         break;
2906       case 'I':         /* cursor up and scroll down if needed */
2907         scr_index (DN);
2908         break;
2909       case 'J':         /* erase to end of screen */
2910         scr_erase_screen (0);
2911         break;
2912       case 'K':         /* erase to end of line */
2913         scr_erase_line (0);
2914         break;
2915       case 'Y':                 /* move to specified row and col */
2916         /* full command is 'ESC Y row col' where row and col
2917          * are encoded by adding 32 and sending the ascii
2918          * character.  eg. SPACE = 0, '+' = 13, '0' = 18,
2919          * etc. */
2920         row = cmd_getc () - ' ';
2921         col = cmd_getc () - ' ';
2922         scr_gotorc (row, col, 0);
2923         break;
2924       case 'Z':         /* identify the terminal type */
2925         tt_printf ("\033/Z");   /* I am a VT100 emulating a VT52 */
2926         break;
2927       case '<':         /* turn off VT52 mode */
2928         PrivMode (0, PrivMode_vt52);
2929         break;
2930       case 'F':         /* use special graphics character set */
2931       case 'G':           /* use regular character set */
2932         /* unimplemented */
2933         break;
2934       case '=':         /* use alternate keypad mode */
2935       case '>':           /* use regular keypad mode */
2936         /* unimplemented */
2937         break;
2938     }
2939 }
2940 /*}}} */
2941
2942
2943 /*{{{ process escape sequences */
2944 void
2945 rxvt_term::process_escape_seq ()
2946 {
2947   unicode_t ch = cmd_getc ();
2948
2949   if (priv_modes & PrivMode_vt52)
2950     {
2951       process_escape_vt52 (ch);
2952       return;
2953     }
2954
2955   switch (ch)
2956     {
2957         /* case 1:        do_tek_mode (); break; */
2958       case '#':
2959         if (cmd_getc () == '8')
2960           scr_E ();
2961         break;
2962       case '(':
2963         scr_charset_set (0, (unsigned int)cmd_getc ());
2964         break;
2965       case ')':
2966         scr_charset_set (1, (unsigned int)cmd_getc ());
2967         break;
2968       case '*':
2969         scr_charset_set (2, (unsigned int)cmd_getc ());
2970         break;
2971       case '+':
2972         scr_charset_set (3, (unsigned int)cmd_getc ());
2973         break;
2974 #if ENABLE_FRILLS
2975       case '6':
2976         scr_backindex ();
2977         break;
2978 #endif
2979       case '7':
2980         scr_cursor (SAVE);
2981         break;
2982       case '8':
2983         scr_cursor (RESTORE);
2984         break;
2985 #if ENABLE_FRILLS
2986       case '9':
2987         scr_forwardindex ();
2988         break;
2989 #endif
2990       case '=':
2991       case '>':
2992         PrivMode ((ch == '='), PrivMode_aplKP);
2993         break;
2994
2995       case C1_40:
2996         cmd_getc ();
2997         break;
2998       case C1_44:
2999         scr_index (UP);
3000         break;
3001
3002         /* 8.3.87: NEXT LINE */
3003       case C1_NEL:              /* ESC E */
3004         {
3005           unicode_t nlcr[] = { C0_LF, C0_CR };
3006           scr_add_lines (nlcr, 1, 2);
3007         }
3008         break;
3009
3010         /* kidnapped escape sequence: Should be 8.3.48 */
3011       case C1_ESA:              /* ESC G */
3012         process_graphics ();
3013         break;
3014
3015         /* 8.3.63: CHARACTER TABULATION SET */
3016       case C1_HTS:              /* ESC H */
3017         scr_set_tab (1);
3018         break;
3019
3020         /* 8.3.105: REVERSE LINE FEED */
3021       case C1_RI:                       /* ESC M */
3022         scr_index (DN);
3023         break;
3024
3025         /* 8.3.142: SINGLE-SHIFT TWO */
3026       /*case C1_SS2: scr_single_shift (2);   break; */
3027
3028         /* 8.3.143: SINGLE-SHIFT THREE */
3029       /*case C1_SS3: scr_single_shift (3);   break; */
3030
3031         /* 8.3.27: DEVICE CONTROL STRING */
3032       case C1_DCS:              /* ESC P */
3033         process_dcs_seq ();
3034         break;
3035
3036         /* 8.3.110: SINGLE CHARACTER INTRODUCER */
3037       case C1_SCI:              /* ESC Z */
3038         tt_write ((const unsigned char *)ESCZ_ANSWER,
3039                  (unsigned int) (sizeof (ESCZ_ANSWER) - 1));
3040         break;                  /* steal obsolete ESC [ c */
3041
3042         /* 8.3.16: CONTROL SEQUENCE INTRODUCER */
3043       case C1_CSI:              /* ESC [ */
3044         process_csi_seq ();
3045         break;
3046
3047         /* 8.3.90: OPERATING SYSTEM COMMAND */
3048       case C1_OSC:              /* ESC ] */
3049         process_osc_seq ();
3050         break;
3051
3052         /* 8.3.106: RESET TO INITIAL STATE */
3053       case 'c':
3054         mbstate.reset ();
3055         scr_poweron ();
3056         scrollbar_show (1);
3057         break;
3058
3059         /* 8.3.79: LOCKING-SHIFT TWO (see ISO2022) */
3060       case 'n':
3061         scr_charset_choose (2);
3062         break;
3063
3064         /* 8.3.81: LOCKING-SHIFT THREE (see ISO2022) */
3065       case 'o':
3066         scr_charset_choose (3);
3067         break;
3068     }
3069 }
3070 /*}}} */
3071
3072 /*{{{ process CONTROL SEQUENCE INTRODUCER (CSI) sequences `ESC[' */
3073 /* *INDENT-OFF* */
3074 enum {
3075   CSI_ICH = 0x40,
3076            CSI_CUU, CSI_CUD, CSI_CUF, CSI_CUB, CSI_CNL, CSI_CPL, CSI_CHA,
3077   CSI_CUP, CSI_CHT, CSI_ED , CSI_EL , CSI_IL , CSI_DL , CSI_EF , CSI_EA ,
3078   CSI_DCH, CSI_SEE, CSI_CPR, CSI_SU , CSI_SD , CSI_NP , CSI_PP , CSI_CTC,
3079   CSI_ECH, CSI_CVT, CSI_CBT, CSI_SRS, CSI_PTX, CSI_SDS, CSI_SIMD, CSI_5F,
3080   CSI_HPA, CSI_HPR, CSI_REP, CSI_DA , CSI_VPA, CSI_VPR, CSI_HVP, CSI_TBC,
3081   CSI_SM , CSI_MC , CSI_HPB, CSI_VPB, CSI_RM , CSI_SGR, CSI_DSR, CSI_DAQ,
3082   CSI_70 , CSI_71 , CSI_72 , CSI_73 , CSI_74 , CSI_75 , CSI_76 , CSI_77 ,
3083   CSI_78 , CSI_79 , CSI_7A , CSI_7B , CSI_7C , CSI_7D , CSI_7E , CSI_7F
3084 };
3085
3086 #define make_byte(b7,b6,b5,b4,b3,b2,b1,b0)                      \
3087     (((b7) << 7) | ((b6) << 6) | ((b5) << 5) | ((b4) << 4)      \
3088      | ((b3) << 3) | ((b2) << 2) | ((b1) << 1) | (b0))
3089 #define get_byte_array_bit(array, bit)                          \
3090     (!! ((array)[ (bit) / 8] & (128 >> ((bit) & 7))))
3091
3092 const unsigned char csi_defaults[] =
3093   {
3094     make_byte (1,1,1,1,1,1,1,1),        /* @, A, B, C, D, E, F, G, */
3095     make_byte (1,1,0,0,1,1,0,0),        /* H, I, J, K, L, M, N, O, */
3096     make_byte (1,0,1,1,1,1,1,0),        /* P, Q, R, S, T, U, V, W, */
3097     make_byte (1,1,1,0,0,0,1,0),        /* X, Y, Z, [, \, ], ^, _, */
3098     make_byte (1,1,1,0,1,1,1,0),        /* `, a, b, c, d, e, f, g, */
3099     make_byte (0,0,1,1,0,0,0,0),        /* h, i, j, k, l, m, n, o, */
3100     make_byte (0,0,0,0,0,0,0,0),        /* p, q, r, s, t, u, v, w, */
3101     make_byte (0,0,0,0,0,0,0,0),        /* x, y, z, {, |, }, ~,    */
3102   };
3103 /* *INDENT-ON* */
3104
3105 void
3106 rxvt_term::process_csi_seq ()
3107 {
3108   unicode_t ch, priv, i;
3109   unsigned int nargs, p;
3110   int n, ndef;
3111   int arg[ESC_ARGS];
3112
3113   for (nargs = ESC_ARGS; nargs > 0;)
3114     arg[--nargs] = 0;
3115
3116   priv = 0;
3117   ch = cmd_getc ();
3118   if (ch >= '<' && ch <= '?')
3119     {   /* '<' '=' '>' '?' */
3120       priv = ch;
3121       ch = cmd_getc ();
3122     }
3123
3124   /* read any numerical arguments */
3125   for (n = -1; ch < CSI_ICH; )
3126     {
3127       if (isdigit (ch))
3128         {
3129           if (n < 0)
3130             n = ch - '0';
3131           else
3132             n = n * 10 + ch - '0';
3133         }
3134       else if (ch == ';')
3135         {
3136           if (nargs < ESC_ARGS)
3137             arg[nargs++] = n;
3138           n = -1;
3139         }
3140       else if (IS_CONTROL (ch))
3141         process_nonprinting (ch);
3142
3143       ch = cmd_getc ();
3144     }
3145
3146   if (ch > CSI_7F)
3147     return;
3148
3149   if (nargs < ESC_ARGS)
3150     arg[nargs++] = n;
3151
3152   i = ch - CSI_ICH;
3153   ndef = get_byte_array_bit (csi_defaults, i);
3154   for (p = 0; p < nargs; p++)
3155     if (arg[p] == -1)
3156       arg[p] = ndef;
3157
3158 #ifdef DEBUG_CMD
3159   fprintf (stderr, "CSI ");
3160   for (p = 0; p < nargs; p++)
3161     fprintf (stderr, "%d%s", arg[p], p < nargs - 1 ? ";" : "");
3162   fprintf (stderr, "%c\n", ch);
3163 #endif
3164
3165   /*
3166    * private mode handling
3167    */
3168   if (priv)
3169     {
3170       switch (priv)
3171         {
3172           case '>':
3173             if (ch == CSI_DA)   /* secondary device attributes */
3174               tt_printf ("\033[>%d;%-.8s;0c", 'R', VSTRING);
3175             break;
3176           case '?':
3177             if (ch == 'h' || ch == 'l' || ch == 'r' || ch == 's' || ch == 't')
3178               process_terminal_mode (ch, priv, nargs, arg);
3179             break;
3180         }
3181       return;
3182     }
3183
3184   switch (ch)
3185     {
3186         /*
3187          * ISO/IEC 6429:1992 (E) CSI sequences (defaults in parentheses)
3188          */
3189 #ifdef PRINTPIPE
3190       case CSI_MC:              /* 8.3.83: (0) MEDIA COPY */
3191         switch (arg[0])
3192           {
3193             case 0:                     /* initiate transfer to primary aux device */
3194               scr_printscreen (0);
3195               break;
3196             case 5:                     /* start relay to primary aux device */
3197               process_print_pipe ();
3198               break;
3199           }
3200         break;
3201 #endif
3202
3203       case CSI_CUU:             /* 8.3.22: (1) CURSOR UP */
3204       case CSI_VPR:             /* 8.3.161: (1) LINE POSITION FORWARD */
3205         arg[0] = -arg[0];
3206         /* FALLTHROUGH */
3207       case CSI_CUD:             /* 8.3.19: (1) CURSOR DOWN */
3208       case CSI_VPB:             /* 8.3.160: (1) LINE POSITION BACKWARD */
3209         scr_gotorc (arg[0], 0, RELATIVE);
3210         break;
3211
3212       case CSI_CUB:             /* 8.3.18: (1) CURSOR LEFT */
3213       case CSI_HPB:             /* 8.3.59: (1) CHARACTER POSITION BACKWARD */
3214 #ifdef ISO6429
3215         arg[0] = -arg[0];
3216 #else                           /* emulate common DEC VTs */
3217         arg[0] = arg[0] ? -arg[0] : -1;
3218 #endif
3219         /* FALLTHROUGH */
3220       case CSI_CUF:             /* 8.3.20: (1) CURSOR RIGHT */
3221       case CSI_HPR:             /* 8.3.60: (1) CHARACTER POSITION FORWARD */
3222 #ifdef ISO6429
3223         scr_gotorc (0, arg[0], RELATIVE);
3224 #else                           /* emulate common DEC VTs */
3225         scr_gotorc (0, arg[0] ? arg[0] : 1, RELATIVE);
3226 #endif
3227         break;
3228
3229       case CSI_CPL:             /* 8.3.13: (1) CURSOR PRECEDING LINE */
3230         arg[0] = -arg[0];
3231         /* FALLTHROUGH */
3232       case CSI_CNL:             /* 8.3.12: (1) CURSOR NEXT LINE */
3233         scr_gotorc (arg[0], 0, R_RELATIVE);
3234         break;
3235
3236       case CSI_CHA:             /* 8.3.9: (1) CURSOR CHARACTER ABSOLUTE */
3237       case CSI_HPA:             /* 8.3.58: (1) CURSOR POSITION ABSOLUTE */
3238         scr_gotorc (0, arg[0] - 1, R_RELATIVE);
3239         break;
3240
3241       case CSI_VPA:             /* 8.3.159: (1) LINE POSITION ABSOLUTE */
3242         scr_gotorc (arg[0] - 1, 0, C_RELATIVE);
3243         break;
3244
3245       case CSI_CUP:             /* 8.3.21: (1,1) CURSOR POSITION */
3246       case CSI_HVP:             /* 8.3.64: (1,1) CHARACTER AND LINE POSITION */
3247         scr_gotorc (arg[0] - 1, nargs < 2 ? 0 : (arg[1] - 1), 0);
3248         break;
3249
3250       case CSI_CBT:             /* 8.3.7: (1) CURSOR BACKWARD TABULATION */
3251         arg[0] = -arg[0];
3252         /* FALLTHROUGH */
3253       case CSI_CHT:             /* 8.3.10: (1) CURSOR FORWARD TABULATION */
3254         scr_tab (arg[0]);
3255         break;
3256
3257       case CSI_ED:              /* 8.3.40: (0) ERASE IN PAGE */
3258         scr_erase_screen (arg[0]);
3259         break;
3260
3261       case CSI_EL:              /* 8.3.42: (0) ERASE IN LINE */
3262         scr_erase_line (arg[0]);
3263         break;
3264
3265       case CSI_ICH:             /* 8.3.65: (1) INSERT CHARACTER */
3266         scr_insdel_chars (arg[0], INSERT);
3267         break;
3268
3269       case CSI_IL:              /* 8.3.68: (1) INSERT LINE */
3270         scr_insdel_lines (arg[0], INSERT);
3271         break;
3272
3273       case CSI_DL:              /* 8.3.33: (1) DELETE LINE */
3274         scr_insdel_lines (arg[0], DELETE);
3275         break;
3276
3277       case CSI_ECH:             /* 8.3.39: (1) ERASE CHARACTER */
3278         scr_insdel_chars (arg[0], ERASE);
3279         break;
3280
3281       case CSI_DCH:             /* 8.3.26: (1) DELETE CHARACTER */
3282         scr_insdel_chars (arg[0], DELETE);
3283         break;
3284
3285       case CSI_SD:              /* 8.3.114: (1) SCROLL DOWN */
3286         arg[0] = -arg[0];
3287         /* FALLTHROUGH */
3288       case CSI_SU:              /* 8.3.148: (1) SCROLL UP */
3289         scr_scroll_text (screen.tscroll, screen.bscroll, arg[0], 0);
3290         break;
3291
3292       case CSI_DA:              /* 8.3.24: (0) DEVICE ATTRIBUTES */
3293         tt_write ((const unsigned char *)VT100_ANS,
3294                   (unsigned int) (sizeof (VT100_ANS) - 1));
3295         break;
3296
3297       case CSI_SGR:             /* 8.3.118: (0) SELECT GRAPHIC RENDITION */
3298         process_sgr_mode (nargs, arg);
3299         break;
3300
3301       case CSI_DSR:             /* 8.3.36: (0) DEVICE STATUS REPORT */
3302         switch (arg[0])
3303           {
3304             case 5:                     /* DSR requested */
3305               tt_printf ("\033[0n");
3306               break;
3307             case 6:                     /* CPR requested */
3308               scr_report_position ();
3309               break;
3310             case 7:                     /* unofficial extension */
3311               if (options & Opt_insecure)
3312                 tt_printf ("%-.250s\012", rs[Rs_display_name]);
3313               break;
3314             case 8:                     /* unofficial extension */
3315               process_xterm_seq (XTerm_title, RESNAME "-" VERSION, CHAR_ST);
3316               break;
3317           }
3318         break;
3319
3320       case CSI_TBC:             /* 8.3.155: (0) TABULATION CLEAR */
3321         switch (arg[0])
3322           {
3323             case 0:                     /* char tab stop cleared at active position */
3324               scr_set_tab (0);
3325               break;
3326               /* case 1: */             /* line tab stop cleared in active line */
3327               /* case 2: */             /* char tab stops cleared in active line */
3328             case 3:                     /* all char tab stops are cleared */
3329               /* case 4: */             /* all line tab stops are cleared */
3330             case 5:                     /* all tab stops are cleared */
3331               scr_set_tab (-1);
3332               break;
3333           }
3334         break;
3335
3336       case CSI_CTC:             /* 8.3.17: (0) CURSOR TABULATION CONTROL */
3337         switch (arg[0])
3338           {
3339             case 0:                     /* char tab stop set at active position */
3340               scr_set_tab (1);
3341               break;            /* = ESC H */
3342               /* case 1: */             /* line tab stop set at active line */
3343             case 2:                     /* char tab stop cleared at active position */
3344               scr_set_tab (0);
3345               break;            /* = ESC [ 0 g */
3346               /* case 3: */             /* line tab stop cleared at active line */
3347               /* case 4: */             /* char tab stops cleared at active line */
3348             case 5:                     /* all char tab stops are cleared */
3349               scr_set_tab (-1);
3350               break;            /* = ESC [ 3 g */
3351               /* case 6: */             /* all line tab stops are cleared */
3352           }
3353         break;
3354
3355       case CSI_RM:              /* 8.3.107: RESET MODE */
3356         if (arg[0] == 4)
3357           scr_insert_mode (0);
3358         else if (arg[0] == 20)
3359           priv_modes &= ~PrivMode_LFNL;
3360         break;
3361
3362       case CSI_SM:              /* 8.3.126: SET MODE */
3363         if (arg[0] == 4)
3364           scr_insert_mode (1);
3365         else if (arg[0] == 20)
3366           priv_modes |= PrivMode_LFNL;
3367         break;
3368
3369         /*
3370          * PRIVATE USE beyond this point.  All CSI_7? sequences here
3371          */
3372       case CSI_72:              /* DECSTBM: set top and bottom margins */
3373         if (nargs == 1)
3374           scr_scroll_region (arg[0] - 1, MAX_ROWS - 1);
3375         else if (nargs == 0 || arg[0] >= arg[1])
3376           scr_scroll_region (0, MAX_ROWS - 1);
3377         else
3378           scr_scroll_region (arg[0] - 1, arg[1] - 1);
3379         break;
3380
3381       case CSI_73:
3382         scr_cursor (SAVE);
3383         break;
3384       case CSI_75:
3385         scr_cursor (RESTORE);
3386         break;
3387
3388 #if ENABLE_FRILLS
3389       case CSI_74:
3390         process_window_ops (arg, nargs);
3391         break;
3392 #endif
3393
3394       case CSI_78:              /* DECREQTPARM */
3395         if (arg[0] == 0 || arg[0] == 1)
3396           tt_printf ("\033[%d;1;1;128;128;1;0x", arg[0] + 2);
3397         break;
3398
3399       default:
3400         break;
3401     }
3402 }
3403 /*}}} */
3404
3405 #if ENABLE_FRILLS
3406 /* ARGSUSED */
3407 void
3408 rxvt_term::process_window_ops (const int *args, unsigned int nargs)
3409 {
3410   int x, y;
3411   XWindowAttributes wattr;
3412   Window wdummy;
3413
3414   if (nargs == 0)
3415     return;
3416
3417   switch (args[0])
3418     {
3419       /*
3420        * commands
3421        */
3422       case 1:                   /* deiconify window */
3423         XMapWindow (display->display, TermWin.parent[0]);
3424         break;
3425       case 2:                   /* iconify window */
3426         XIconifyWindow (display->display, TermWin.parent[0],
3427                        DefaultScreen (display->display));
3428         break;
3429       case 3:                   /* set position (pixels) */
3430         XMoveWindow (display->display, TermWin.parent[0], args[1], args[2]);
3431         break;
3432       case 4:                   /* set size (pixels) */
3433         set_widthheight ((unsigned int)args[2], (unsigned int)args[1]);
3434         break;
3435       case 5:                   /* raise window */
3436         XRaiseWindow (display->display, TermWin.parent[0]);
3437         break;
3438       case 6:                   /* lower window */
3439         XLowerWindow (display->display, TermWin.parent[0]);
3440         break;
3441       case 7:                   /* refresh window */
3442         scr_touch (true);
3443         break;
3444       case 8:                   /* set size (chars) */
3445         set_widthheight ((unsigned int) (args[2] * TermWin.fwidth),
3446                          (unsigned int) (args[1] * TermWin.fheight));
3447         break;
3448
3449       //case 9: NYI, TODO, restore maximized window or maximize window
3450       default:
3451         if (args[0] >= 24)      /* set height (chars) */
3452           set_widthheight ((unsigned int)TermWin.width,
3453                            (unsigned int) (args[1] * TermWin.fheight));
3454         break;
3455
3456
3457       /*
3458        * reports - some output format copied from XTerm
3459        */
3460       case 11:                  /* report window state */
3461         XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
3462         tt_printf ("\033[%dt", wattr.map_state == IsViewable ? 1 : 2);
3463         break;
3464       case 13:                  /* report window position */
3465         XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
3466         XTranslateCoordinates (display->display, TermWin.parent[0], wattr.root,
3467                                -wattr.border_width, -wattr.border_width,
3468                                &x, &y, &wdummy);
3469         tt_printf ("\033[3;%d;%dt", x, y);
3470         break;
3471       case 14:                  /* report window size (pixels) */
3472         XGetWindowAttributes (display->display, TermWin.parent[0], &wattr);
3473         tt_printf ("\033[4;%d;%dt", wattr.height, wattr.width);
3474         break;
3475       case 18:                  /* report text area size (chars) */
3476         tt_printf ("\033[8;%d;%dt", TermWin.nrow, TermWin.ncol);
3477         break;
3478       case 19:                  /* report window size (chars) */
3479         tt_printf ("\033[9;%d;%dt", TermWin.nrow, TermWin.ncol);
3480         break;
3481       case 20:                  /* report icon label */
3482         {
3483           char *s;
3484           XGetIconName (display->display, TermWin.parent[0], &s);
3485           tt_printf ("\033]L%-.250s\234", (options & Opt_insecure) && s ? s : "");      /* 8bit ST */
3486           XFree (s);
3487         }
3488         break;
3489       case 21:                  /* report window title */
3490         {
3491           char *s;
3492           XFetchName (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     }
3498 }
3499 #endif
3500
3501 /*----------------------------------------------------------------------*/
3502 /*
3503  * get input up until STRING TERMINATOR (or BEL)
3504  * ends_how is terminator used. returned input must be free()'d
3505  */
3506 unsigned char *
3507 rxvt_term::get_to_st (unicode_t &ends_how)
3508 {
3509   unicode_t seen_esc = 0, ch;
3510   unsigned int n = 0;
3511   unsigned char *s;
3512   unsigned char string[STRING_MAX];
3513
3514   while ((ch = cmd_getc ()) != NOCHAR)
3515     {
3516       if (seen_esc)
3517         {
3518           if (ch == 0x5c)       /* 7bit ST */
3519             break;
3520           else
3521             return NULL;
3522         }
3523       else if (ch == C0_ESC)
3524         {
3525           seen_esc = 1;
3526           continue;
3527         }
3528       else if (ch == C0_BEL || ch == CHAR_ST)
3529         break;
3530       else if (ch < 0x20)
3531         return NULL;    /* other control character - exit */
3532
3533       seen_esc = 0;
3534
3535       if (n >= sizeof (string) - 1)
3536         // stop at some sane length
3537         return NULL;
3538
3539       if (ch == C0_SYN)
3540         string[n++] = cmd_get8 ();
3541       else
3542         string[n++] = ch;
3543     }
3544
3545   string[n++] = '\0';
3546
3547   if ((s = (unsigned char *)rxvt_malloc (n)) == NULL)
3548     return NULL;
3549
3550   ends_how = (ch == 0x5c ? C0_ESC : ch);
3551   strncpy (s, string, n);
3552   return s;
3553 }
3554
3555 /*----------------------------------------------------------------------*/
3556 /*
3557  * process DEVICE CONTROL STRING `ESC P ... (ST|BEL)' or `0x90 ... (ST|BEL)'
3558  */
3559 void
3560 rxvt_term::process_dcs_seq ()
3561 {
3562   unsigned char *s;
3563   unicode_t eh;
3564
3565   /*
3566    * Not handled yet
3567    */
3568   s = get_to_st (eh);
3569   if (s)
3570     free (s);
3571
3572   return;
3573 }
3574
3575 /*----------------------------------------------------------------------*/
3576 /*
3577  * process OPERATING SYSTEM COMMAND sequence `ESC ] Ps ; Pt (ST|BEL)'
3578  */
3579 void
3580 rxvt_term::process_osc_seq ()
3581 {
3582   unicode_t ch, eh;
3583   int arg;
3584
3585   ch = cmd_getc ();
3586   for (arg = 0; isdigit (ch); ch = cmd_getc ())
3587     arg = arg * 10 + (ch - '0');
3588
3589   if (ch == ';')
3590     {
3591       unsigned char *s = get_to_st (eh);
3592
3593       if (s)
3594         {
3595           process_xterm_seq (arg, (char *)s, eh);
3596           free (s);
3597         }
3598     }
3599 }
3600
3601 void
3602 rxvt_term::process_color_seq (int report, int color, const char *str, unsigned char resp)
3603 {
3604   if (str[0] == '?' && !str[1])
3605     {
3606       unsigned short r, g, b;
3607       pix_colors_focused[color].get (display, r, g, b);
3608       tt_printf ("\033]%d;rgb:%04x/%04x/%04x%c", report, r, g, b, resp);
3609     }
3610   else
3611     set_window_color (color, str);
3612 }
3613
3614 /*
3615  * XTerm escape sequences: ESC ] Ps;Pt (ST|BEL)
3616  *       0 = change iconName/title
3617  *       1 = change iconName
3618  *       2 = change title
3619  *       4 = change color
3620  *      10 = change fg color
3621  *      11 = change bg color
3622  *      12 = change text color
3623  *      13 = change mouse foreground color 
3624  *      17 = change highlight character colour
3625  *      18 = change bold character color
3626  *      19 = change underlined character color 
3627  *      46 = change logfile (not implemented)
3628  *      50 = change font
3629  *
3630  * rxvt extensions:
3631  *      20 = bg pixmap
3632  *      39 = change default fg color
3633  *      49 = change default bg color
3634  *      55 = dump scrollback buffer and all of screen
3635  *     701 = change locale
3636  *     702 = find font
3637  *     703 = menu
3638  */
3639 void
3640 rxvt_term::process_xterm_seq (int op, const char *str, unsigned char resp)
3641 {
3642   int changed = 0;
3643   int color;
3644   char *buf, *name;
3645   bool query = str[0] == '?' && !str[1];
3646   int saveop = op;
3647
3648   assert (str != NULL);
3649   switch (op)
3650     {
3651       case XTerm_name:
3652         set_title (str);
3653         /* FALLTHROUGH */
3654       case XTerm_iconName:
3655         set_icon_name (str);
3656         break;
3657       case XTerm_title:
3658         set_title (str);
3659         break;
3660       case XTerm_property:
3661         if (str[0] == '?')
3662           {
3663             Atom prop = XInternAtom (display->display, str + 1, True);
3664             Atom actual_type;
3665             int actual_format;
3666             unsigned long nitems;
3667             unsigned long bytes_after;
3668             unsigned char *value = 0;
3669             const char *str = "";
3670
3671             if (prop
3672                 && XGetWindowProperty (display->display, TermWin.parent[0],
3673                                        prop, 0, 1<<16, 0, AnyPropertyType,
3674                                        &actual_type, &actual_format,
3675                                        &nitems, &bytes_after, &value) == Success
3676                 && actual_type != None
3677                 && actual_format == 8)
3678               str = (const char *)(value);
3679
3680             tt_printf ("\033]%d;%s%c", XTerm_property, str, resp);
3681
3682             XFree (value);
3683           }
3684         else
3685           {
3686             char *eq = strchr (str, '='); // constness lost, but verified to be ok
3687
3688             if (eq)
3689               {
3690                 *eq = 0;
3691                 XChangeProperty (display->display, TermWin.parent[0],
3692                                  display->atom (str), XA_STRING, 8,
3693                                  PropModeReplace, (unsigned char *)eq + 1,
3694                                  strlen (eq + 1));
3695               }
3696             else
3697               XDeleteProperty (display->display, TermWin.parent[0],
3698                                display->atom (str));
3699           }
3700         break;
3701
3702       case XTerm_Color:
3703         for (buf = (char *)str; buf && *buf;)
3704           {
3705             if ((name = strchr (buf, ';')) == NULL)
3706               break;
3707
3708             *name++ = '\0';
3709             color = atoi (buf);
3710
3711             if (color < 0 || color >= TOTAL_COLORS)
3712               break;
3713
3714             if ((buf = strchr (name, ';')) != NULL)
3715               *buf++ = '\0';
3716
3717             if (name[0] == '?' && !name[1])
3718               {
3719                 unsigned short r, g, b;
3720                 pix_colors_focused[color + minCOLOR].get (display, r, g, b);
3721                 tt_printf ("\033]%d;%d;rgb:%04x/%04x/%04x%c", XTerm_Color, color, r, g, b, resp);
3722               }
3723             else
3724               set_window_color (color + minCOLOR, name);
3725           }
3726         break;
3727       case XTerm_Color00:
3728         process_color_seq (XTerm_Color00, Color_fg, str, resp);
3729         break;
3730       case XTerm_Color01:
3731         process_color_seq (XTerm_Color00, Color_bg, str, resp);
3732         break;
3733 #ifndef NO_CURSORCOLOR
3734       case XTerm_Color_cursor:
3735         process_color_seq (XTerm_Color_cursor, Color_cursor, str, resp);
3736         break;
3737 #endif
3738       case XTerm_Color_pointer_fg:
3739         process_color_seq (XTerm_Color_pointer_fg, Color_pointer_fg, str, resp);
3740         break;
3741       case XTerm_Color_pointer_bg:
3742         process_color_seq (XTerm_Color_pointer_bg, Color_pointer_bg, str, resp);
3743         break;
3744 #ifndef NO_BOLD_UNDERLINE_REVERSE
3745       case XTerm_Color_BD:
3746         process_color_seq (XTerm_Color_BD, Color_BD, str, resp);
3747         break;
3748       case XTerm_Color_IT:
3749         process_color_seq (XTerm_Color_IT, Color_IT, str, resp);
3750         break;
3751       case XTerm_Color_UL:
3752         process_color_seq (XTerm_Color_UL, Color_UL, str, resp);
3753         break;
3754       case XTerm_Color_RV:
3755         process_color_seq (XTerm_Color_RV, Color_RV, str, resp);
3756         break;
3757 #endif
3758 #if TRANSPARENT || TINTING
3759       case XTerm_Color_tint:
3760         process_color_seq (XTerm_Color_tint, Color_tint, str, resp);
3761         check_our_parents ();
3762         if (am_transparent)
3763           want_full_refresh = want_refresh = 1;
3764         break;
3765 #endif
3766
3767       case XTerm_Pixmap:
3768         if (*str != ';')
3769           {
3770 #if XPM_BACKGROUND
3771             scale_pixmap ("");  /* reset to default scaling */
3772             set_bgPixmap (str); /* change pixmap */
3773 #endif
3774             scr_touch (true);
3775           }
3776         while ((str = strchr (str, ';')) != NULL)
3777           {
3778             str++;
3779 #if XPM_BACKGROUND
3780             changed += scale_pixmap (str);
3781 #endif
3782
3783           }
3784         if (changed)
3785           {
3786 #ifdef XPM_BACKGROUND
3787             resize_pixmap ();
3788 #endif
3789             scr_touch (true);
3790           }
3791         break;
3792
3793       case XTerm_restoreFG:
3794         set_window_color (Color_fg, str);
3795         break;
3796       case XTerm_restoreBG:
3797         set_window_color (Color_bg, str);
3798         break;
3799
3800       case XTerm_logfile:
3801         // TODO, when secure mode?
3802         break;
3803
3804       case XTerm_font:
3805         op = URxvt_font;
3806       case URxvt_font:
3807 #if ENABLE_STYLES
3808       case URxvt_boldFont:
3809       case URxvt_italicFont:
3810       case URxvt_boldItalicFont:
3811 #endif
3812         if (query)
3813           tt_printf ("\33]%d;%-.250s%c", saveop,
3814                      (options & Opt_insecure) && TermWin.fontset[op - URxvt_font]->fontdesc
3815                        ? TermWin.fontset[op - URxvt_font]->fontdesc : "",
3816                      resp);
3817         else
3818           {
3819             const char *&res = rs[Rs_font + (op - URxvt_font)];
3820
3821             res = strdup (str);
3822             allocated.push_back ((void *)res);
3823             set_fonts ();
3824           }
3825         break;
3826
3827 #if ENABLE_FRILLS
3828       case XTerm_locale:
3829         if (query)
3830           tt_printf ("\33]%d;%-.250s%c", XTerm_locale, (options & Opt_insecure) ? locale : "", resp);
3831         else
3832           {
3833             set_locale (str);
3834 # ifdef USE_XIM
3835             im_cb ();
3836 # endif
3837           }
3838         break;
3839 #endif
3840
3841 #ifdef MENUBAR
3842      case XTerm_Menu:
3843        if (options & Opt_insecure)
3844          menubar_dispatch (const_cast<char *>(str)); // casting away constness is checked
3845        break;
3846 #endif
3847 #if 0
3848       case XTerm_dumpscreen:    /* no error notices */
3849         {
3850           int fd;
3851           if ((fd = open (str, O_RDWR | O_CREAT | O_EXCL, 0600)) >= 0)
3852             {
3853               scr_dump (fd);
3854               close (fd);
3855             }
3856         }
3857         break;
3858 #endif
3859     }
3860 }
3861 /*----------------------------------------------------------------------*/
3862
3863 /*{{{ process DEC private mode sequences `ESC [ ? Ps mode' */
3864 /*
3865  * mode can only have the following values:
3866  *      'l' = low
3867  *      'h' = high
3868  *      's' = save
3869  *      'r' = restore
3870  *      't' = toggle
3871  * so no need for fancy checking
3872  */
3873 int
3874 rxvt_term::privcases (int mode, unsigned long bit)
3875 {
3876   int state;
3877
3878   if (mode == 's')
3879     {
3880       SavedModes |= (priv_modes & bit);
3881       return -1;
3882     }
3883   else
3884     {
3885       if (mode == 'r')
3886         state = (SavedModes & bit) ? 1 : 0;     /* no overlapping */
3887       else
3888         state = (mode == 't') ? ! (priv_modes & bit) : mode;
3889       PrivMode (state, bit);
3890     }
3891
3892   return state;
3893 }
3894
3895 /* we're not using priv _yet_ */
3896 void
3897 rxvt_term::process_terminal_mode (int mode, int priv __attribute__ ((unused)), unsigned int nargs, const int *arg)
3898 {
3899   unsigned int i, j;
3900   int state;
3901
3902   static const struct
3903     {
3904       const int       argval;
3905       const unsigned long bit;
3906     }
3907
3908   argtopriv[] = {
3909                   { 1, PrivMode_aplCUR },
3910                   { 2, PrivMode_vt52 },
3911                   { 3, PrivMode_132 },
3912                   { 4, PrivMode_smoothScroll },
3913                   { 5, PrivMode_rVideo },
3914                   { 6, PrivMode_relOrigin },
3915                   { 7, PrivMode_Autowrap },
3916                  // 8, bi-directional support mode
3917                   { 9, PrivMode_MouseX10 },
3918 #ifdef menuBar_esc
3919                   { menuBar_esc, PrivMode_menuBar },
3920 #endif
3921                  // 18, 19 printing-related
3922                   { 25, PrivMode_VisibleCursor },
3923 #ifdef scrollBar_esc
3924                   { scrollBar_esc, PrivMode_scrollBar },
3925 #endif
3926                   { 35, PrivMode_ShiftKeys }, // rxvt extension
3927                   { 40, PrivMode_132OK },
3928                  // 41 xterm more fixes NYI
3929                  // 45 margin bell NYI
3930                  // 46 start logging
3931                   { 47, PrivMode_Screen },
3932                   { 66, PrivMode_aplKP },
3933 #ifndef NO_BACKSPACE_KEY
3934                   { 67, PrivMode_BackSpace },
3935 #endif
3936                   { 1000, PrivMode_MouseX11 },
3937                  // 1001 Use Hilite Mouse Tracking. NYI, TODO
3938                  // 1002 Use Cell Motion Mouse Tracking. NYI, TODO
3939                  // 1003 Use All Motion Mouse Tracking. NYI, TODO
3940                   { 1010, PrivMode_TtyOutputInh }, // rxvt extension
3941                   { 1011, PrivMode_Keypress }, // rxvt extension
3942                  // 1035 enable modifiers for alt, numlock NYI
3943                  // 1036 send ESC for meta keys NYI
3944                  // 1037 send DEL for keypad delete NYI
3945                   { 1047, PrivMode_Screen },
3946                  // 1048 save and restore cursor
3947                   { 1049, PrivMode_Screen }, /* xterm extension, clear screen on ti rather than te */
3948                  // 1051, 1052, 1060, 1061 keyboard emulation NYI
3949                 };
3950
3951   if (nargs == 0)
3952     return;
3953
3954   /* make lo/hi boolean */
3955   if (mode == 'l')
3956     mode = 0;           /* reset */
3957   else if (mode == 'h')
3958     mode = 1;           /* set */
3959
3960   for (i = 0; i < nargs; i++)
3961     {
3962       state = -1;
3963
3964       /* basic handling */
3965       for (j = 0; j < (sizeof (argtopriv)/sizeof (argtopriv[0])); j++)
3966         if (argtopriv[j].argval == arg[i])
3967           {
3968             state = privcases (mode, argtopriv[j].bit);
3969             break;
3970           }
3971
3972       /* extra handling for values with state unkept  */
3973       switch (arg[i])
3974         {
3975           case 1048:            /* alternative cursor save */
3976           case 1049:
3977             if (options & Opt_secondaryScreen)
3978               if (mode == 0)
3979                 scr_cursor (RESTORE);
3980               else if (mode == 1)
3981                 scr_cursor (SAVE);
3982             break;
3983         }
3984
3985       if (state >= 0)
3986         /* extra handling for values with valid 0 or 1 state */
3987         switch (arg[i])
3988           {
3989               /* case 1:        - application cursor keys */
3990             case 2:                     /* VT52 mode */
3991               /* oddball mode.  should be set regardless of set/reset
3992                * parameter.  Return from VT52 mode with an ESC < from
3993                * within VT52 mode
3994                */
3995               PrivMode (1, PrivMode_vt52);
3996               break;
3997             case 3:                     /* 80/132 */
3998               if (priv_modes & PrivMode_132OK)
3999                 set_widthheight (((state ? 132 : 80) * TermWin.fwidth), TermWin.height);
4000               break;
4001             case 4:                     /* smooth scrolling */
4002               if (state)
4003                 options &= ~Opt_jumpScroll;
4004               else
4005                 options |= Opt_jumpScroll;
4006               break;
4007             case 5:                     /* reverse video */
4008               scr_rvideo_mode (state);
4009               break;
4010             case 6:                     /* relative/absolute origins  */
4011               scr_relative_origin (state);
4012               break;
4013             case 7:                     /* autowrap */
4014               scr_autowrap (state);
4015               break;
4016             /* case 8:  - auto repeat, can't do on a per window basis */
4017             case 9:                     /* X10 mouse reporting */
4018               if (state)                /* orthogonal */
4019                 priv_modes &= ~PrivMode_MouseX11;
4020               break;
4021 #ifdef menuBar_esc
4022             case menuBar_esc:
4023 #ifdef MENUBAR
4024               map_menuBar (state);
4025 #endif
4026               break;
4027 #endif
4028 #ifdef scrollBar_esc
4029             case scrollBar_esc:
4030               if (scrollbar_mapping (state))
4031                 {
4032                   resize_all_windows (0, 0, 0);
4033                   scr_touch (true);
4034                 }
4035               break;
4036 #endif
4037             case 25:            /* visible/invisible cursor */
4038               scr_cursor_visible (state);
4039               break;
4040             /* case 35: - shift keys */
4041             /* case 40: - 80 <--> 132 mode */
4042             case 47:            /* secondary screen */
4043               scr_change_screen (state);
4044               break;
4045             /* case 66: - application key pad */
4046             /* case 67: - backspace key */
4047             case 1000:          /* X11 mouse reporting */
4048               if (state)                /* orthogonal */
4049                 priv_modes &= ~PrivMode_MouseX10;
4050               break;
4051 #if 0
4052             case 1001:
4053               break;            /* X11 mouse highlighting */
4054 #endif
4055             case 1010:          /* scroll to bottom on TTY output inhibit */
4056               if (state)
4057                 options &= ~Opt_scrollTtyOutput;
4058               else
4059                 options |= Opt_scrollTtyOutput;
4060               break;
4061             case 1011:          /* scroll to bottom on key press */
4062               if (state)
4063                 options |= Opt_scrollTtyKeypress;
4064               else
4065                 options &= ~Opt_scrollTtyKeypress;
4066               break;
4067             case 1047:          /* secondary screen w/ clearing last */
4068               if (options & Opt_secondaryScreen)
4069                 if (current_screen != PRIMARY)
4070                   scr_erase_screen (2);
4071               scr_change_screen (state);
4072               break;
4073             case 1049:          /* secondary screen w/ clearing first */
4074               scr_change_screen (state);
4075               if (options & Opt_secondaryScreen)
4076                 if (current_screen != PRIMARY)
4077                   scr_erase_screen (2);
4078               break;
4079             default:
4080               break;
4081           }
4082     }
4083 }
4084 /*}}} */
4085
4086 /*{{{ process sgr sequences */
4087 void
4088 rxvt_term::process_sgr_mode (unsigned int nargs, const int *arg)
4089 {
4090   unsigned int i;
4091   short rendset;
4092   int rendstyle;
4093
4094   if (nargs == 0)
4095     {
4096       scr_rendition (0, ~RS_None);
4097       return;
4098     }
4099
4100   for (i = 0; i < nargs; i++)
4101     {
4102       rendset = -1;
4103       switch (arg[i])
4104         {
4105           case 0:
4106             rendset = 0, rendstyle = ~RS_None;
4107             break;
4108           case 1:
4109             rendset = 1, rendstyle = RS_Bold;
4110             break;
4111           //case 2: // low intensity
4112           case 3:
4113             rendset = 1, rendstyle = RS_Italic;
4114             break;
4115           case 4:
4116             rendset = 1, rendstyle = RS_Uline;
4117             break;
4118           case 5: // slowly blinking
4119           case 6: // rapidly blinking
4120             rendset = 1, rendstyle = RS_Blink;
4121             break;
4122           //case 6: // scoansi light background
4123           case 7:
4124             rendset = 1, rendstyle = RS_RVid;
4125             break;
4126           case 8:
4127             // invisible. NYI
4128             break;
4129           //case 9: // crossed out
4130           //case 10: // scoansi acs off, primary font
4131           //case 11: // scoansi acs on, first alt font
4132           //case 12: // scoansi acs on, |0x80, second alt font
4133           //...
4134           //case 19: // ninth alt font
4135           //case 20: // gothic
4136           case 21: // disable bold, faint, sometimes doubly underlined (iso 8613)
4137             rendset = 0, rendstyle = RS_Bold;
4138             break;
4139           case 22: // normal intensity
4140             rendset = 0, rendstyle = RS_Bold;
4141             break;
4142           case 23: // disable italic
4143             rendset = 0, rendstyle = RS_Italic;
4144             break;
4145           case 24:
4146             rendset = 0, rendstyle = RS_Uline;
4147             break;
4148           case 25:
4149             rendset = 0, rendstyle = RS_Blink;
4150             break;
4151           case 26: // variable spacing (iso 8613)
4152             rendset = 0, rendstyle = RS_Blink;
4153             break;
4154           case 27:
4155             rendset = 0, rendstyle = RS_RVid;
4156             break;
4157           //case 28: // visible. NYI
4158           //case 29: // not crossed-out
4159         }
4160
4161       if (rendset != -1)
4162         {
4163           scr_rendition (rendset, rendstyle);
4164           continue;             /* for (;i;) */
4165         }
4166
4167       switch (arg[i])
4168         {
4169           case 30:
4170           case 31:              /* set fg color */
4171           case 32:
4172           case 33:
4173           case 34:
4174           case 35:
4175           case 36:
4176           case 37:
4177             scr_color ((unsigned int) (minCOLOR + (arg[i] - 30)), Color_fg);
4178             break;
4179           case 38: // set fg color, ISO 8613-6
4180             if (nargs > i + 2 && arg[i + 1] == 5)
4181               {
4182                 scr_color ((unsigned int) (minCOLOR + arg[i + 2]), Color_fg);
4183                 i += 2;
4184               }
4185             break;
4186           case 39:              /* default fg */
4187             scr_color (Color_fg, Color_fg);
4188             break;
4189
4190           case 40:
4191           case 41:              /* set bg color */
4192           case 42:
4193           case 43:
4194           case 44:
4195           case 45:
4196           case 46:
4197           case 47:
4198             scr_color ((unsigned int) (minCOLOR + (arg[i] - 40)), Color_bg);
4199             break;
4200           case 48: // set bg color, ISO 8613-6
4201             if (nargs > i + 2 && arg[i + 1] == 5)
4202               {
4203                 scr_color ((unsigned int) (minCOLOR + arg[i + 2]), Color_bg);
4204                 i += 2;
4205               }
4206             break;
4207           case 49:              /* default bg */
4208             scr_color (Color_bg, Color_bg);
4209             break;
4210
4211           //case 50: // not variable spacing
4212
4213 #ifndef NO_BRIGHTCOLOR
4214           case 90:
4215           case 91:              /* set bright fg color */
4216           case 92:
4217           case 93:
4218           case 94:
4219           case 95:
4220           case 96:
4221           case 97:
4222             scr_color ((unsigned int) (minBrightCOLOR + (arg[i] - 90)), Color_fg);
4223             break;
4224           case 100:
4225           case 101:             /* set bright bg color */
4226           case 102:
4227           case 103:
4228           case 104:
4229           case 105:
4230           case 106:
4231           case 107:
4232             scr_color ((unsigned int) (minBrightCOLOR + (arg[i] - 100)), Color_bg);
4233             break;
4234 #endif
4235
4236         }
4237     }
4238 }
4239 /*}}} */
4240
4241 /*{{{ (do not) process Rob Nation's own graphics mode sequences */
4242 void
4243 rxvt_term::process_graphics ()
4244 {
4245   unicode_t ch, cmd = cmd_getc ();
4246
4247   if (cmd == 'Q')
4248     {           /* query graphics */
4249       tt_printf ("\033G0\012"); /* no graphics */
4250       return;
4251     }
4252   /* swallow other graphics sequences until terminating ':' */
4253   do
4254     ch = cmd_getc ();
4255   while (ch != ':');
4256 }
4257 /*}}} */
4258
4259 /* ------------------------------------------------------------------------- */
4260
4261 /*
4262  * Send printf () formatted output to the command.
4263  * Only use for small amounts of data.
4264  */
4265 void
4266 rxvt_term::tt_printf (const char *fmt,...)
4267 {
4268   va_list arg_ptr;
4269   unsigned char buf[256];
4270
4271   va_start (arg_ptr, fmt);
4272   vsnprintf ((char *)buf, 256, fmt, arg_ptr);
4273   va_end (arg_ptr);
4274   tt_write (buf, strlen (buf));
4275 }
4276
4277 /* ---------------------------------------------------------------------- */
4278 /* Write data to the pty as typed by the user, pasted with the mouse,
4279  * or generated by us in response to a query ESC sequence.
4280  */
4281 void
4282 rxvt_term::tt_write (const unsigned char *data, unsigned int len)
4283 {
4284   const unsigned int MAX_PTY_WRITE = 255; // minimum MAX_INPUT
4285
4286   if (len)
4287     {
4288       if (v_buflen == 0)
4289         {
4290           ssize_t written = write (pty.pty, data, min (len, MAX_PTY_WRITE));
4291
4292           if ((unsigned int)written == len)
4293             return;
4294
4295           data += written;
4296           len -= written;
4297         }
4298
4299
4300       v_buffer = (unsigned char *)realloc (v_buffer, v_buflen + len);
4301
4302       memcpy (v_buffer + v_buflen, data, len);
4303       v_buflen += len;
4304     }
4305
4306   for (;;)
4307     {
4308       int written = write (pty.pty, v_buffer, min (v_buflen, MAX_PTY_WRITE));
4309
4310       if (written > 0)
4311         {
4312           v_buflen -= written;
4313
4314           if (v_buflen == 0)
4315             {
4316               free (v_buffer);
4317               v_buffer = 0;
4318               v_buflen = 0;
4319
4320               pty_ev.set (EVENT_READ);
4321               return;
4322             }
4323
4324           memmove (v_buffer, v_buffer + written, v_buflen);
4325         }
4326       else if (written != -1 || (errno != EAGAIN && errno != EINTR))
4327         // original code just ignores this...
4328         destroy ();
4329       else
4330         {
4331           pty_ev.set (EVENT_READ | EVENT_WRITE);
4332           return;
4333         }
4334     }
4335 }
4336
4337 /*----------------------- end-of-file (C source) -----------------------*/
4338