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