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