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