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