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