1 /*--------------------------------*-C-*---------------------------------*
2 * File: command.c
3 *----------------------------------------------------------------------*
4 * Copyright (C) 1997,1998 Oezguer Kesim <kesim@math.fu-berlin.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *----------------------------------------------------------------------*/
20 /*----------------------------------------------------------------------*
21 * Originally written:
22 * 1992 John Bovey, University of Kent at Canterbury <jdb@ukc.ac.uk>
23 * Modifications:
24 * 1994 Robert Nation <nation@rocket.sanders.lockheed.com>
25 * - extensive modifications
26 * 1995 Garrett D'Amore <garrett@netcom.com>
27 * - vt100 printing
28 * 1995 Steven Hirsch <hirsch@emba.uvm.edu>
29 * - X11 mouse report mode and support for DEC "private mode"
30 * save/restore functions.
31 * 1995 Jakub Jelinek <jj@gnu.ai.mit.edu>
32 * - key-related changes to handle Shift+function keys properly.
33 * 1997 MJ Olesen <olesen@me.queensu.ca>
34 * - extensive modifications
35 * 1997 Raul Garcia Garcia <rgg@tid.es>
36 * - modification and cleanups for Solaris 2.x and Linux 1.2.x
37 * 1997,1998 Oezguer Kesim <kesim@math.fu-berlin.de>
38 * 1998 Geoff Wing <gcw@pobox.com>
39 * 1998 Alfredo K. Kojima <kojima@windowmaker.org>
40 * 1999 Rafal Wierzbicki <rafal@mcss.mcmaster.ca>
41 * - support for Unix98 ptys with linux-2.2.x and glibc-2.1
42 *----------------------------------------------------------------------*/
43
44
45 #ifndef lint
46 static const char rcsid[] = "$Id: command.c,v 1.24 2006/06/26 18:01:20 sasha Exp $";
47 #endif
48
49 /*{{{ includes: */
50 #include "rxvt.h" /* NECESSARY */
51 #ifdef HAVE_AFTERSTEP
52 #include "libAfterStep/event.h"
53 #endif
54
55 #define TT_PRINTF_LIMIT 1024
56
57 #if defined(OFFIX_DND) || defined(TRANSPARENT)
58 # include <X11/Xatom.h>
59 #endif
60 #ifdef OFFIX_DND
61 # define DndFile 2
62 # define DndDir 5
63 # define DndLink 7
64 #endif
65
66 #if defined __GLIBC__ && __GLIBC__ >= 2
67 # if defined __GLIBC_MINOR__ && __GLIBC_MINOR__ >= 1
68 # define __lnx21__ 1
69 # endif
70 #endif
71
72 #include <X11/keysym.h>
73 #ifndef NO_XLOCALE
74 # if (XtSpecificationRelease < 6)
75 # define NO_XLOCALE
76 # endif
77 #endif /* NO_XLOCALE */
78
79 #ifndef NO_XSETLOCALE
80 # define X_LOCALE
81 # include <X11/Xlocale.h>
82 #else
83 # ifndef NO_SETLOCALE
84 # include <locale.h>
85 # endif
86 #endif /* NO_XLOCALE */
87
88 #ifdef TTY_GID_SUPPORT
89 # include <grp.h>
90 #endif
91
92 #if defined (__svr4__) || defined (__lnx21__)
93 # include <sys/resource.h> /* for struct rlimit */
94 # include <sys/stropts.h> /* for I_PUSH */
95 # define _NEW_TTY_CTRL /* to get proper defines in <termios.h> */
96 #endif
97
98 /*}}} */
99
100 static unsigned int ModMetaMask, ModNumLockMask;
101
102 /* pasting */
103 static char *v_buffer; /* pointer to physical buffer */
104 static char *v_bufstr = NULL; /* beginning of area to write */
105 static char *v_bufptr; /* end of area to write */
106 static char *v_bufend; /* end of physical buffer */
107
108 #define UPDATE_BACKGROUND_TIMEOUT_SEC 1
109 #define UPDATE_BACKGROUND_TIMEOUT_USEC 350000 /* third of a second delay */
110 static time_t last_update_background_request_sec = 0 ;
111 static time_t last_update_background_request_usec = 0 ;
112 static Bool first_background_update = True ;
113
114
115 static void
timer_get_time(time_t * sec,time_t * usec)116 timer_get_time (time_t * sec, time_t * usec)
117 {
118 struct timeval tv;
119
120 gettimeofday (&tv, NULL);
121 *sec = tv.tv_sec;
122 *usec = tv.tv_usec;
123 }
124
125
126 /* PROTO */
127 void
request_background_update()128 request_background_update()
129 {
130 Bool repetitive = (last_update_background_request_sec!=0);
131 timer_get_time (&last_update_background_request_sec, &last_update_background_request_usec );
132 if( first_background_update )
133 {
134 last_update_background_request_usec += 5000;
135 }else
136 last_update_background_request_usec += repetitive?UPDATE_BACKGROUND_TIMEOUT_USEC:UPDATE_BACKGROUND_TIMEOUT_USEC*2 ;
137 while( last_update_background_request_usec > 1000000 )
138 {
139 ++last_update_background_request_sec ;
140 last_update_background_request_usec -= 1000000 ;
141 }
142 #ifdef DEBUG_BACKGROUND_PMAP
143 fprintf( stderr, "!!! %s\n", __FUNCTION__);
144 #endif
145 }
146
147 /* PROTO */
148 void
cancel_background_update()149 cancel_background_update()
150 {
151 last_update_background_request_sec = 0;
152 #ifdef DEBUG_BACKGROUND_PMAP
153 fprintf( stderr, "!!! %s\n", __FUNCTION__);
154 #endif
155 }
156
157 /* PROTO */
158 void
set_background_updated()159 set_background_updated()
160 {
161 first_background_update = False ;
162 last_update_background_request_sec = 0;
163 #ifdef DEBUG_BACKGROUND_PMAP
164 fprintf( stderr, "!!! %s\n", __FUNCTION__);
165 #endif
166 }
167
168
169 void get_ourmods(void);
170 int pixmap_error_handler (Display * dpy, XErrorEvent * error);
171
172 /* #define DEBUG_TTYMODE */
173 /* #define DEBUG_CMD */
174
175 /*{{{ terminal mode defines: */
176 /* use the fastest baud-rate */
177 #ifdef B38400
178 # define BAUDRATE B38400
179 #else
180 # ifdef B19200
181 # define BAUDRATE B19200
182 # else
183 # define BAUDRATE B9600
184 # endif
185 #endif
186
187 /* Disable special character functions */
188 #ifdef _POSIX_VDISABLE
189 # define VDISABLE _POSIX_VDISABLE
190 #else
191 # define VDISABLE 255
192 #endif
193
194 /*----------------------------------------------------------------------*
195 * system default characters if defined and reasonable
196 */
197 #ifndef CINTR
198 # define CINTR '\003' /* ^C */
199 #endif
200 #ifndef CQUIT
201 # define CQUIT '\034' /* ^\ */
202 #endif
203 #ifndef CERASE
204 # ifdef linux
205 # define CERASE '\177' /* ^? */
206 # else
207 # define CERASE '\010' /* ^H */
208 # endif
209 #endif
210 #ifndef CKILL
211 # define CKILL '\025' /* ^U */
212 #endif
213 #ifndef CEOF
214 # define CEOF '\004' /* ^D */
215 #endif
216 #ifndef CSTART
217 # define CSTART '\021' /* ^Q */
218 #endif
219 #ifndef CSTOP
220 # define CSTOP '\023' /* ^S */
221 #endif
222 #ifndef CSUSP
223 # define CSUSP '\032' /* ^Z */
224 #endif
225 #ifndef CDSUSP
226 # define CDSUSP '\031' /* ^Y */
227 #endif
228 #ifndef CRPRNT
229 # define CRPRNT '\022' /* ^R */
230 #endif
231 #ifndef CFLUSH
232 # define CFLUSH '\017' /* ^O */
233 #endif
234 #ifndef CWERASE
235 # define CWERASE '\027' /* ^W */
236 #endif
237 #ifndef CLNEXT
238 # define CLNEXT '\026' /* ^V */
239 #endif
240
241 #ifndef VDISCRD
242 # ifdef VDISCARD
243 # define VDISCRD VDISCARD
244 # endif
245 #endif
246
247 #ifndef VWERSE
248 # ifdef VWERASE
249 # define VWERSE VWERASE
250 # endif
251 #endif
252 /*}}} */
253
254 /*{{{ defines: */
255
256 #ifdef USE_XIM
257 # define KBUFSZ 64 /* size of keyboard mapping buffer */
258 #else
259 # define KBUFSZ 8 /* size of keyboard mapping buffer */
260 #endif
261 #define STRING_MAX 512 /* max string size for process_xterm_seq() */
262 #define ESC_ARGS 32 /* max # of args for esc sequences */
263
264 /* a large REFRESH_PERIOD causes problems with `cat' */
265 #define REFRESH_PERIOD 1
266
267 #ifndef REFRESH_PERIOD
268 # define REFRESH_PERIOD 10
269 #endif
270
271 #ifndef MULTICLICK_TIME
272 # define MULTICLICK_TIME 500
273 #endif
274 #ifndef SCROLLBAR_INITIAL_DELAY
275 # ifdef NEXT_SCROLLER
276 # define SCROLLBAR_INITIAL_DELAY 20
277 # else
278 # define SCROLLBAR_INITIAL_DELAY 40
279 # endif
280 #endif
281 #ifndef SCROLLBAR_CONTINUOUS_DELAY
282 # define SCROLLBAR_CONTINUOUS_DELAY 2
283 #endif
284
285 /* time factor to slow down a `jumpy' mouse */
286 #define MOUSE_THRESHOLD 50
287 #define CONSOLE "/dev/console" /* console device */
288
289 /*
290 * key-strings: if only these keys were standardized <sigh>
291 */
292 #ifdef LINUX_KEYS
293 # define KS_HOME "\033[1~" /* Home == Find */
294 # define KS_END "\033[4~" /* End == Select */
295 #else
296 # define KS_HOME "\033[7~" /* Home */
297 # define KS_END "\033[8~" /* End */
298 #endif
299
300 /*
301 * ESC-Z processing:
302 *
303 * By stealing a sequence to which other xterms respond, and sending the
304 * same number of characters, but having a distinguishable sequence,
305 * we can avoid having a timeout (when not under an rxvt) for every login
306 * shell to auto-set its DISPLAY.
307 *
308 * This particular sequence is even explicitly stated as obsolete since
309 * about 1985, so only very old software is likely to be confused, a
310 * confusion which can likely be remedied through termcap or TERM. Frankly,
311 * I doubt anyone will even notice. We provide a #ifdef just in case they
312 * don't care about auto-display setting. Just in case the ancient
313 * software in question is broken enough to be case insensitive to the 'c'
314 * character in the answerback string, we make the distinguishing
315 * characteristic be capitalization of that character. The length of the
316 * two strings should be the same so that identical read(2) calls may be
317 * used.
318 */
319 #define VT100_ANS "\033[?1;2c" /* vt100 answerback */
320 #ifndef ESCZ_ANSWER
321 # define ESCZ_ANSWER VT100_ANS /* obsolete ANSI ESC[c */
322 #endif
323 /*}}} */
324
325 /*{{{ local variables */
326 static char *ptydev = NULL, *ttydev = NULL; /* pty/tty name */
327 static int cmd_fd = -1; /* file descriptor connected to the command */
328 static pid_t cmd_pid = -1; /* process id if child */
329 static int Xfd = -1; /* file descriptor of X server connection */
330 static unsigned int num_fds = 0; /* number of file descriptors being used */
331 static struct stat ttyfd_stat; /* original status of the tty we will use */
332
333 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
334 static int scroll_arrow_delay;
335 #endif
336
337 #ifdef META8_OPTION
338 static unsigned char meta_char = 033; /* Alt-key prefix */
339 #endif
340 static unsigned int ModXMask = Mod1Mask;
341
342 /* DEC private modes */
343 #define PrivMode_132 (1LU<<0)
344 #define PrivMode_132OK (1LU<<1)
345 #define PrivMode_rVideo (1LU<<2)
346 #define PrivMode_relOrigin (1LU<<3)
347 #define PrivMode_Screen (1LU<<4)
348 #define PrivMode_Autowrap (1LU<<5)
349 #define PrivMode_aplCUR (1LU<<6)
350 #define PrivMode_aplKP (1LU<<7)
351 #define PrivMode_HaveBackSpace (1LU<<8)
352 #define PrivMode_BackSpace (1LU<<9)
353 #define PrivMode_ShiftKeys (1LU<<10)
354 #define PrivMode_VisibleCursor (1LU<<11)
355 #define PrivMode_MouseX10 (1LU<<12)
356 #define PrivMode_MouseX11 (1LU<<13)
357 #define PrivMode_scrollBar (1LU<<14)
358 #define PrivMode_menuBar (1LU<<15)
359 #define PrivMode_TtyOutputInh (1LU<<16)
360 #define PrivMode_Keypress (1LU<<17)
361 /* too annoying to implement X11 highlight tracking */
362 /* #define PrivMode_MouseX11Track (1LU<<18) */
363
364 #define PrivMode_mouse_report (PrivMode_MouseX10|PrivMode_MouseX11)
365 #define PrivMode(test,bit) do { \
366 if (test) PrivateModes |= (bit); else PrivateModes &= ~(bit);} while (0)
367
368 #define PrivMode_Default \
369 (PrivMode_Autowrap|PrivMode_aplKP|PrivMode_ShiftKeys|PrivMode_VisibleCursor)
370
371 static unsigned long PrivateModes = PrivMode_Default;
372 static unsigned long SavedModes = PrivMode_Default;
373
374 #undef PrivMode_Default
375
376 static int refresh_count = 0, refresh_limit = 1,
377 refresh_type = SLOW_REFRESH;
378
379 static Atom wmDeleteWindow;
380
381 /* OffiX Dnd (drag 'n' drop) support */
382 #ifdef OFFIX_DND
383 static Atom DndProtocol, DndSelection;
384 #endif /* OFFIX_DND */
385
386 #ifdef USE_XIM
387 static XIC Input_Context; /* input context */
388 #else
389 #ifndef NO_XLOCALE
390 static char *rs_inputMethod = ""; /* XtNinputMethod */
391 static char *rs_preeditType = NULL; /* XtNpreeditType */
392 static XIC Input_Context; /* input context */
393 #endif /* NO_XLOCALE */
394 #endif /* USE_XIM */
395
396 /* command input buffering */
397 #ifndef BUFSIZ
398 # define BUFSIZ 4096
399 #endif
400 static unsigned char cmdbuf_base[BUFSIZ], *cmdbuf_ptr, *cmdbuf_endp;
401
402 /*}}} */
403
404 /*----------------------------------------------------------------------*/
405 /*}}} */
406
407 /*{{{ substitute system functions */
408 #ifndef _POSIX_VERSION
409 # if defined (__svr4__) || defined (__lnx21__)
410 /* PROTO */
411 int
getdtablesize(void)412 getdtablesize(void)
413 {
414 struct rlimit rlim;
415
416 getrlimit(RLIMIT_NOFILE, &rlim);
417 return rlim.rlim_cur;
418 }
419 # endif
420 #endif
421 /*}}} */
422
423 /*{{{ take care of suid/sgid super-user (root) privileges */
424 /* PROTO */
425 void
privileges(int mode)426 privileges(int mode)
427 {
428 #ifdef HAVE_SETEUID
429 static uid_t euid;
430 static gid_t egid;
431
432 switch (mode) {
433 case IGNORE:
434 /*
435 * change effective uid/gid - not real uid/gid - so we can switch
436 * back to root later, as required
437 */
438 seteuid(getuid());
439 setegid(getgid());
440 break;
441
442 case SAVE:
443 euid = geteuid();
444 egid = getegid();
445 break;
446
447 case RESTORE:
448 seteuid(euid);
449 setegid(egid);
450 break;
451 }
452 #else
453 # ifndef __CYGWIN32__
454 switch (mode) {
455 case IGNORE:
456 setuid(getuid());
457 setgid(getgid());
458 break;
459
460 case SAVE:
461 break;
462 case RESTORE:
463 break;
464 }
465 # endif
466 #endif
467 }
468 /*}}} */
469
470 /*{{{ signal handling, exit handler */
471 /*
472 * Catch a SIGCHLD signal and exit if the direct child has died
473 */
474 /* ARGSUSED */
475 /* PROTO */
476 RETSIGTYPE
Child_signal(int unused)477 Child_signal(int unused)
478 {
479 int pid, save_errno = errno;
480
481 do {
482 errno = 0;
483 }
484 while ((-1 == (pid = waitpid(-1, NULL, WNOHANG))) &&
485 (errno == EINTR));
486
487 if (pid == cmd_pid)
488 exit(EXIT_SUCCESS);
489 errno = save_errno;
490
491 signal(SIGCHLD, Child_signal);
492 }
493
494 /*
495 * Catch a fatal signal and tidy up before quitting
496 */
497 /* PROTO */
498 RETSIGTYPE
Exit_signal(int sig)499 Exit_signal(int sig)
500 {
501 #ifdef DEBUG_CMD
502 print_error("signal %d", sig);
503 #endif
504 signal(sig, SIG_DFL);
505
506 #ifdef UTMP_SUPPORT
507 privileges(RESTORE);
508 cleanutent();
509 privileges(IGNORE);
510 #endif
511
512 kill(getpid(), sig);
513 }
514
515 /*
516 * Exit gracefully, clearing the utmp entry and restoring tty attributes
517 * TODO: this should free up any known resources if we can
518 */
519 /* PROTO */
520 void
clean_exit(void)521 clean_exit(void)
522 {
523 #ifdef DEBUG_CMD
524 fprintf(stderr, "Restoring \"%s\" to mode %03o, uid %d, gid %d\n",
525 ttydev, ttyfd_stat.st_mode, ttyfd_stat.st_uid, ttyfd_stat.st_gid);
526 #endif
527 scr_release();
528 privileges(RESTORE);
529 #ifndef __CYGWIN32__
530 chmod(ttydev, ttyfd_stat.st_mode);
531 chown(ttydev, ttyfd_stat.st_uid, ttyfd_stat.st_gid);
532 #endif
533 #ifdef UTMP_SUPPORT
534 cleanutent();
535 #endif
536 privileges(IGNORE);
537 }
538
539 /*}}} */
540
541 /*{{{ Acquire a pseudo-teletype from the system. */
542 /*
543 * On failure, returns -1.
544 * On success, returns the file descriptor.
545 *
546 * If successful, ttydev and ptydev point to the names of the
547 * master and slave parts
548 */
549 /* PROTO */
550 int
get_pty(void)551 get_pty(void)
552 {
553 int fd = -1;
554
555 #if defined (__sgi)
556 ptydev = ttydev = _getpty(&fd, O_RDWR | O_NDELAY, 0622, 0);
557 if (ptydev == NULL)
558 goto Failed;
559 #elif defined (__svr4__) || defined(__CYGWIN32__) || defined(__lnx21__)
560 {
561 extern char *ptsname();
562
563 /* open the STREAMS, clone device /dev/ptmx (master pty) */
564 #ifdef HAVE_GETPT
565 if ((fd = getpt()) < 0) {
566 #else
567 if ((fd = open("/dev/ptmx", O_RDWR)) < 0) {
568 #endif
569 goto Failed;
570 } else {
571 grantpt(fd); /* change slave permissions */
572 unlockpt(fd); /* unlock slave */
573 ptydev = ttydev = ptsname(fd); /* get slave's name */
574 goto Found;
575 }
576 }
577 #elif defined (_AIX)
578 if ((fd = open("/dev/ptc", O_RDWR)) < 0)
579 goto Failed;
580 else
581 ptydev = ttydev = ttyname(fd);
582 #elif defined(ALL_NUMERIC_PTYS) /* SCO OSr5 */
583 static char pty_name[] = "/dev/ptyp??\0\0\0";
584 static char tty_name[] = "/dev/ttyp??\0\0\0";
585 int len = strlen(tty_name);
586 char *c1, *c2;
587 int idx;
588
589 ptydev = pty_name;
590 ttydev = tty_name;
591
592 for (idx = 0; idx < 256; idx++) {
593 sprintf(ptydev, "%s%d", "/dev/ptyp", idx);
594 sprintf(ttydev, "%s%d", "/dev/ttyp", idx);
595
596 if (access(ttydev, F_OK) < 0) {
597 idx = 256;
598 break;
599 }
600 if ((fd = open(ptydev, O_RDWR)) >= 0) {
601 if (access(ttydev, R_OK | W_OK) == 0)
602 goto Found;
603 close(fd);
604 }
605 }
606 goto Failed;
607 #else
608 static char pty_name[] = "/dev/pty??";
609 static char tty_name[] = "/dev/tty??";
610 int len = strlen(tty_name);
611 char *c1, *c2;
612
613 ptydev = pty_name;
614 ttydev = tty_name;
615
616 # define PTYCHAR1 "pqrstuvwxyz"
617 # define PTYCHAR2 "0123456789abcdefghijklmnopqrstuvwxyz"
618 for (c1 = PTYCHAR1; *c1; c1++) {
619 ptydev[len - 2] = ttydev[len - 2] = *c1;
620 for (c2 = PTYCHAR2; *c2; c2++) {
621 ptydev[len - 1] = ttydev[len - 1] = *c2;
622 if ((fd = open(ptydev, O_RDWR)) >= 0) {
623 if (access(ttydev, R_OK | W_OK) == 0)
624 goto Found;
625 close(fd);
626 }
627 }
628 }
629 goto Failed;
630 #endif
631
632 Found:
633 fcntl(fd, F_SETFL, O_NDELAY);
634 return fd;
635
636 Failed:
637 print_error("can't open pseudo-tty");
638 return -1;
639 }
640 /*}}} */
641
642 /*{{{ establish a controlling teletype for new session */
643 /*
644 * On some systems this can be done with ioctl() but on others we
645 * need to re-open the slave tty.
646 */
647 /* PROTO */
648 int
649 get_tty(void)
650 {
651 int fd;
652 pid_t pid;
653
654 /*
655 * setsid() [or setpgrp] must be before open of the terminal,
656 * otherwise there is no controlling terminal (Solaris 2.4, HP-UX 9)
657 */
658 #ifndef ultrix
659 # ifdef NO_SETSID
660 pid = setpgrp(0, 0);
661 # else
662 pid = setsid();
663 # endif
664 if (pid < 0)
665 perror(rs_name);
666 # ifdef DEBUG_TTYMODE
667 print_error("(%s: line %d): PID = %d\n", __FILE__, __LINE__, pid);
668 # endif
669 #endif /* ultrix */
670
671 if ((fd = open(ttydev, O_RDWR)) < 0) {
672 print_error("can't open slave tty %s", ttydev);
673 exit(EXIT_FAILURE);
674 }
675 #if defined (__svr4__) || defined (__lnx21__)
676 /*
677 * Push STREAMS modules:
678 * ptem: pseudo-terminal hardware emulation module.
679 * ldterm: standard terminal line discipline.
680 * ttcompat: V7, 4BSD and XENIX STREAMS compatibility module.
681 */
682 ioctl(fd, I_PUSH, "ptem");
683 ioctl(fd, I_PUSH, "ldterm");
684 ioctl(fd, I_PUSH, "ttcompat");
685 #else /* __svr4__ */
686 {
687 /* change ownership of tty to real uid and real group */
688 #ifndef __CYGWIN32__
689 unsigned int mode = 0622;
690 gid_t gid = getgid();
691
692 # ifdef TTY_GID_SUPPORT
693 {
694 struct group *gr = getgrnam("tty");
695
696 if (gr) {
697 /* change ownership of tty to real uid, "tty" gid */
698 gid = gr->gr_gid;
699 mode = 0620;
700 }
701 }
702 # endif /* TTY_GID_SUPPORT */
703 #endif
704 privileges(RESTORE);
705 #ifndef __CYGWIN32__
706 fchown(fd, getuid(), gid); /* fail silently */
707 fchmod(fd, mode);
708 #endif
709 privileges(IGNORE);
710 }
711 #endif /* __svr4__ */
712
713 /*
714 * Close all file descriptors. If only stdin/out/err are closed,
715 * child processes remain alive upon deletion of the window.
716 */
717 {
718 int i;
719
720 for (i = 0; i < num_fds; i++)
721 if (i != fd)
722 close(i);
723 }
724
725 /* Reopen stdin, stdout and stderr over the tty file descriptor */
726 dup(fd); /* 0: stdin */
727 dup(fd); /* 1: stdout */
728 dup(fd); /* 2: stderr */
729
730 if (fd > 2)
731 close(fd);
732
733 #ifdef ultrix
734 if ((fd = open("/dev/tty", O_RDONLY)) >= 0) {
735 ioctl(fd, TIOCNOTTY, 0);
736 close(fd);
737 } else {
738 pid = setpgrp(0, 0);
739 if (pid < 0)
740 perror(rs_name);
741 }
742
743 /* no error, we could run with no tty to begin with */
744 #else /* ultrix */
745 # ifdef TIOCSCTTY
746 ioctl(0, TIOCSCTTY, 0);
747 # endif
748
749 /* set process group */
750 # if defined (_POSIX_VERSION) || defined (__svr4__)
751 tcsetpgrp(0, pid);
752 # elif defined (TIOCSPGRP)
753 ioctl(0, TIOCSPGRP, &pid);
754 # endif
755
756 /* svr4 problems: reports no tty, no job control */
757 /* # if !defined (__svr4__) && defined (TIOCSPGRP) */
758
759 close(open(ttydev, O_RDWR, 0));
760 /* # endif */
761 #endif /* ultrix */
762
763 privileges(IGNORE);
764
765 return fd;
766 }
767 /*}}} */
768
769 /*{{{ debug_ttymode() */
770 #ifdef DEBUG_TTYMODE
771 /* PROTO */
772 void
773 debug_ttymode(ttymode_t * ttymode)
774 {
775 # ifdef HAVE_TERMIOS_H
776 /* c_iflag bits */
777 fprintf(stderr, "Input flags\n");
778
779 /* cpp token stringize doesn't work on all machines <sigh> */
780 # define FOO(flag,name) \
781 if ((ttymode->c_iflag) & flag) \
782 fprintf (stderr, "%s ", name)
783
784 /* c_iflag bits */
785 FOO(IGNBRK, "IGNBRK");
786 FOO(BRKINT, "BRKINT");
787 FOO(IGNPAR, "IGNPAR");
788 FOO(PARMRK, "PARMRK");
789 FOO(INPCK, "INPCK");
790 FOO(ISTRIP, "ISTRIP");
791 FOO(INLCR, "INLCR");
792 FOO(IGNCR, "IGNCR");
793 FOO(ICRNL, "ICRNL");
794 FOO(IXON, "IXON");
795 FOO(IXOFF, "IXOFF");
796 # ifdef IUCLC
797 FOO(IUCLC, "IUCLC");
798 # endif
799 # ifdef IXANY
800 FOO(IXANY, "IXANY");
801 # endif
802 # ifdef IMAXBEL
803 FOO(IMAXBEL, "IMAXBEL");
804 # endif
805 fprintf(stderr, "\n\n");
806
807 # undef FOO
808 # define FOO(entry, name) \
809 fprintf (stderr, "%s = %#3o\n", name, ttymode->c_cc [entry])
810
811 FOO(VINTR, "VINTR");
812 FOO(VQUIT, "VQUIT");
813 FOO(VERASE, "VERASE");
814 FOO(VKILL, "VKILL");
815 FOO(VEOF, "VEOF");
816 FOO(VEOL, "VEOL");
817 # ifdef VEOL2
818 FOO(VEOL2, "VEOL2");
819 # endif
820 # ifdef VSWTC
821 FOO(VSWTC, "VSWTC");
822 # endif
823 # ifdef VSWTCH
824 FOO(VSWTCH, "VSWTCH");
825 # endif
826 FOO(VSTART, "VSTART");
827 FOO(VSTOP, "VSTOP");
828 FOO(VSUSP, "VSUSP");
829 # ifdef VDSUSP
830 FOO(VDSUSP, "VDSUSP");
831 # endif
832 # ifdef VREPRINT
833 FOO(VREPRINT, "VREPRINT");
834 # endif
835 # ifdef VDISCRD
836 FOO(VDISCRD, "VDISCRD");
837 # endif
838 # ifdef VWERSE
839 FOO(VWERSE, "VWERSE");
840 # endif
841 # ifdef VLNEXT
842 FOO(VLNEXT, "VLNEXT");
843 # endif
844 fprintf(stderr, "\n\n");
845 # undef FOO
846 # endif /* HAVE_TERMIOS_H */
847 }
848 #endif /* DEBUG_TTYMODE */
849 /*}}} */
850
851 /*{{{ get_ttymode() */
852 /* PROTO */
853 void
854 get_ttymode(ttymode_t * tio)
855 {
856 #ifdef HAVE_TERMIOS_H
857 /*
858 * standard System V termios interface
859 */
860 if (GET_TERMIOS(0, tio) < 0) {
861 /* return error - use system defaults */
862 tio->c_cc[VINTR] = CINTR;
863 tio->c_cc[VQUIT] = CQUIT;
864 tio->c_cc[VERASE] = CERASE;
865 tio->c_cc[VKILL] = CKILL;
866 tio->c_cc[VSTART] = CSTART;
867 tio->c_cc[VSTOP] = CSTOP;
868 tio->c_cc[VSUSP] = CSUSP;
869 # ifdef VDSUSP
870 tio->c_cc[VDSUSP] = CDSUSP;
871 # endif
872 # ifdef VREPRINT
873 tio->c_cc[VREPRINT] = CRPRNT;
874 # endif
875 # ifdef VDISCRD
876 tio->c_cc[VDISCRD] = CFLUSH;
877 # endif
878 # ifdef VWERSE
879 tio->c_cc[VWERSE] = CWERASE;
880 # endif
881 # ifdef VLNEXT
882 tio->c_cc[VLNEXT] = CLNEXT;
883 # endif
884 }
885 tio->c_cc[VEOF] = CEOF;
886 tio->c_cc[VEOL] = VDISABLE;
887 # ifdef VEOL2
888 tio->c_cc[VEOL2] = VDISABLE;
889 # endif
890 # ifdef VSWTC
891 tio->c_cc[VSWTC] = VDISABLE;
892 # endif
893 # ifdef VSWTCH
894 tio->c_cc[VSWTCH] = VDISABLE;
895 # endif
896 # if VMIN != VEOF
897 tio->c_cc[VMIN] = 1;
898 # endif
899 # if VTIME != VEOL
900 tio->c_cc[VTIME] = 0;
901 # endif
902
903 /* input modes */
904 tio->c_iflag = (BRKINT | IGNPAR | ICRNL | IXON
905 # ifdef IMAXBEL
906 | IMAXBEL
907 # endif
908 );
909
910 /* output modes */
911 tio->c_oflag = (OPOST | ONLCR);
912
913 /* control modes */
914 tio->c_cflag = (CS8 | CREAD);
915
916 /* line discipline modes */
917 tio->c_lflag = (ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK
918 # if defined (ECHOCTL) && defined (ECHOKE)
919 | ECHOCTL | ECHOKE
920 # endif
921 );
922
923 #if 0
924 /*
925 * guess an appropriate value for Backspace
926 */
927 # ifdef FORCE_BACKSPACE
928 PrivMode(1, PrivMode_BackSpace);
929 # elif defined (FORCE_DELETE)
930 PrivMode(0, PrivMode_BackSpace);
931 # else
932 PrivMode((tio->c_cc[VERASE] == '\b'), PrivMode_BackSpace);
933 # endif
934 #endif /* if 0 */
935
936 #else /* HAVE_TERMIOS_H */
937
938 /*
939 * sgtty interface
940 */
941
942 /* get parameters -- gtty */
943 if (ioctl(0, TIOCGETP, &(tio->sg)) < 0) {
944 tio->sg.sg_erase = CERASE; /* ^H */
945 tio->sg.sg_kill = CKILL; /* ^U */
946 }
947 tio->sg.sg_flags = (CRMOD | ECHO | EVENP | ODDP);
948
949 /* get special characters */
950 if (ioctl(0, TIOCGETC, &(tio->tc)) < 0) {
951 tio->tc.t_intrc = CINTR; /* ^C */
952 tio->tc.t_quitc = CQUIT; /* ^\ */
953 tio->tc.t_startc = CSTART; /* ^Q */
954 tio->tc.t_stopc = CSTOP; /* ^S */
955 tio->tc.t_eofc = CEOF; /* ^D */
956 tio->tc.t_brkc = -1;
957 }
958 /* get local special chars */
959 if (ioctl(0, TIOCGLTC, &(tio->lc)) < 0) {
960 tio->lc.t_suspc = CSUSP; /* ^Z */
961 tio->lc.t_dsuspc = CDSUSP; /* ^Y */
962 tio->lc.t_rprntc = CRPRNT; /* ^R */
963 tio->lc.t_flushc = CFLUSH; /* ^O */
964 tio->lc.t_werasc = CWERASE; /* ^W */
965 tio->lc.t_lnextc = CLNEXT; /* ^V */
966 }
967 /* get line discipline */
968 ioctl(0, TIOCGETD, &(tio->line));
969 # ifdef NTTYDISC
970 tio->line = NTTYDISC;
971 # endif /* NTTYDISC */
972 tio->local = (LCRTBS | LCRTERA | LCTLECH | LPASS8 | LCRTKIL);
973
974 /*
975 * guess an appropriate value for Backspace
976 */
977 #if 0
978 # ifdef FORCE_BACKSPACE
979 PrivMode(1, PrivMode_BackSpace);
980 # elif defined (FORCE_DELETE)
981 PrivMode(0, PrivMode_BackSpace);
982 # else
983 PrivMode((tio->sg.sg_erase == '\b'), PrivMode_BackSpace);
984 # endif
985 #endif /* if 0 */
986
987 #endif /* HAVE_TERMIOS_H */
988 }
989 /*}}} */
990
991 /*{{{ run_command() */
992 /*
993 * Run the command in a subprocess and return a file descriptor for the
994 * master end of the pseudo-teletype pair with the command talking to
995 * the slave.
996 */
997 /* PROTO */
998 int
999 run_command(char *argv[])
1000 {
1001 ttymode_t tio;
1002 int ptyfd;
1003
1004 ptyfd = get_pty();
1005 if (ptyfd < 0)
1006 return -1;
1007
1008 /* store original tty status for restoration clean_exit() -- rgg 04/12/95 */
1009 lstat(ttydev, &ttyfd_stat);
1010 #ifdef DEBUG_CMD
1011 fprintf(stderr, "Original settings of %s are mode %o, uid %d, gid %d\n",
1012 ttydev, ttyfd_stat.st_mode, ttyfd_stat.st_uid, ttyfd_stat.st_gid);
1013 #endif
1014
1015 /* install exit handler for cleanup */
1016 #ifdef HAVE_ATEXIT
1017 atexit(clean_exit);
1018 #else
1019 # if defined (__sun__)
1020 on_exit(clean_exit, NULL); /* non-ANSI exit handler */
1021 # else
1022 # ifdef UTMP_SUPPORT
1023 print_error("no atexit(), UTMP entries may not be cleaned");
1024 # endif
1025 # endif
1026 #endif
1027
1028 /*
1029 * get tty settings before fork()
1030 * and make a reasonable guess at the value for BackSpace
1031 */
1032 get_ttymode(&tio);
1033 #if 0
1034 /* add Backspace value */
1035 SavedModes |= (PrivateModes & PrivMode_BackSpace);
1036 #endif
1037
1038 /* add value for scrollBar */
1039 if (scrollbar_visible()) {
1040 PrivateModes |= PrivMode_scrollBar;
1041 SavedModes |= PrivMode_scrollBar;
1042 }
1043 if (menubar_visible()) {
1044 PrivateModes |= PrivMode_menuBar;
1045 SavedModes |= PrivMode_menuBar;
1046 }
1047 #ifdef DEBUG_TTYMODE
1048 debug_ttymode(&tio);
1049 #endif
1050
1051 /* spin off the command interpreter */
1052 signal(SIGHUP, Exit_signal);
1053 #ifndef __svr4__
1054 signal(SIGINT, Exit_signal);
1055 #endif
1056 signal(SIGQUIT, Exit_signal);
1057 signal(SIGTERM, Exit_signal);
1058 signal(SIGCHLD, Child_signal);
1059
1060 /* apparently on FBSD that should be done at line 1085 below : */
1061 tt_winsize(ptyfd); /* set window size */
1062
1063 /* need to trap SIGURG for SVR4 (Unixware) rlogin */
1064 /* signal (SIGURG, SIG_DFL); */
1065
1066 cmd_pid = fork();
1067 if (cmd_pid < 0) {
1068 print_error("can't fork");
1069 return -1;
1070 }
1071 if (cmd_pid == 0) { /* child */
1072 /* signal (SIGHUP, Exit_signal); */
1073 /* signal (SIGINT, Exit_signal); */
1074 #ifdef HAVE_UNSETENV
1075 /* avoid passing old settings and confusing term size */
1076 unsetenv("LINES");
1077 unsetenv("COLUMNS");
1078 /* avoid passing termcap since terminfo should be okay */
1079 unsetenv("TERMCAP");
1080 #endif /* HAVE_UNSETENV */
1081 /* establish a controlling teletype for the new session */
1082 get_tty();
1083
1084 /* initialize terminal attributes */
1085 SET_TTYMODE(0, &tio);
1086
1087 /* become virtual console, fail silently */
1088 if (Options & Opt_console) {
1089 #ifdef TIOCCONS
1090 unsigned int on = 1;
1091
1092 ioctl(0, TIOCCONS, &on);
1093 #elif defined (SRIOCSREDIR)
1094 int fd = open(CONSOLE, O_WRONLY);
1095
1096 if (fd < 0 || ioctl(fd, SRIOCSREDIR, 0) < 0) {
1097 if (fd >= 0)
1098 close(fd);
1099 }
1100 #endif /* SRIOCSREDIR */
1101 }
1102
1103 tt_winsize(0); /* set window size */
1104
1105 /* reset signals and spin off the command interpreter */
1106 signal(SIGINT, SIG_DFL);
1107 signal(SIGQUIT, SIG_DFL);
1108 signal(SIGCHLD, SIG_DFL);
1109 /*
1110 * mimick login's behavior by disabling the job control signals
1111 * a shell that wants them can turn them back on
1112 */
1113 #ifdef SIGTSTP
1114 signal(SIGTSTP, SIG_IGN);
1115 signal(SIGTTIN, SIG_IGN);
1116 signal(SIGTTOU, SIG_IGN);
1117 #endif /* SIGTSTP */
1118
1119 #ifdef __CYGWIN__
1120 /* cygwin does not close files properly */
1121 if( Xfd > 0 )
1122 close(Xfd);
1123 #endif
1124
1125 /* command interpreter path */
1126 if (argv != NULL) {
1127 #ifdef DEBUG_CMD
1128 int i;
1129
1130 for (i = 0; argv[i]; i++)
1131 fprintf(stderr, "argv [%d] = \"%s\"\n", i, argv[i]);
1132 #endif
1133 execvp(argv[0], argv);
1134 print_error("can't execute \"%s\"", argv[0]);
1135 } else {
1136 const char *argv0, *shell;
1137
1138 if ((shell = getenv("SHELL")) == NULL || *shell == '\0')
1139 shell = DEFAULT_SHELL;
1140
1141 argv0 = my_basename(shell);
1142 if (Options & Opt_loginShell) {
1143 char *p = MALLOC((strlen(argv0) + 2) * sizeof(char));
1144
1145 p[0] = '-';
1146 STRCPY(&p[1], argv0);
1147 argv0 = p;
1148 }
1149 execlp(shell, argv0, NULL);
1150 print_error("can't execute \"%s\"", shell);
1151 }
1152 exit(EXIT_FAILURE);
1153 }
1154 #ifdef UTMP_SUPPORT
1155 privileges(RESTORE);
1156 if (!(Options & Opt_utmpInhibit))
1157 makeutent(ttydev, display_name); /* stamp /etc/utmp */
1158 privileges(IGNORE);
1159 #endif
1160
1161 return ptyfd;
1162 }
1163 /*}}} */
1164
1165 /*
1166 * Probe the modifier keymap to get the Meta (Alt) and Num_Lock settings
1167 */
1168 /* INTPROTO */
1169 void
1170 get_ourmods(void)
1171 {
1172 int i, j, k;
1173 int got_meta, got_numlock;
1174 XModifierKeymap *map;
1175 KeyCode *kc;
1176 unsigned int modmasks[] =
1177 {Mod1Mask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask};
1178
1179 got_meta = got_numlock = 0;
1180 map = XGetModifierMapping(Xdisplay);
1181 kc = map->modifiermap;
1182 for (i = 3; i < 8; i++) {
1183 k = i * map->max_keypermod;
1184 for (j = 0; j < map->max_keypermod; j++, k++) {
1185 if (kc[k] == 0)
1186 break;
1187 switch (XKeycodeToKeysym(Xdisplay, kc[k], 0)) {
1188 case XK_Num_Lock:
1189 if (!got_numlock) {
1190 ModNumLockMask = modmasks[i - 3];
1191 got_numlock = 1;
1192 }
1193 break;
1194 case XK_Meta_L:
1195 case XK_Meta_R:
1196 case XK_Alt_L:
1197 case XK_Alt_R:
1198 if (!got_meta) {
1199 ModMetaMask = modmasks[i - 3];
1200 got_meta = 1;
1201 }
1202 break;
1203 }
1204 }
1205 if (got_meta && got_numlock)
1206 break;
1207 }
1208 XFreeModifiermap(map);
1209 }
1210
1211
1212 /*{{{ init_command() */
1213 /* PROTO */
1214 void
1215 init_command(char *argv[])
1216 {
1217 /*
1218 * Initialize the command connection.
1219 * This should be called after the X server connection is established.
1220 */
1221
1222 /* Enable delete window protocol */
1223 wmDeleteWindow = XInternAtom(Xdisplay, "WM_DELETE_WINDOW", False);
1224 XSetWMProtocols(Xdisplay, TermWin.parent, &wmDeleteWindow, 1);
1225
1226 #ifdef OFFIX_DND
1227 /* Enable OffiX Dnd (drag 'n' drop) protocol */
1228 DndProtocol = XInternAtom(Xdisplay, "DndProtocol", False);
1229 DndSelection = XInternAtom(Xdisplay, "DndSelection", False);
1230 #endif /* OFFIX_DND */
1231
1232 #ifndef NO_XLOCALE
1233 init_xlocale();
1234 #else
1235 setlocale(LC_CTYPE, "");
1236 #endif
1237
1238 #ifdef USE_XIM
1239 setTermFontSet();
1240 XRegisterIMInstantiateCallback(Xdisplay, NULL, NULL, NULL, IMInstantiateCallback, NULL);
1241 #endif
1242
1243 /* get number of available file descriptors */
1244 #ifdef _POSIX_VERSION
1245 num_fds = sysconf(_SC_OPEN_MAX);
1246 #else
1247 num_fds = getdtablesize();
1248 #endif
1249
1250 #ifdef META8_OPTION
1251 meta_char = (Options & Opt_meta8 ? 0x80 : 033);
1252 if (rs_modifier
1253 && strlen(rs_modifier) == 4
1254 && toupper(*rs_modifier) == 'M'
1255 && toupper(rs_modifier[1]) == 'O'
1256 && toupper(rs_modifier[2]) == 'D')
1257 switch (rs_modifier[3]) {
1258 case '2':
1259 ModXMask = Mod2Mask;
1260 break;
1261 case '3':
1262 ModXMask = Mod3Mask;
1263 break;
1264 case '4':
1265 ModXMask = Mod4Mask;
1266 break;
1267 case '5':
1268 ModXMask = Mod5Mask;
1269 break;
1270 default:
1271 ModXMask = Mod1Mask;
1272 }
1273 #endif
1274 get_ourmods();
1275 if (!(Options & Opt_scrollTtyOutput))
1276 PrivateModes |= PrivMode_TtyOutputInh;
1277 if (Options & Opt_scrollKeypress)
1278 PrivateModes |= PrivMode_Keypress;
1279 #ifndef NO_BACKSPACE_KEY
1280 if (strcmp(rs_backspace_key, "DEC") == 0)
1281 PrivateModes |= PrivMode_HaveBackSpace;
1282 #endif
1283
1284 #ifdef GREEK_SUPPORT
1285 greek_init();
1286 #endif
1287
1288 Xfd = XConnectionNumber(Xdisplay);
1289
1290 /*fprintf( stderr, "%s:%d display = %p, Xfd = %d\n\n", __FUNCTION__, __LINE__, Xdisplay, Xfd ); */
1291 /* sleep(10); */
1292
1293 cmdbuf_ptr = cmdbuf_endp = cmdbuf_base;
1294
1295 if ((cmd_fd = run_command(argv)) < 0) {
1296 print_error("aborting");
1297 exit(EXIT_FAILURE);
1298 }
1299 }
1300 /*}}} */
1301
1302 /*{{{ Xlocale */
1303 /*
1304 * This is more or less stolen straight from XFree86 xterm.
1305 * This should support all European type languages.
1306 */
1307 /* PROTO */
1308 void
1309 init_xlocale(void)
1310 {
1311 #ifndef NO_XLOCALE
1312 #ifndef USE_XIM
1313 char *p, *s, buf[32], tmp[1024];
1314 XIM xim = NULL;
1315 XIMStyle input_style = 0;
1316 XIMStyles *xim_styles = NULL;
1317 int found;
1318
1319 Input_Context = NULL;
1320
1321 # if !defined(NO_SETLOCALE) || !defined(NO_XSETLOCALE)
1322 /* setlocale(LC_CTYPE, ""); */ /* XXX: should we do this? */
1323 # endif
1324 if (rs_inputMethod == NULL || !*rs_inputMethod) {
1325 if ((p = XSetLocaleModifiers("")) != NULL)
1326 xim = XOpenIM(Xdisplay, NULL, NULL, NULL);
1327 } else {
1328 STRCPY(tmp, rs_inputMethod);
1329 for (s = tmp; *s; s++) {
1330 char *end, *next_s;
1331
1332 for (; *s && isspace(*s); s++)
1333 /* */ ;
1334 if (!*s)
1335 break;
1336 for (end = s; (*end && (*end != ',')); end++)
1337 /* */ ;
1338 for (next_s = end--; ((end >= s) && isspace(*end)); end--)
1339 /* */ ;
1340 *++end = '\0';
1341 if (*s) {
1342 STRCPY(buf, "@im=");
1343 strcat(buf, s);
1344 if ((p = XSetLocaleModifiers(buf)) != NULL &&
1345 (xim = XOpenIM(Xdisplay, NULL, NULL, NULL)) != NULL)
1346 break;
1347 }
1348 if (!*(s = next_s))
1349 break;
1350 }
1351 }
1352
1353 if (xim == NULL && (p = XSetLocaleModifiers("@im=none")) != NULL)
1354 xim = XOpenIM(Xdisplay, NULL, NULL, NULL);
1355
1356 if (xim == NULL) {
1357 print_error("Failed to open input method");
1358 return;
1359 }
1360 if (XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL) || !xim_styles) {
1361 print_error("input method doesn't support any style");
1362 XCloseIM(xim);
1363 return;
1364 }
1365 STRCPY(tmp, (rs_preeditType ? rs_preeditType : "Root"));
1366 for (found = 0, s = tmp; *s && !found; ) {
1367 unsigned short i;
1368 char *end, *next_s;
1369
1370 for (; *s && isspace(*s); s++)
1371 /* */ ;
1372 if (!*s)
1373 break;
1374 for (end = s; (*end && (*end != ',')); end++)
1375 /* */ ;
1376 for (next_s = end--; ((end >= s) && isspace(*end));)
1377 *end-- = 0;
1378
1379 if (!strcmp(s, "OverTheSpot"))
1380 input_style = (XIMPreeditPosition | XIMStatusArea);
1381 else if (!strcmp(s, "OffTheSpot"))
1382 input_style = (XIMPreeditArea | XIMStatusArea);
1383 else if (!strcmp(s, "Root"))
1384 input_style = (XIMPreeditNothing | XIMStatusNothing);
1385
1386 for (i = 0; i < xim_styles->count_styles; i++)
1387 if (input_style == xim_styles->supported_styles[i]) {
1388 found = 1;
1389 break;
1390 }
1391 s = next_s;
1392 }
1393 XFree(xim_styles);
1394
1395 if (found == 0) {
1396 print_error("input method doesn't support my preedit type");
1397 XCloseIM(xim);
1398 return;
1399 }
1400 /*
1401 * This program only understands the Root preedit_style yet
1402 * Then misc.preedit_type should default to:
1403 * "OverTheSpot,OffTheSpot,Root"
1404 * /MaF
1405 */
1406 if (input_style != (XIMPreeditNothing | XIMStatusNothing)) {
1407 print_error("This program only supports the \"Root\" preedit type");
1408 XCloseIM(xim);
1409 return;
1410 }
1411 Input_Context = XCreateIC(xim, XNInputStyle, input_style,
1412 XNClientWindow, TermWin.parent,
1413 XNFocusWindow, TermWin.parent,
1414 NULL);
1415
1416 if (Input_Context == NULL) {
1417 print_error("Failed to create input context");
1418 XCloseIM(xim);
1419 }
1420 #endif /* USE_XIM */
1421 #endif /* NO_XLOCALE */
1422 }
1423 /*}}} */
1424
1425 /*{{{ window resizing */
1426 /*
1427 * Tell the teletype handler what size the window is.
1428 * Called after a window size change.
1429 */
1430 /* PROTO */
1431 void
1432 tt_winsize(int fd)
1433 {
1434 struct winsize ws;
1435
1436 if (fd < 0)
1437 return;
1438
1439 ws.ws_col = (unsigned short)TermWin.ncol;
1440 ws.ws_row = (unsigned short)TermWin.nrow;
1441 #ifndef __CYGWIN32__
1442 ws.ws_xpixel = ws.ws_ypixel = 0;
1443 #endif
1444 ioctl(fd, TIOCSWINSZ, &ws);
1445 }
1446
1447 /* PROTO */
1448 void
1449 tt_resize(void)
1450 {
1451 tt_winsize(cmd_fd);
1452 }
1453
1454 /*}}} */
1455
1456 /*{{{ Convert the keypress event into a string */
1457 /* PROTO */
1458 void
1459 lookup_key(XEvent * ev)
1460 {
1461 static int numlock_state = 0;
1462
1463 #ifdef DEBUG_CMD
1464 static int debug_key = 1; /* accessible by a debugger only */
1465 #endif
1466 #ifdef GREEK_SUPPORT
1467 static short greek_mode = 0;
1468 #endif
1469 static XComposeStatus compose =
1470 {NULL, 0};
1471 static unsigned char kbuf[KBUFSZ];
1472 int ctrl, meta, shft, len;
1473 KeySym keysym = 0;
1474
1475 /*
1476 * use Num_Lock to toggle Keypad on/off. If Num_Lock is off, allow an
1477 * escape sequence to toggle the Keypad.
1478 *
1479 * Always permit `shift' to override the current setting
1480 */
1481 shft = (ev->xkey.state & ShiftMask);
1482 ctrl = (ev->xkey.state & ControlMask);
1483 meta = (ev->xkey.state & ModXMask);
1484
1485 if (numlock_state || (ev->xkey.state & ModNumLockMask)) {
1486 numlock_state = (ev->xkey.state & ModNumLockMask); /* numlock toggle */
1487 PrivMode((!numlock_state), PrivMode_aplKP);
1488 }
1489 #ifdef USE_XIM
1490 len = 0;
1491 if (Input_Context != NULL) {
1492 Status status_return;
1493
1494 kbuf[0] = '\0';
1495 len = XmbLookupString(Input_Context, &ev->xkey, kbuf,
1496 sizeof(kbuf), &keysym,
1497 &status_return);
1498 } else {
1499 len = XLookupString(&ev->xkey, kbuf,
1500 sizeof(kbuf), &keysym,
1501 &compose);
1502 }
1503 #else /* USE_XIM */
1504 len = XLookupString(&ev->xkey, (char *) kbuf, sizeof(kbuf), &keysym, &compose);
1505 /*
1506 * have unmapped Latin[2-4] entries -> Latin1
1507 * good for installations with correct fonts, but without XLOCAL
1508 */
1509 if (!len && (keysym >= 0x0100) && (keysym < 0x0400)) {
1510 len = 1;
1511 kbuf[0] = (keysym & 0xFF);
1512 }
1513 #endif /* USE_XIM */
1514
1515 if (len && (Options & Opt_scrollKeypress))
1516 TermWin.view_start = 0;
1517
1518 /* for some backwards compatibility */
1519 #if defined (HOTKEY_CTRL) || defined (HOTKEY_META)
1520 # ifdef HOTKEY_CTRL
1521 # define HOTKEY ctrl
1522 # else
1523 # ifdef HOTKEY_META
1524 # define HOTKEY meta
1525 # endif
1526 # endif
1527 if (HOTKEY) {
1528 if (keysym == ks_bigfont) {
1529 change_font(0, FONT_UP);
1530 return;
1531 } else if (keysym == ks_smallfont) {
1532 change_font(0, FONT_DN);
1533 return;
1534 }
1535 }
1536 # undef HOTKEY
1537 #endif
1538
1539 if (shft) {
1540 /* Shift + F1 - F10 generates F11 - F20 */
1541 if (keysym >= XK_F1 && keysym <= XK_F10) {
1542 keysym += (XK_F11 - XK_F1);
1543 shft = 0; /* turn off Shift */
1544 } else if (!ctrl && !meta && (PrivateModes & PrivMode_ShiftKeys)) {
1545 int lnsppg = TermWin.nrow * 4 / 5;
1546
1547 #ifdef PAGING_CONTEXT_LINES
1548 lnsppg = TermWin.nrow - PAGING_CONTEXT_LINES;
1549 #endif
1550
1551 switch (keysym) {
1552 /* normal XTerm key bindings */
1553 case XK_Prior: /* Shift+Prior = scroll back */
1554 if (TermWin.saveLines) {
1555 scr_page(UP, lnsppg);
1556 return;
1557 }
1558 break;
1559
1560 case XK_Up: /* Shift+XK_Up = scroll up one line */
1561 if (TermWin.saveLines) {
1562 scr_page(UP, 1);
1563 return;
1564 }
1565 break;
1566
1567 case XK_Down: /* Shift+XK_Down = scroll down one line */
1568 if (TermWin.saveLines) {
1569 scr_page(DN, 1);
1570 return;
1571 }
1572 break;
1573
1574 case XK_Next: /* Shift+Next = scroll forward */
1575 if (TermWin.saveLines) {
1576 scr_page(DN, lnsppg);
1577 return;
1578 }
1579 break;
1580
1581 case XK_Insert: /* Shift+Insert = paste mouse selection */
1582 selection_request(ev->xkey.time, ev->xkey.x, ev->xkey.y);
1583 return;
1584 break;
1585
1586 /* rxvt extras */
1587 case XK_KP_Add: /* Shift+KP_Add = bigger font */
1588 change_font(0, FONT_UP);
1589 return;
1590 break;
1591
1592 case XK_KP_Subtract: /* Shift+KP_Subtract = smaller font */
1593 change_font(0, FONT_DN);
1594 return;
1595 break;
1596 }
1597 }
1598 }
1599 #ifdef UNSHIFTED_SCROLLKEYS
1600 else if (!ctrl && !meta) {
1601 switch (keysym) {
1602 case XK_Prior:
1603 if (TermWin.saveLines) {
1604 scr_page(UP, TermWin.nrow * 4 / 5);
1605 return;
1606 }
1607 break;
1608
1609 case XK_Next:
1610 if (TermWin.saveLines) {
1611 scr_page(DN, TermWin.nrow * 4 / 5);
1612 return;
1613 }
1614 break;
1615 }
1616 }
1617 #endif
1618
1619 switch (keysym) {
1620 case XK_Print:
1621 #ifdef PRINTPIPE
1622 scr_printscreen(ctrl | shft);
1623 return;
1624 #endif
1625 break;
1626
1627 case XK_Mode_switch:
1628 #ifdef GREEK_SUPPORT
1629 greek_mode = !greek_mode;
1630 if (greek_mode) {
1631 xterm_seq(XTerm_title, (greek_getmode() == GREEK_ELOT928 ?
1632 "[Greek: iso]" : "[Greek: ibm]"));
1633 greek_reset();
1634 } else
1635 xterm_seq(XTerm_title, APL_NAME "-" VERSION);
1636 return;
1637 #endif
1638 break;
1639 }
1640
1641 if (keysym >= 0xFF00 && keysym <= 0xFFFF) {
1642 #ifdef KEYSYM_RESOURCE
1643 if (!(shft | ctrl) && KeySym_map[keysym - 0xFF00] != NULL) {
1644 const unsigned char *kbuf;
1645 unsigned int len;
1646
1647 kbuf = (KeySym_map[keysym - 0xFF00]);
1648 len = *kbuf++;
1649
1650 /* escape prefix */
1651 if (meta
1652 # ifdef META8_OPTION
1653 && (meta_char == 033)
1654 # endif
1655 ) {
1656 const unsigned char ch = '\033';
1657
1658 tt_write(&ch, 1);
1659 }
1660 tt_write(kbuf, len);
1661 return;
1662 } else
1663 #endif
1664 switch (keysym) {
1665 #ifndef NO_BACKSPACE_KEY
1666 case XK_BackSpace:
1667 if (PrivateModes & PrivMode_HaveBackSpace) {
1668 len = 1;
1669 kbuf[0] = (((PrivateModes & PrivMode_BackSpace) ?
1670 !(shft | ctrl) : (shft | ctrl)) ? '\b' : '\177');
1671 } else
1672 len = strlen(STRCPY(kbuf, rs_backspace_key));
1673 break;
1674 #endif
1675 #ifndef NO_DELETE_KEY
1676 case XK_Delete:
1677 len = strlen(STRCPY(kbuf, rs_delete_key));
1678 break;
1679 #endif
1680 case XK_Tab:
1681 if (shft) {
1682 len = 3;
1683 STRCPY(kbuf, "\033[Z");
1684 }
1685 break;
1686
1687 #ifdef XK_KP_Home
1688 case XK_KP_Home:
1689 /* allow shift to override */
1690 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1691 len = 3;
1692 STRCPY(kbuf, "\033Ow");
1693 break;
1694 }
1695 /* -> else FALL THROUGH */
1696 #endif
1697 case XK_Home:
1698 len = strlen(STRCPY(kbuf, KS_HOME));
1699 break;
1700
1701 #ifdef XK_KP_Left
1702 case XK_KP_Left: /* \033Ot or standard */
1703 case XK_KP_Up: /* \033Ox or standard */
1704 case XK_KP_Right: /* \033Ov or standard */
1705 case XK_KP_Down: /* \033Or or standard */
1706 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1707 len = 3;
1708 STRCPY(kbuf, "\033OZ");
1709 kbuf[2] = ("txvr"[keysym - XK_KP_Left]);
1710 break;
1711 } else {
1712 /* translate to std. cursor key */
1713 keysym = XK_Left + (keysym - XK_KP_Left);
1714 }
1715 /* FALL THROUGH */
1716 #endif
1717 case XK_Left: /* "\033[D" */
1718 case XK_Up: /* "\033[A" */
1719 case XK_Right: /* "\033[C" */
1720 case XK_Down: /* "\033[B" */
1721 len = 3;
1722 STRCPY(kbuf, "\033[@");
1723 kbuf[2] = ("DACB"[keysym - XK_Left]);
1724 /* do Shift first */
1725 if (shft) {
1726 kbuf[2] = ("dacb"[keysym - XK_Left]);
1727 } else if (ctrl) {
1728 kbuf[1] = 'O';
1729 kbuf[2] = ("dacb"[keysym - XK_Left]);
1730 } else if (PrivateModes & PrivMode_aplCUR) {
1731 kbuf[1] = 'O';
1732 }
1733 break;
1734
1735 #ifndef UNSHIFTED_SCROLLKEYS
1736 # ifdef XK_KP_Prior
1737 case XK_KP_Prior:
1738 /* allow shift to override */
1739 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1740 len = 3;
1741 STRCPY(kbuf, "\033Oy");
1742 break;
1743 }
1744 /* -> else FALL THROUGH */
1745 # endif
1746 case XK_Prior:
1747 len = 4;
1748 STRCPY(kbuf, "\033[5~");
1749 break;
1750 # ifdef XK_KP_Next
1751 case XK_KP_Next:
1752 /* allow shift to override */
1753 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1754 len = 3;
1755 STRCPY(kbuf, "\033Os");
1756 break;
1757 }
1758 /* -> else FALL THROUGH */
1759 # endif
1760 case XK_Next:
1761 len = 4;
1762 STRCPY(kbuf, "\033[6~");
1763 break;
1764 #endif
1765 #ifdef XK_KP_End
1766 case XK_KP_End:
1767 /* allow shift to override */
1768 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1769 len = 3;
1770 STRCPY(kbuf, "\033Oq");
1771 break;
1772 }
1773 /* -> else FALL THROUGH */
1774 #endif
1775 case XK_End:
1776 len = strlen(STRCPY(kbuf, KS_END));
1777 break;
1778
1779 case XK_Select:
1780 len = 4;
1781 STRCPY(kbuf, "\033[4~");
1782 break;
1783 #ifdef DXK_Remove /* support for DEC remove like key */
1784 case DXK_Remove: /* drop */
1785 #endif
1786 case XK_Execute:
1787 len = 4;
1788 STRCPY(kbuf, "\033[3~");
1789 break;
1790 case XK_Insert:
1791 len = 4;
1792 STRCPY(kbuf, "\033[2~");
1793 break;
1794
1795 case XK_Menu:
1796 len = 5;
1797 STRCPY(kbuf, "\033[29~");
1798 break;
1799 case XK_Find:
1800 len = 4;
1801 STRCPY(kbuf, "\033[1~");
1802 break;
1803 case XK_Help:
1804 len = 5;
1805 STRCPY(kbuf, "\033[28~");
1806 break;
1807
1808 case XK_KP_Enter:
1809 /* allow shift to override */
1810 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1811 len = 3;
1812 STRCPY(kbuf, "\033OM");
1813 } else {
1814 len = 1;
1815 kbuf[0] = '\r';
1816 }
1817 break;
1818
1819 #ifdef XK_KP_Begin
1820 case XK_KP_Begin:
1821 len = 3;
1822 STRCPY(kbuf, "\033Ou");
1823 break;
1824
1825 case XK_KP_Insert:
1826 len = 3;
1827 STRCPY(kbuf, "\033Op");
1828 break;
1829
1830 case XK_KP_Delete:
1831 len = 3;
1832 STRCPY(kbuf, "\033On");
1833 break;
1834 #endif
1835
1836 case XK_KP_F1: /* "\033OP" */
1837 case XK_KP_F2: /* "\033OQ" */
1838 case XK_KP_F3: /* "\033OR" */
1839 case XK_KP_F4: /* "\033OS" */
1840 len = 3;
1841 STRCPY(kbuf, "\033OP");
1842 kbuf[2] += (keysym - XK_KP_F1);
1843 break;
1844
1845 case XK_KP_Multiply: /* "\033Oj" : "*" */
1846 case XK_KP_Add: /* "\033Ok" : "+" */
1847 case XK_KP_Separator: /* "\033Ol" : "," */
1848 case XK_KP_Subtract: /* "\033Om" : "-" */
1849 case XK_KP_Decimal: /* "\033On" : "." */
1850 case XK_KP_Divide: /* "\033Oo" : "/" */
1851 case XK_KP_0: /* "\033Op" : "0" */
1852 case XK_KP_1: /* "\033Oq" : "1" */
1853 case XK_KP_2: /* "\033Or" : "2" */
1854 case XK_KP_3: /* "\033Os" : "3" */
1855 case XK_KP_4: /* "\033Ot" : "4" */
1856 case XK_KP_5: /* "\033Ou" : "5" */
1857 case XK_KP_6: /* "\033Ov" : "6" */
1858 case XK_KP_7: /* "\033Ow" : "7" */
1859 case XK_KP_8: /* "\033Ox" : "8" */
1860 case XK_KP_9: /* "\033Oy" : "9" */
1861 /* allow shift to override */
1862 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1863 len = 3;
1864 STRCPY(kbuf, "\033Oj");
1865 kbuf[2] += (keysym - XK_KP_Multiply);
1866 } else {
1867 len = 1;
1868 kbuf[0] = ('*' + (keysym - XK_KP_Multiply));
1869 }
1870 break;
1871
1872 case XK_F1: /* "\033OP" */
1873 case XK_F2: /* "\033OQ" */
1874 case XK_F3: /* "\033OR" */
1875 case XK_F4: /* "\033OS" */
1876 len = 3;
1877 STRCPY(kbuf, "\033OP");
1878 kbuf[2] += (keysym - XK_F1);
1879 break;
1880
1881 #define FKEY(n, fkey) \
1882 len = 5; \
1883 sprintf((char *) kbuf,"\033[%02d~", (int)((n) + (keysym - fkey)))
1884 #if 0 /* old style keymappings : */
1885 case XK_F1: /* "\033[11~" */
1886 case XK_F2: /* "\033[12~" */
1887 case XK_F3: /* "\033[13~" */
1888 case XK_F4: /* "\033[14~" */
1889 FKEY(11, XK_F1);
1890 break;
1891 #endif
1892 case XK_F5: /* "\033[15~" */
1893 FKEY(15, XK_F5);
1894 break;
1895
1896 case XK_F6: /* "\033[17~" */
1897 case XK_F7: /* "\033[18~" */
1898 case XK_F8: /* "\033[19~" */
1899 case XK_F9: /* "\033[20~" */
1900 case XK_F10: /* "\033[21~" */
1901 FKEY(17, XK_F6);
1902 break;
1903
1904 case XK_F11: /* "\033[23~" */
1905 case XK_F12: /* "\033[24~" */
1906 case XK_F13: /* "\033[25~" */
1907 case XK_F14: /* "\033[26~" */
1908 FKEY(23, XK_F11);
1909 break;
1910
1911 case XK_F15: /* "\033[28~" */
1912 case XK_F16: /* "\033[29~" */
1913 FKEY(28, XK_F15);
1914 break;
1915
1916 case XK_F17: /* "\033[31~" */
1917 case XK_F18: /* "\033[32~" */
1918 case XK_F19: /* "\033[33~" */
1919 case XK_F20: /* "\033[34~" */
1920 case XK_F21: /* "\033[35~" */
1921 case XK_F22: /* "\033[36~" */
1922 case XK_F23: /* "\033[37~" */
1923 case XK_F24: /* "\033[38~" */
1924 case XK_F25: /* "\033[39~" */
1925 case XK_F26: /* "\033[40~" */
1926 case XK_F27: /* "\033[41~" */
1927 case XK_F28: /* "\033[42~" */
1928 case XK_F29: /* "\033[43~" */
1929 case XK_F30: /* "\033[44~" */
1930 case XK_F31: /* "\033[45~" */
1931 case XK_F32: /* "\033[46~" */
1932 case XK_F33: /* "\033[47~" */
1933 case XK_F34: /* "\033[48~" */
1934 case XK_F35: /* "\033[49~" */
1935 FKEY(31, XK_F17);
1936 break;
1937 #undef FKEY
1938 }
1939 /*
1940 * Pass meta for all function keys, if 'meta' option set
1941 */
1942 #ifdef META8_OPTION
1943 if (meta && (meta_char == 0x80) && len > 0) {
1944 kbuf[len - 1] |= 0x80;
1945 }
1946 #endif
1947 } else if (ctrl && keysym == XK_minus) {
1948 len = 1;
1949 kbuf[0] = '\037'; /* Ctrl-Minus generates ^_ (31) */
1950 } else {
1951 #ifdef META8_OPTION
1952 /* set 8-bit on */
1953 if (meta && (meta_char == 0x80)) {
1954 unsigned char *ch;
1955
1956 for (ch = kbuf; ch < kbuf + len; ch++)
1957 *ch |= 0x80;
1958 meta = 0;
1959 }
1960 #endif
1961 #ifdef GREEK_SUPPORT
1962 if (greek_mode)
1963 len = greek_xlat(kbuf, len);
1964 #endif
1965 /* nil */ ;
1966 }
1967
1968 if (len <= 0)
1969 return; /* not mapped */
1970
1971 /*
1972 * these modifications only affect the static keybuffer
1973 * pass Shift/Control indicators for function keys ending with `~'
1974 *
1975 * eg,
1976 * Prior = "ESC[5~"
1977 * Shift+Prior = "ESC[5~"
1978 * Ctrl+Prior = "ESC[5^"
1979 * Ctrl+Shift+Prior = "ESC[5@"
1980 * Meta adds an Escape prefix (with META8_OPTION, if meta == <escape>).
1981 */
1982 if (kbuf[0] == '\033' && kbuf[1] == '[' && kbuf[len - 1] == '~')
1983 kbuf[len - 1] = (shft ? (ctrl ? '@' : '$') : (ctrl ? '^' : '~'));
1984
1985 /* escape prefix */
1986 if (meta
1987 #ifdef META8_OPTION
1988 && (meta_char == 033)
1989 #endif
1990 ) {
1991 const unsigned char ch = '\033';
1992
1993 tt_write(&ch, 1);
1994 }
1995 #ifdef DEBUG_CMD
1996 if (debug_key) { /* Display keyboard buffer contents */
1997 char *p;
1998 int i;
1999
2000 fprintf(stderr, "key 0x%04X [%d]: `", (unsigned int)keysym, len);
2001 for (i = 0, p = kbuf; i < len; i++, p++)
2002 fprintf(stderr, (*p >= ' ' && *p < '\177' ? "%c" : "\\%03o"), *p);
2003 fprintf(stderr, "'\n");
2004 }
2005 #endif /* DEBUG_CMD */
2006 tt_write(kbuf, len);
2007 }
2008 /*}}} */
2009
2010 #if (MENUBAR_MAX)
2011 /*{{{ cmd_write(), cmd_getc() */
2012 /* attempt to `write' COUNT to the input buffer */
2013 /* PROTO */
2014 unsigned int
2015 cmd_write(const unsigned char *str, unsigned int count)
2016 {
2017 int n;
2018
2019 n = (count - (cmdbuf_ptr - cmdbuf_base));
2020 /* need to insert more chars that space available in the front */
2021 if (n > 0) {
2022 /* try and get more space from the end */
2023 unsigned char *src, *dst;
2024
2025 dst = (cmdbuf_base + sizeof(cmdbuf_base) - 1); /* max pointer */
2026
2027 if ((cmdbuf_ptr + n) > dst)
2028 n = (dst - cmdbuf_ptr); /* max # chars to insert */
2029
2030 if ((cmdbuf_endp + n) > dst)
2031 cmdbuf_endp = (dst - n); /* truncate end if needed */
2032
2033 /* equiv: memmove ((cmdbuf_ptr+n), cmdbuf_ptr, n); */
2034 src = cmdbuf_endp;
2035 dst = src + n;
2036 /* FIXME: anything special to avoid possible pointer wrap? */
2037 while (src >= cmdbuf_ptr)
2038 *dst-- = *src--;
2039
2040 /* done */
2041 cmdbuf_ptr += n;
2042 cmdbuf_endp += n;
2043 }
2044 while (count-- && cmdbuf_ptr > cmdbuf_base) {
2045 /* sneak one in */
2046 cmdbuf_ptr--;
2047 *cmdbuf_ptr = str[count];
2048 }
2049
2050 return 0;
2051 }
2052 #endif /* MENUBAR_MAX */
2053 /* cmd_getc() - Return next input character */
2054 /*
2055 * Return the next input character after first passing any keyboard input
2056 * to the command.
2057 */
2058 /* PROTO */
2059 unsigned char
2060 cmd_getc(void)
2061 {
2062 #define TIMEOUT_USEC 5000
2063 static short refreshed = 0;
2064 fd_set readfds;
2065 int retval;
2066
2067 struct timeval value;
2068 #if 0
2069 #ifdef __CYGWIN32__
2070 struct timeval value;
2071 #else
2072 struct itimerval value;
2073 #endif
2074 #endif
2075
2076 /* If there have been a lot of new lines, then update the screen
2077 * What the heck I'll cheat and only refresh less than every page-full.
2078 * the number of pages between refreshes is refresh_limit, which
2079 * is incremented here because we must be doing flat-out scrolling.
2080 *
2081 * refreshing should be correct for small scrolls, because of the
2082 * time-out */
2083 if (refresh_count >= (refresh_limit * (TermWin.nrow - 1))) {
2084 if (refresh_limit < REFRESH_PERIOD)
2085 refresh_limit++;
2086 refresh_count = 0;
2087 refreshed = 1;
2088 scr_refresh(refresh_type);
2089 }
2090 /* characters already read in */
2091 if (cmdbuf_ptr < cmdbuf_endp)
2092 goto Return_Char;
2093
2094 for (;;) {
2095 struct timeval *timeout = &value;
2096
2097 if (v_bufstr < v_bufptr) /* output any pending chars */
2098 tt_write(NULL, 0);
2099 while (XPending(Xdisplay)) { /* process pending X events */
2100 refreshed = 0;
2101 #ifdef USE_XIM
2102 XProcessEvent(Xdisplay);
2103 #else
2104 {
2105 XEvent ev;
2106 XNextEvent(Xdisplay, &ev);
2107 /*fprintf( stderr, "%s:%d Received event %d\n", __FUNCTION__, __LINE__, ev.type );*/
2108 process_x_event(&ev);
2109 }
2110 #endif
2111 /* in case button actions pushed chars to cmdbuf */
2112 if (cmdbuf_ptr < cmdbuf_endp)
2113 goto Return_Char;
2114 }
2115 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
2116 if (scrollbar_isUp()) {
2117 if (!scroll_arrow_delay-- && scr_page(UP, 1)) {
2118 scroll_arrow_delay = SCROLLBAR_CONTINUOUS_DELAY;
2119 refreshed = 0;
2120 refresh_type |= SMOOTH_REFRESH;
2121 }
2122 } else if (scrollbar_isDn()) {
2123 if (!scroll_arrow_delay-- && scr_page(DN, 1)) {
2124 scroll_arrow_delay = SCROLLBAR_CONTINUOUS_DELAY;
2125 refreshed = 0;
2126 refresh_type |= SMOOTH_REFRESH;
2127 }
2128 }
2129 #endif /* NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING */
2130
2131 /* Nothing to do! */
2132 FD_ZERO(&readfds);
2133 FD_SET(cmd_fd, &readfds);
2134 FD_SET(Xfd, &readfds);
2135
2136 if( refreshed && last_update_background_request_sec == 0
2137 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
2138 && !(scrollbar_isUpDn())
2139 #endif
2140 )
2141 timeout = NULL ;
2142 else if( !refreshed )
2143 {
2144 timeout->tv_usec = TIMEOUT_USEC;
2145 timeout->tv_sec = 0;
2146 }else if( last_update_background_request_sec )
2147 {
2148 time_t curr_t_sec, curr_t_usec, wait_usec = 0 ;
2149 timer_get_time (&curr_t_sec, &curr_t_usec);
2150 if( curr_t_sec == last_update_background_request_sec )
2151 {
2152 if( curr_t_usec < last_update_background_request_usec )
2153 wait_usec = last_update_background_request_usec - curr_t_usec ;
2154 }else if( curr_t_sec < last_update_background_request_sec )
2155 {
2156 wait_usec = (last_update_background_request_sec - curr_t_sec)*1000000 ;
2157 wait_usec += last_update_background_request_usec ;
2158 wait_usec -= curr_t_usec ;
2159 }
2160 if( wait_usec == 0 )
2161 wait_usec = TIMEOUT_USEC ;
2162 timeout->tv_usec = wait_usec;
2163 timeout->tv_sec = 0 ; /*UPDATE_BACKGROUND_TIMEOUT_SEC;*/
2164 }
2165 /*fprintf( stderr, "%s:%d select(%d, cmd_fd = %d, Xfd = %d, %p)...", __FUNCTION__, __LINE__, num_fds, cmd_fd,Xfd ,timeout ); */
2166 retval = select(max(cmd_fd,Xfd)+1, &readfds, NULL, NULL, timeout );
2167 /* fprintf( stderr, "Done(retval = %d).\n", retval); */
2168 /* See if we can read from the application */
2169 if (FD_ISSET(cmd_fd, &readfds)) {
2170 int n;
2171 unsigned int count;
2172
2173 cmdbuf_ptr = cmdbuf_endp = cmdbuf_base;
2174 for (count = BUFSIZ; count; count -= n, cmdbuf_endp += n)
2175 if ((n = read(cmd_fd, cmdbuf_endp, count)) > 0)
2176 continue;
2177 else if (n == 0 || (n < 0 && errno == EAGAIN))
2178 break;
2179 else {
2180 #if !defined(HAVE_ATEXIT) && !defined(__sun__)
2181 clean_exit();
2182 #endif
2183 exit(EXIT_SUCCESS);
2184 }
2185 /* some characters read in */
2186 if (count != BUFSIZ)
2187 goto Return_Char;
2188 }
2189 /* select statement timed out - better update the screen */
2190 if (retval == 0)
2191 {
2192 refresh_count = 0;
2193 refresh_limit = 1;
2194 if( last_update_background_request_sec > 0 )
2195 {
2196 time_t curr_t_sec, curr_t_usec;
2197 timer_get_time (&curr_t_sec, &curr_t_usec);
2198 if( ( last_update_background_request_sec == curr_t_sec &&
2199 last_update_background_request_usec < curr_t_usec) ||
2200 last_update_background_request_sec < curr_t_sec ||
2201 last_update_background_request_sec-1 > curr_t_sec )
2202 {
2203 /* TODO update background pixmap */
2204 RenderPixmap(1);
2205 refresh_transparent_scrollbar();
2206 scr_clear();
2207 scr_touch();
2208 refreshed = True ;
2209 }
2210 }
2211 if (!refreshed)
2212 {
2213 refreshed = 1;
2214 scr_refresh(refresh_type);
2215 scrollbar_show(1);
2216 #ifdef USE_XIM
2217 IMSendSpot();
2218 #endif
2219 }
2220 }
2221 }
2222 /* NOTREACHED */
2223 return 0;
2224
2225 Return_Char:
2226 refreshed = 0;
2227 return (*cmdbuf_ptr++);
2228 }
2229 /*}}} */
2230
2231 /*
2232 * the 'essential' information for reporting Mouse Events
2233 * pared down from XButtonEvent
2234 */
2235 static struct {
2236 int clicks;
2237 Time time; /* milliseconds */
2238 unsigned int state; /* key or button mask */
2239 unsigned int button; /* detail */
2240 } MEvent = {
2241 0, CurrentTime, 0, AnyButton
2242 };
2243
2244 /* PROTO */
2245 void
2246 mouse_report(XButtonEvent * ev)
2247 {
2248 int button_number, key_state = 0;
2249 int x, y;
2250
2251 x = ev->x;
2252 y = ev->y;
2253 pixel_position(&x, &y);
2254
2255 button_number = ((MEvent.button == AnyButton) ? 3 :
2256 (MEvent.button - Button1));
2257
2258 if (PrivateModes & PrivMode_MouseX10) {
2259 /*
2260 * do not report ButtonRelease
2261 * no state info allowed
2262 */
2263 key_state = 0;
2264 if (button_number == 3)
2265 return;
2266 } else {
2267 /* let's be explicit here ... from <X11/X.h>
2268 * #define ShiftMask (1<<0)
2269 * #define ControlMask (1<<2)
2270 * #define Mod1Mask (1<<3)
2271 *
2272 * and XTerm mouse reporting needs these values:
2273 * 4 = Shift
2274 * 8 = Meta
2275 * 16 = Control
2276 * plus will add in our own Double-Click reporting
2277 * 32 = Double Click
2278 */
2279 key_state = (((MEvent.state & (ShiftMask | ControlMask))
2280 + ((MEvent.state & Mod1Mask) ? 2 : 0)
2281 #ifdef MOUSE_REPORT_DOUBLECLICK
2282 + (MEvent.clicks > 1 ? 8 : 0)
2283 #endif
2284 ) << 2);
2285 /* Report mouse wheel events. */
2286 if (ev->button == Button4 || ev->button == Button5) {
2287 key_state |= 1 << 6;
2288 button_number = ev->button - Button4;
2289 }
2290 }
2291
2292 #ifdef DEBUG_MOUSEREPORT
2293 fprintf(stderr, "Mouse [");
2294 if (key_state & 16)
2295 fputc('C', stderr);
2296 if (key_state & 4)
2297 fputc('S', stderr);
2298 if (key_state & 2)
2299 fputc('A', stderr);
2300 if (key_state & 32)
2301 fputc('2', stderr);
2302 fprintf(stderr, "]: <%d>, %d/%d\n",
2303 button_number,
2304 x + 1,
2305 y + 1);
2306 #else
2307 tt_printf((unsigned char *) "\033[M%c%c%c",
2308 (32 + button_number + key_state),
2309 (32 + x + 1),
2310 (32 + y + 1));
2311 #endif
2312 }
2313
2314 /*{{{ process an X event */
2315 /* PROTO */
2316 void
2317 process_x_event(XEvent * ev)
2318 {
2319 static int bypass_keystate = 0;
2320 int reportmode;
2321 static int csrO = 0; /* Hops - csr offset in thumb/slider */
2322
2323
2324 #if !defined(NO_DEBUG_OUTPUT)
2325 fprintf(stderr, "****************************************************************\n");
2326 fprintf(stderr, "%s:%s:%d(%ld)><<EVENT type(%d(%s))->x.window(%lx)->send_event(%d)\n", __FILE__, __FUNCTION__, __LINE__, time(NULL), ev->type, event_type2name(ev->type), ev->xany.window, ev->xany.send_event);
2327 #endif
2328
2329 /* to give proper Scroll behaviour */
2330 switch (ev->type) {
2331 case KeyPress:
2332 lookup_key(ev);
2333 break;
2334
2335 case DestroyNotify :
2336 if( ev->xdestroywindow.window == TermWin.vt || ev->xdestroywindow.window == TermWin.parent )
2337 {
2338 #ifdef __CYGWIN__
2339 /* cygwin does not kill shell properly */
2340 /* fprintf( stderr, "cmd_pid = %d\n", cmd_pid ); */
2341 kill( cmd_pid, SIGKILL );
2342 #endif
2343 exit(EXIT_SUCCESS);
2344 }
2345 break ;
2346
2347 case ClientMessage:
2348 if (ev->xclient.format == 32 && ev->xclient.data.l[0] == wmDeleteWindow)
2349 {
2350 #ifdef __CYGWIN__
2351 /* cygwin does not kill shell properly */
2352 /* fprintf( stderr, "cmd_pid = %d\n", cmd_pid ); */
2353 kill( cmd_pid, SIGKILL );
2354 #endif
2355 exit(EXIT_SUCCESS);
2356 }
2357 #ifdef OFFIX_DND
2358 /* OffiX Dnd (drag 'n' drop) protocol */
2359 if (ev->xclient.message_type == DndProtocol &&
2360 ((ev->xclient.data.l[0] == DndFile) ||
2361 (ev->xclient.data.l[0] == DndDir) ||
2362 (ev->xclient.data.l[0] == DndLink))) {
2363 /* Get Dnd data */
2364 Atom ActualType;
2365 int ActualFormat;
2366 unsigned char *data;
2367 unsigned long Size, RemainingBytes;
2368
2369 XGetWindowProperty(Xdisplay, Xroot,
2370 DndSelection,
2371 0L, 1000000L,
2372 False, AnyPropertyType,
2373 &ActualType, &ActualFormat,
2374 &Size, &RemainingBytes,
2375 &data);
2376 XChangeProperty(Xdisplay, Xroot,
2377 XA_CUT_BUFFER0, XA_STRING,
2378 8, PropModeReplace,
2379 data, strlen(data));
2380 selection_paste(Xroot, XA_CUT_BUFFER0, True);
2381 XSetInputFocus(Xdisplay, Xroot, RevertToNone, CurrentTime);
2382 }
2383 #endif /* OFFIX_DND */
2384 break;
2385
2386 case MappingNotify:
2387 XRefreshKeyboardMapping(&(ev->xmapping));
2388 break;
2389
2390 /* Here's my conclusiion:
2391 * If the window is completely unobscured, use bitblt's
2392 * to scroll. Even then, they're only used when doing partial
2393 * screen scrolling. When partially obscured, we have to fill
2394 * in the GraphicsExpose parts, which means that after each refresh,
2395 * we need to wait for the graphics expose or Noexpose events,
2396 * which ought to make things real slow!
2397 */
2398 case VisibilityNotify:
2399 switch (ev->xvisibility.state) {
2400 case VisibilityUnobscured:
2401 refresh_type = FAST_REFRESH;
2402 break;
2403
2404 case VisibilityPartiallyObscured:
2405 refresh_type = SLOW_REFRESH;
2406 break;
2407
2408 default:
2409 refresh_type = NO_REFRESH;
2410 break;
2411 }
2412 break;
2413
2414 case FocusIn:
2415 if (!TermWin.focus) {
2416 TermWin.focus = 1;
2417 #ifdef OFF_FOCUS_FADING
2418 if( rs_fade != NULL )
2419 {
2420 PixColors = &(PixColorsFocused[0]);
2421 on_colors_changed(Color_bg);
2422 }
2423 #endif
2424 #if !defined(USE_XIM) && !defined(NO_XLOCALE)
2425 if (Input_Context != NULL)
2426 XSetICFocus(Input_Context);
2427 #endif
2428 }
2429 break;
2430
2431 case FocusOut:
2432 if (TermWin.focus) {
2433 TermWin.focus = 0;
2434 #ifdef OFF_FOCUS_FADING
2435 if( rs_fade != NULL )
2436 {
2437 PixColors = &(PixColorsUnFocused[0]);
2438 on_colors_changed(Color_bg);
2439 }
2440 #endif
2441 #if !defined(USE_XIM) && !defined(NO_XLOCALE)
2442 if (Input_Context != NULL)
2443 XUnsetICFocus(Input_Context);
2444 #endif
2445 }
2446 break;
2447
2448 case ConfigureNotify:
2449 while( XCheckTypedWindowEvent( Xdisplay, ev->xconfigure.window, ConfigureNotify, ev ) );
2450 resize_window(ev);
2451 menubar_expose();
2452 break;
2453
2454 case SelectionClear:
2455 selection_clear();
2456 break;
2457
2458 case SelectionNotify:
2459 selection_paste(ev->xselection.requestor, ev->xselection.property, True);
2460 break;
2461
2462 case SelectionRequest:
2463 selection_send(&(ev->xselectionrequest));
2464 break;
2465
2466 case PropertyNotify:
2467 #ifdef DEBUG_BACKGROUND_PMAP
2468 {
2469 char *prop_name = XGetAtomName( Xdisplay, ev->xproperty.atom );
2470 fprintf( stderr, "PropertyNotify : %s(%lX)\n", prop_name, ev->xproperty.atom );
2471 if( prop_name )
2472 XFree( prop_name );
2473 }
2474 #endif
2475 if( ev->xproperty.window == Xroot )
2476 {
2477 #ifdef HAVE_AFTERSTEP
2478 if (ev->xproperty.atom == _AS_STYLE )
2479 {
2480 const char *mystyle_name ;
2481 if( rs_mystyle )
2482 mystyle_name = rs_mystyle ;
2483 else
2484 mystyle_name = GetDefaultMyStyle() ;
2485
2486 mystyle_handle_property_event( ev );
2487 set_mystyle( mystyle_find( mystyle_name ), True );
2488 break;
2489 }else if( ev->xproperty.atom == _XROOTPMAP_ID )
2490 {
2491 if( TermWin.background.trgType == BGT_MyStyle )
2492 {
2493 if( !TransparentMS(TermWin.background.mystyle) )
2494 {
2495 #ifdef DEBUG_BACKGROUND_PMAP
2496 fprintf( stderr, "texture type = %d\n", TermWin.background.mystyle->texture_type );
2497 #endif
2498 break ;
2499 }
2500 destroy_asimage( &Scr.RootImage );
2501 }
2502 }
2503 #endif
2504 #ifdef TRANSPARENT
2505 if( ev->xproperty.atom == _XROOTPMAP_ID )
2506 {
2507 if( IsTransparentPixmap() )
2508 {
2509 Pixmap p;
2510 if( read_32bit_property (Xroot, _XROOTPMAP_ID, &p) )
2511 {
2512 if( p != TermWin.background.srcPixmap )
2513 SetSrcPixmap( p );
2514 }else
2515 ValidateSrcPixmap(True);
2516
2517 #ifdef DEBUG_BACKGROUND_PMAP
2518 fprintf( stderr, "root pmap changed to = %lX\n", TermWin.background.srcPixmap );
2519 #endif
2520
2521 if( TransparentPixmapNeedsUpdate() )
2522 request_background_update();
2523 }else if( get_flags(Options, Opt_transparent) )
2524 {
2525 scr_clear();
2526 scr_touch();
2527 }
2528 }
2529 #endif
2530 if( ev->xproperty.atom == _XA_NET_SUPPORTING_WM_CHECK )
2531 {
2532 check_extended_wm_hints_support();
2533 #ifdef DEBUG_BACKGROUND_PMAP
2534 fprintf( stderr, "WM change. Desktops are %ssupported\n", get_flags( ExtWM.flags, WM_SupportsDesktops )?"":"not " );
2535 #endif
2536 }else if( ev->xproperty.atom == _XA_NET_CURRENT_DESKTOP )
2537 {
2538 if( !read_32bit_property (Xroot, _XA_NET_CURRENT_DESKTOP, &ExtWM.current_desktop) )
2539 clear_flags( ExtWM.flags, WM_SupportsDesktops );
2540 else if( get_flags( ExtWM.flags, WM_ClaimSupportsDesktops ) )
2541 set_flags( ExtWM.flags, WM_SupportsDesktops );
2542
2543
2544 #ifdef DEBUG_BACKGROUND_PMAP
2545 fprintf( stderr, "Curr Desk change to %ld. Desktops are %ssupported\n", ExtWM.current_desktop, get_flags( ExtWM.flags, WM_SupportsDesktops )?"":"not " );
2546 #endif
2547 /* Don't do it here - wait for background change :
2548 * if( TransparentPixmapNeedsUpdate() )
2549 request_background_update();
2550 */
2551 }
2552 }
2553 if( ev->xproperty.window == TermWin.parent )
2554 {
2555 if( ev->xproperty.atom == _XA_NET_WM_DESKTOP )
2556 {
2557 #ifdef DEBUG_BACKGROUND_PMAP
2558 fprintf( stderr, "Aterm Desk change from %ld. Desktops are %ssupported\n", ExtWM.aterm_desktop, get_flags( ExtWM.flags, WM_SupportsDesktops )?"":"not " );
2559 #endif
2560 if( !read_32bit_property (TermWin.parent, _XA_NET_WM_DESKTOP, &ExtWM.aterm_desktop) )
2561 clear_flags( ExtWM.flags, WM_SupportsDesktops );
2562 #ifdef DEBUG_BACKGROUND_PMAP
2563 fprintf( stderr, "Aterm Desk change to %ld. Desktops are %ssupported\n", ExtWM.aterm_desktop, get_flags( ExtWM.flags, WM_SupportsDesktops )?"":"not " );
2564 #endif
2565 if( TransparentPixmapNeedsUpdate() )
2566 request_background_update();
2567 }else if( ev->xproperty.atom == _XA_NET_WM_STATE )
2568 {
2569 if( check_extended_wm_state() )
2570 if( TransparentPixmapNeedsUpdate() )
2571 request_background_update();
2572 #ifdef DEBUG_BACKGROUND_PMAP
2573 fprintf( stderr, "Aterm state change to: %sSticky, %sShaded, %sHidden\n",
2574 get_flags( ExtWM.flags, WM_AtermStateSticky )?"":"not ",
2575 get_flags( ExtWM.flags, WM_AtermStateShaded )?"":"not ",
2576 get_flags( ExtWM.flags, WM_AtermStateHidden )?"":"not " );
2577 #endif
2578
2579 }
2580 }
2581 break;
2582 case UnmapNotify:
2583 TermWin.bMapped = 0 ;
2584 /* fprintf(stderr, "\n aterm is UnMapped(event)");*/
2585 break;
2586
2587 case MapNotify:
2588 /* {
2589 XWindowAttributes attr ;
2590
2591 XGetWindowAttributes( Xdisplay, ParentWin[0], &attr );
2592 TermWin.bMapped = (attr.map_state == IsViewable)?1:0 ;
2593
2594 fprintf(stderr, "\n aterm is %s", (TermWin.bMapped)?"Mapped":"UnMapped");
2595 }*/
2596 /* fprintf(stderr, "\n aterm is %s", (TermWin.bMapped)?"Mapped":"UnMapped");*/
2597 TermWin.bMapped = 1 ;
2598 #ifdef TRANSPARENT
2599 if ( TransparentPixmapNeedsUpdate() )
2600 request_background_update();
2601 #endif
2602 #if 0
2603 refresh_transparent_scrollbar();
2604 if( Options & Opt_transparent)
2605 {
2606 Pixmap tmp = GetRootPixmap(None);
2607 if( tmp != TermWin.background.srcPixmap && TermWin.background.srcPixmap != None )
2608 {
2609 if( TermWin.background.trgType != BGT_None )
2610 {
2611 SetSrcPixmap(tmp);
2612 RenderPixmap(0);
2613 }
2614 scr_clear();
2615 scr_touch();
2616 }
2617 }
2618 #endif
2619 break ;
2620 case ReparentNotify:
2621 #ifdef TRANSPARENT
2622 {
2623 int n;
2624 XWindowAttributes attr ;
2625 int (*oldXErrorHandler) (Display *, XErrorEvent *) =
2626 XSetErrorHandler (pixmap_error_handler);
2627
2628 for( n = 1 ; n < PARENTS_NUM ; n ++ ) ParentWin[n] = None;
2629 /*
2630 * Make the frame window set by the window manager have
2631 * the root background. Some window managers put few nested frame
2632 * windows for each client, so we have to take care about that.
2633 */
2634 for( ParentWinNum = 1 ;ParentWinNum<PARENTS_NUM; ParentWinNum++)
2635 {
2636 Window root;
2637 Window parent;
2638 Window *list;
2639
2640 XQueryTree(Xdisplay, ParentWin[ParentWinNum-1], &root, &parent, &list, &n);
2641 XFree(list);
2642 if (parent == Xroot) break;
2643 ParentWin[ParentWinNum] = parent;
2644 /* we want to get all map/unmap events from this window as well*/
2645 /* XSelectInput(Xdisplay, parent, StructureNotifyMask); */
2646
2647 if (Options & Opt_transparent)
2648 XSetWindowBackgroundPixmap(Xdisplay, parent, ParentRelative);
2649 }
2650 if( XGetWindowAttributes( Xdisplay, ParentWin[0], &attr ))
2651 TermWin.bMapped = (attr.map_state == IsViewable)?1:0 ;
2652
2653 XSetErrorHandler (oldXErrorHandler);
2654 scr_clear();
2655 }
2656 #endif /* TRANSPARENT */
2657 break;
2658 case GraphicsExpose:
2659 case Expose:
2660 if (ev->xany.window == TermWin.vt) {
2661 #if !defined(NO_DEBUG_OUTPUT)
2662 fprintf(stderr, "exposed area = %dx%d%+d%+d\n", ev->xexpose.x, ev->xexpose.y,
2663 ev->xexpose.width, ev->xexpose.height);
2664 #endif
2665 scr_expose(ev->xexpose.x, ev->xexpose.y,
2666 ev->xexpose.width, ev->xexpose.height);
2667 } else {
2668 XEvent unused_xevent;
2669
2670 while (XCheckTypedWindowEvent(Xdisplay, ev->xany.window,
2671 Expose,
2672 &unused_xevent)) ;
2673 while (XCheckTypedWindowEvent(Xdisplay, ev->xany.window,
2674 GraphicsExpose,
2675 &unused_xevent)) ;
2676 if (isScrollbarWindow(ev->xany.window)) {
2677 scrollbar_setNone();
2678 scrollbar_show(0);
2679 }
2680 if (menubar_visible() && isMenuBarWindow(ev->xany.window))
2681 menubar_expose();
2682 Gr_expose(ev->xany.window);
2683 }
2684 break;
2685
2686 case ButtonPress:
2687 bypass_keystate = (ev->xbutton.state & (Mod1Mask | ShiftMask));
2688 reportmode = (bypass_keystate ?
2689 0 : (PrivateModes & PrivMode_mouse_report));
2690
2691 if (ev->xany.window == TermWin.vt) {
2692 if (ev->xbutton.subwindow != None)
2693 Gr_ButtonPress(ev->xbutton.x, ev->xbutton.y);
2694 else {
2695 if (reportmode) {
2696 /* mouse report from vt window */
2697 /* save the xbutton state (for ButtonRelease) */
2698 MEvent.state = ev->xbutton.state;
2699 #ifdef MOUSE_REPORT_DOUBLECLICK
2700 if (ev->xbutton.button == MEvent.button
2701 && (ev->xbutton.time - MEvent.time < MULTICLICK_TIME)) {
2702 /* same button, within alloted time */
2703 MEvent.clicks++;
2704 if (MEvent.clicks > 1) {
2705 /* only report double clicks */
2706 MEvent.clicks = 2;
2707 mouse_report(&(ev->xbutton));
2708
2709 /* don't report the release */
2710 MEvent.clicks = 0;
2711 MEvent.button = AnyButton;
2712 }
2713 } else {
2714 /* different button, or time expired */
2715 MEvent.clicks = 1;
2716 MEvent.button = ev->xbutton.button;
2717 mouse_report(&(ev->xbutton));
2718 }
2719 #else
2720 MEvent.button = ev->xbutton.button;
2721 mouse_report(&(ev->xbutton));
2722 #endif /* MOUSE_REPORT_DOUBLECLICK */
2723 } else {
2724 if (ev->xbutton.button != MEvent.button)
2725 MEvent.clicks = 0;
2726 switch (ev->xbutton.button) {
2727 case Button1:
2728 if (MEvent.button == Button1
2729 && (ev->xbutton.time - MEvent.time < MULTICLICK_TIME))
2730 MEvent.clicks++;
2731 else
2732 MEvent.clicks = 1;
2733 selection_click(MEvent.clicks, ev->xbutton.x,
2734 ev->xbutton.y);
2735 MEvent.button = Button1;
2736 break;
2737
2738 case Button3:
2739 if (MEvent.button == Button3
2740 && (ev->xbutton.time - MEvent.time < MULTICLICK_TIME))
2741 selection_rotate(ev->xbutton.x, ev->xbutton.y);
2742 else
2743 selection_extend(ev->xbutton.x, ev->xbutton.y, 1);
2744 MEvent.button = Button3;
2745 break;
2746 }
2747 }
2748 MEvent.time = ev->xbutton.time;
2749 return;
2750 }
2751 }
2752 if (isScrollbarWindow(ev->xany.window)) {
2753 scrollbar_setNone();
2754 /*
2755 * Rxvt-style scrollbar:
2756 * move up if mouse is above slider
2757 * move dn if mouse is below slider
2758 *
2759 * XTerm-style scrollbar:
2760 * Move display proportional to pointer location
2761 * pointer near top -> scroll one line
2762 * pointer near bot -> scroll full page
2763 */
2764 #ifndef NO_SCROLLBAR_REPORT
2765 if (reportmode) {
2766 /*
2767 * Mouse report disabled scrollbar:
2768 * arrow buttons - send up/down
2769 * click on scrollbar - send pageup/down
2770 */
2771 if (scrollbar_upButton(ev->xbutton.y))
2772 tt_printf((unsigned char *) "\033[A");
2773 else if (scrollbar_dnButton(ev->xbutton.y))
2774 tt_printf((unsigned char *) "\033[B");
2775 else
2776 switch (ev->xbutton.button) {
2777 case Button2:
2778 tt_printf((unsigned char *) "\014");
2779 break;
2780 case Button1:
2781 tt_printf((unsigned char *) "\033[6~");
2782 break;
2783 case Button3:
2784 tt_printf((unsigned char *) "\033[5~");
2785 break;
2786 }
2787 } else
2788 #endif /* NO_SCROLLBAR_REPORT */
2789 {
2790 if (scrollbar_upButton(ev->xbutton.y)) {
2791 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
2792 scroll_arrow_delay = SCROLLBAR_INITIAL_DELAY;
2793 #endif
2794 if (scr_page(UP, 1))
2795 scrollbar_setUp();
2796 } else if (scrollbar_dnButton(ev->xbutton.y)) {
2797 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
2798 scroll_arrow_delay = SCROLLBAR_INITIAL_DELAY;
2799 #endif
2800 if (scr_page(DN, 1))
2801 scrollbar_setDn();
2802 } else
2803 switch (ev->xbutton.button) {
2804 case Button2:
2805 #ifndef FUNKY_SCROLL_BEHAVIOUR
2806 csrO = (scrollBar.bot - scrollBar.top) / 2;
2807 /* align to thumb center */
2808 #else
2809 # ifndef XTERM_SCROLLBAR
2810 if (scrollbar_above_slider(ev->xbutton.y) ||
2811 scrollbar_below_slider(ev->xbutton.y))
2812 # endif
2813 #endif /* !FUNKY_SCROLL_BEHAVIOUR */
2814 scr_move_to(scrollbar_position(ev->xbutton.y) - csrO,
2815 scrollbar_size());
2816 scrollbar_setMotion();
2817 break;
2818
2819 case Button1:
2820 #ifndef FUNKY_SCROLL_BEHAVIOUR
2821 csrO = ev->xbutton.y - scrollBar.top;
2822 /* ptr ofset in thumb */
2823 #endif
2824 /*drop */
2825
2826 case Button3:
2827 #ifndef XTERM_SCROLLBAR
2828 if (scrollbar_above_slider(ev->xbutton.y))
2829 # ifdef RXVT_SCROLL_FULL
2830 scr_page(UP, TermWin.nrow - 1);
2831 # else
2832 scr_page(UP, TermWin.nrow / 4);
2833 # endif
2834 else if (scrollbar_below_slider(ev->xbutton.y))
2835 # ifdef RXVT_SCROLL_FULL
2836 scr_page(DN, TermWin.nrow - 1);
2837 # else
2838 scr_page(DN, TermWin.nrow / 4);
2839 # endif
2840 else
2841 scrollbar_setMotion();
2842 #else /* XTERM_SCROLLBAR */
2843 scr_page((ev->xbutton.button == Button1 ? DN : UP),
2844 (TermWin.nrow *
2845 scrollbar_position(ev->xbutton.y) /
2846 scrollbar_size())
2847 );
2848 #endif /* XTERM_SCROLLBAR */
2849 break;
2850 }
2851 }
2852 return;
2853 }
2854 if (isMenuBarWindow(ev->xany.window)) {
2855 menubar_control(&(ev->xbutton));
2856 return;
2857 }
2858 break;
2859
2860 case ButtonRelease:
2861 csrO = 0; /* reset csr Offset */
2862 reportmode = (bypass_keystate ?
2863 0 : (PrivateModes & PrivMode_mouse_report));
2864
2865 if (scrollbar_isUpDn()) {
2866 scrollbar_setNone();
2867 scrollbar_show(0);
2868 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
2869 refresh_type &= ~SMOOTH_REFRESH;
2870 #endif
2871 }
2872 if (ev->xany.window == TermWin.vt) {
2873 if (ev->xbutton.subwindow != None)
2874 Gr_ButtonRelease(ev->xbutton.x, ev->xbutton.y);
2875 else {
2876 if (reportmode) {
2877 /* Don't report release events for the mouse wheel */
2878 if (ev->xbutton.button == Button4 || ev->xbutton.button == Button5)
2879 return;
2880 /* mouse report from vt window */
2881 #ifdef MOUSE_REPORT_DOUBLECLICK
2882 /* only report the release of 'slow' single clicks */
2883 if (MEvent.button != AnyButton &&
2884 (ev->xbutton.button != MEvent.button ||
2885 (ev->xbutton.time - MEvent.time > MULTICLICK_TIME / 2))
2886 ) {
2887 MEvent.clicks = 0;
2888 MEvent.button = AnyButton;
2889 mouse_report(&(ev->xbutton));
2890 }
2891 #else /* MOUSE_REPORT_DOUBLECLICK */
2892 MEvent.button = AnyButton;
2893 mouse_report(&(ev->xbutton));
2894 #endif /* MOUSE_REPORT_DOUBLECLICK */
2895 return;
2896 }
2897 /*
2898 * dumb hack to compensate for the failure of click-and-drag
2899 * when overriding mouse reporting
2900 */
2901 if ((PrivateModes & PrivMode_mouse_report) &&
2902 (bypass_keystate) &&
2903 (ev->xbutton.button == Button1) &&
2904 (MEvent.clicks <= 1))
2905 selection_extend(ev->xbutton.x, ev->xbutton.y, 0);
2906
2907 switch (ev->xbutton.button) {
2908 case Button1:
2909 case Button3:
2910 selection_make(ev->xbutton.time, ev->xbutton.state);
2911 break;
2912
2913 case Button2:
2914 selection_request(ev->xbutton.time,
2915 ev->xbutton.x, ev->xbutton.y);
2916 break;
2917 #ifndef NO_MOUSE_WHEEL
2918 case Button4:
2919 scr_page(UP, (ev->xbutton.state & ShiftMask) ? 1 : 5);
2920 break;
2921 case Button5:
2922 scr_page(DN, (ev->xbutton.state & ShiftMask) ? 1 : 5);
2923 break;
2924 #endif
2925 }
2926 }
2927 } else if (isMenuBarWindow(ev->xany.window)) {
2928 menubar_control(&(ev->xbutton));
2929 }
2930 break;
2931
2932 case MotionNotify:
2933 if (isMenuBarWindow(ev->xany.window)) {
2934 menubar_control(&(ev->xbutton));
2935 break;
2936 }
2937 if ((PrivateModes & PrivMode_mouse_report) && !(bypass_keystate))
2938 break;
2939
2940 if (ev->xany.window == TermWin.vt) {
2941 if ((ev->xbutton.state & (Button1Mask | Button3Mask))) {
2942 Window unused_root, unused_child;
2943 int unused_root_x, unused_root_y;
2944 unsigned int unused_mask;
2945
2946 while (XCheckTypedWindowEvent(Xdisplay, TermWin.vt,
2947 MotionNotify, ev)) ;
2948 XQueryPointer(Xdisplay, TermWin.vt,
2949 &unused_root, &unused_child,
2950 &unused_root_x, &unused_root_y,
2951 &(ev->xbutton.x), &(ev->xbutton.y),
2952 &unused_mask);
2953 #ifdef MOUSE_THRESHOLD
2954 /* deal with a `jumpy' mouse */
2955 if ((ev->xmotion.time - MEvent.time) > MOUSE_THRESHOLD)
2956 #endif
2957 selection_extend((ev->xbutton.x), (ev->xbutton.y),
2958 (ev->xbutton.state & Button3Mask) ? 2 : 0);
2959 }
2960 } else if ((ev->xany.window == scrollBar.win) && scrollbar_isMotion()) {
2961 Window unused_root, unused_child;
2962 int unused_root_x, unused_root_y;
2963 unsigned int unused_mask;
2964
2965 while (XCheckTypedWindowEvent(Xdisplay, scrollBar.win,
2966 MotionNotify, ev)) ;
2967 XQueryPointer(Xdisplay, scrollBar.win,
2968 &unused_root, &unused_child,
2969 &unused_root_x, &unused_root_y,
2970 &(ev->xbutton.x), &(ev->xbutton.y),
2971 &unused_mask);
2972 scr_move_to(scrollbar_position(ev->xbutton.y) - csrO,
2973 scrollbar_size());
2974 scr_refresh(refresh_type);
2975 refresh_count = refresh_limit = 0;
2976 scrollbar_show(1);
2977 #ifdef USE_XIM
2978 IMSendSpot();
2979 #endif
2980 }
2981 break;
2982 }
2983 }
2984 /*}}} */
2985
2986 /*
2987 * Send printf() formatted output to the command.
2988 * Only use for small ammounts of data.
2989 */
2990 /* PROTO */
2991 void
2992 tt_printf(const unsigned char *fmt,...)
2993 {
2994 static unsigned char buf[TT_PRINTF_LIMIT];
2995 va_list arg_ptr;
2996
2997 va_start(arg_ptr, fmt);
2998 vsprintf((char *) buf, (char *) fmt, arg_ptr);
2999 va_end(arg_ptr);
3000 tt_write(buf, strlen(buf));
3001 }
3002
3003 /*}}} */
3004
3005 /*{{{ print pipe */
3006 /*----------------------------------------------------------------------*/
3007 #ifdef PRINTPIPE
3008 /* PROTO */
3009 FILE *
3010 popen_printer(void)
3011 {
3012 FILE *stream = popen(rs_print_pipe, "w");
3013
3014 if (stream == NULL)
3015 print_error("can't open printer pipe");
3016 return stream;
3017 }
3018
3019 /* PROTO */
3020 int
3021 pclose_printer(FILE * stream)
3022 {
3023 fflush(stream);
3024 /* pclose() reported not to work on SunOS 4.1.3 */
3025 #if defined (__sun__) /* TODO: RESOLVE THIS */
3026 /* pclose works provided SIGCHLD handler uses waitpid */
3027 return pclose(stream); /* return fclose (stream); */
3028 #else
3029 return pclose(stream);
3030 #endif
3031 }
3032
3033 /*
3034 * simulate attached vt100 printer
3035 */
3036 /* PROTO */
3037 void
3038 process_print_pipe(void)
3039 {
3040 int done;
3041 FILE *fd;
3042
3043 if ((fd = popen_printer()) == NULL)
3044 return;
3045
3046 /*
3047 * Send all input to the printer until either ESC[4i or ESC[?4i
3048 * is received.
3049 */
3050 for (done = 0; !done;) {
3051 unsigned char buf[8];
3052 unsigned char ch;
3053 unsigned int i, len;
3054
3055 if ((ch = cmd_getc()) != '\033') {
3056 if (putc(ch, fd) == EOF)
3057 break; /* done = 1 */
3058 } else {
3059 len = 0;
3060 buf[len++] = ch;
3061
3062 if ((buf[len++] = cmd_getc()) == '[') {
3063 if ((ch = cmd_getc()) == '?') {
3064 buf[len++] = '?';
3065 ch = cmd_getc();
3066 }
3067 if ((buf[len++] = ch) == '4') {
3068 if ((buf[len++] = cmd_getc()) == 'i')
3069 break; /* done = 1 */
3070 }
3071 }
3072 for (i = 0; i < len; i++)
3073 if (putc(buf[i], fd) == EOF) {
3074 done = 1;
3075 break;
3076 }
3077 }
3078 }
3079 pclose_printer(fd);
3080 }
3081 #endif /* PRINTPIPE */
3082 /*}}} */
3083
3084 /*{{{ process escape sequences */
3085 /* PROTO */
3086 void
3087 process_escape_seq(void)
3088 {
3089 unsigned char ch = cmd_getc();
3090
3091 switch (ch) {
3092 /* case 1: do_tek_mode (); break; */
3093 case '#':
3094 if (cmd_getc() == '8')
3095 scr_E();
3096 break;
3097 case '(':
3098 scr_charset_set(0, cmd_getc());
3099 break;
3100 case ')':
3101 scr_charset_set(1, cmd_getc());
3102 break;
3103 case '*':
3104 scr_charset_set(2, cmd_getc());
3105 break;
3106 case '+':
3107 scr_charset_set(3, cmd_getc());
3108 break;
3109 #ifdef MULTICHAR_SET
3110 case '$':
3111 scr_charset_set(-2, cmd_getc());
3112 break;
3113 #endif
3114 case '7':
3115 scr_cursor(SAVE);
3116 break;
3117 case '8':
3118 scr_cursor(RESTORE);
3119 break;
3120 case '=':
3121 case '>':
3122 PrivMode((ch == '='), PrivMode_aplKP);
3123 break;
3124 case '@':
3125 (void)cmd_getc();
3126 break;
3127 case 'D':
3128 scr_index(UP);
3129 break;
3130 case 'E':
3131 scr_add_lines((unsigned char *) "\n\r", 1, 2);
3132 break;
3133 case 'G':
3134 process_graphics();
3135 break;
3136 case 'H':
3137 scr_set_tab(1);
3138 break;
3139 case 'M':
3140 scr_index(DN);
3141 break;
3142 /*case 'N': scr_single_shift (2); break; */
3143 /*case 'O': scr_single_shift (3); break; */
3144 case 'Z':
3145 tt_printf((unsigned char *) ESCZ_ANSWER);
3146 break; /* steal obsolete ESC [ c */
3147 case '[':
3148 process_csi_seq();
3149 break;
3150 case ']':
3151 process_xterm_seq();
3152 break;
3153 case 'c':
3154 scr_poweron();
3155 break;
3156 case 'n':
3157 scr_charset_choose(2);
3158 break;
3159 case 'o':
3160 scr_charset_choose(3);
3161 break;
3162 }
3163 }
3164 /*}}} */
3165
3166 /*{{{ process CSI (code sequence introducer) sequences `ESC[' */
3167 /* PROTO */
3168 void
3169 process_csi_seq(void)
3170 {
3171 unsigned char ch, priv;
3172 unsigned int nargs;
3173 int arg[ESC_ARGS];
3174
3175 nargs = 0;
3176 arg[0] = 0;
3177 arg[1] = 0;
3178
3179 priv = 0;
3180 ch = cmd_getc();
3181 if (ch >= '<' && ch <= '?') {
3182 priv = ch;
3183 ch = cmd_getc();
3184 }
3185 /* read any numerical arguments */
3186 do {
3187 int n;
3188
3189 for (n = 0; isdigit(ch); ch = cmd_getc())
3190 n = n * 10 + (ch - '0');
3191
3192 if (nargs < ESC_ARGS)
3193 arg[nargs++] = n;
3194 if (ch == '\b') {
3195 scr_backspace();
3196 } else if (ch == 033) {
3197 process_escape_seq();
3198 return;
3199 } else if (ch < ' ') {
3200 scr_add_lines(&ch, 0, 1);
3201 return;
3202 }
3203 if (ch < '@')
3204 ch = cmd_getc();
3205 }
3206 while (ch >= ' ' && ch < '@');
3207 if (ch == 033) {
3208 process_escape_seq();
3209 return;
3210 } else if (ch < ' ')
3211 return;
3212
3213 switch (ch) {
3214 #ifdef PRINTPIPE
3215 case 'i': /* printing */
3216 switch (arg[0]) {
3217 case 0:
3218 scr_printscreen(0);
3219 break;
3220 case 5:
3221 process_print_pipe();
3222 break;
3223 }
3224 break;
3225 #endif
3226 case 'A':
3227 case 'e': /* up <n> */
3228 scr_gotorc((arg[0] ? -arg[0] : -1), 0, RELATIVE);
3229 break;
3230 case 'B': /* down <n> */
3231 scr_gotorc((arg[0] ? +arg[0] : +1), 0, RELATIVE);
3232 break;
3233 case 'C':
3234 case 'a': /* right <n> */
3235 scr_gotorc(0, (arg[0] ? +arg[0] : +1), RELATIVE);
3236 break;
3237 case 'D': /* left <n> */
3238 scr_gotorc(0, (arg[0] ? -arg[0] : -1), RELATIVE);
3239 break;
3240 case 'E': /* down <n> & to first column */
3241 scr_gotorc((arg[0] ? +arg[0] : +1), 0, R_RELATIVE);
3242 break;
3243 case 'F': /* up <n> & to first column */
3244 scr_gotorc((arg[0] ? -arg[0] : -1), 0, R_RELATIVE);
3245 break;
3246 case 'G':
3247 case '`': /* move to col <n> */
3248 scr_gotorc(0, (arg[0] ? arg[0] - 1 : +1), R_RELATIVE);
3249 break;
3250 case 'd': /* move to row <n> */
3251 scr_gotorc((arg[0] ? arg[0] - 1 : +1), 0, C_RELATIVE);
3252 break;
3253 case 'H':
3254 case 'f': /* position cursor */
3255 switch (nargs) {
3256 case 0:
3257 scr_gotorc(0, 0, 0);
3258 break;
3259 case 1:
3260 scr_gotorc((arg[0] ? arg[0] - 1 : 0), 0, 0);
3261 break;
3262 default:
3263 scr_gotorc(arg[0] - 1, arg[1] - 1, 0);
3264 break;
3265 }
3266 break;
3267 case 'I':
3268 scr_tab(arg[0] ? +arg[0] : +1);
3269 break;
3270 case 'Z':
3271 scr_tab(arg[0] ? -arg[0] : -1);
3272 break;
3273 case 'J':
3274 scr_erase_screen(arg[0]);
3275 break;
3276 case 'K':
3277 scr_erase_line(arg[0]);
3278 break;
3279 case '@':
3280 scr_insdel_chars((arg[0] ? arg[0] : 1), INSERT);
3281 break;
3282 case 'L':
3283 scr_insdel_lines((arg[0] ? arg[0] : 1), INSERT);
3284 break;
3285 case 'M':
3286 scr_insdel_lines((arg[0] ? arg[0] : 1), DELETE);
3287 break;
3288 case 'X':
3289 scr_insdel_chars((arg[0] ? arg[0] : 1), ERASE);
3290 break;
3291 case 'P':
3292 scr_insdel_chars((arg[0] ? arg[0] : 1), DELETE);
3293 break;
3294
3295 case 'c':
3296 tt_printf((unsigned char *) VT100_ANS);
3297 break;
3298 case 'm':
3299 process_sgr_mode(nargs, arg);
3300 break;
3301 case 'n': /* request for information */
3302 switch (arg[0]) {
3303 case 5:
3304 tt_printf((unsigned char *) "\033[0n");
3305 break; /* ready */
3306 case 6:
3307 scr_report_position();
3308 break;
3309 #if defined (ENABLE_DISPLAY_ANSWER)
3310 case 7:
3311 if( strlen(display_name) < TT_PRINTF_LIMIT-2 )
3312 tt_printf((unsigned char *) "%s\n", display_name);
3313 break;
3314 #endif
3315 case 8:
3316 xterm_seq(XTerm_title, APL_NAME "-" VERSION);
3317 break;
3318 }
3319 break;
3320 case 'r': /* set top and bottom margins */
3321 if (priv != '?') {
3322 if (nargs < 2 || arg[0] >= arg[1])
3323 scr_scroll_region(0, 10000);
3324 else
3325 scr_scroll_region(arg[0] - 1, arg[1] - 1);
3326 break;
3327 }
3328 /* drop */
3329 case 's':
3330 case 't':
3331 if(arg[0] == 21)
3332 tt_printf((unsigned char *) "\033]l%s\033\\", rs_title);
3333 break;
3334 case 'h':
3335 case 'l':
3336 process_terminal_mode(ch, priv, nargs, arg);
3337 break;
3338 case 'g':
3339 switch (arg[0]) {
3340 case 0:
3341 scr_set_tab(0);
3342 break; /* delete tab */
3343 case 3:
3344 scr_set_tab(-1);
3345 break; /* clear all tabs */
3346 }
3347 break;
3348 case 'W':
3349 switch (arg[0]) {
3350 case 0:
3351 scr_set_tab(1);
3352 break; /* = ESC H */
3353 case 2:
3354 scr_set_tab(0);
3355 break; /* = ESC [ 0 g */
3356 case 5:
3357 scr_set_tab(-1);
3358 break; /* = ESC [ 3 g */
3359 }
3360 break;
3361 }
3362 }
3363 /*}}} */
3364
3365 /*{{{ process xterm text parameters sequences `ESC ] Ps ; Pt BEL' */
3366 /* PROTO */
3367 void
3368 process_xterm_seq(void)
3369 {
3370 unsigned char ch, string[STRING_MAX];
3371 int arg;
3372
3373 ch = cmd_getc();
3374 for (arg = 0; isdigit(ch); ch = cmd_getc())
3375 arg = arg * 10 + (ch - '0');
3376
3377 if (ch == ';') {
3378 int n = 0;
3379
3380 while ((ch = cmd_getc()) != 007) {
3381 if (ch) {
3382 if (ch == '\t')
3383 ch = ' '; /* translate '\t' to space */
3384 else if (ch < ' ')
3385 return; /* control character - exit */
3386
3387 if (n < sizeof(string) - 1)
3388 string[n++] = ch;
3389 }
3390 }
3391 string[n] = '\0';
3392 /*
3393 * menubar_dispatch() violates the constness of the string,
3394 * so do it here
3395 */
3396 if (arg == XTerm_Menu)
3397 menubar_dispatch((char *) string);
3398 else
3399 xterm_seq(arg, (char *) string);
3400 }
3401 }
3402 /*}}} */
3403
3404 /*{{{ process DEC private mode sequences `ESC [ ? Ps mode' */
3405 /*
3406 * mode can only have the following values:
3407 * 'l' = low
3408 * 'h' = high
3409 * 's' = save
3410 * 'r' = restore
3411 * 't' = toggle
3412 * so no need for fancy checking
3413 */
3414 /* PROTO */
3415 void
3416 process_terminal_mode(int mode, int priv, unsigned int nargs, int arg[])
3417 {
3418 unsigned int i;
3419 int state;
3420
3421 if (nargs == 0)
3422 return;
3423
3424 /* make lo/hi boolean */
3425 switch (mode) {
3426 case 'l':
3427 mode = 0;
3428 break;
3429 case 'h':
3430 mode = 1;
3431 break;
3432 }
3433
3434 switch (priv) {
3435 case 0:
3436 if (mode && mode != 1)
3437 return; /* only do high/low */
3438 for (i = 0; i < nargs; i++)
3439 switch (arg[i]) {
3440 case 4:
3441 scr_insert_mode(mode);
3442 break;
3443 /* case 38: TEK mode */
3444 }
3445 break;
3446
3447 #define PrivCases(bit) \
3448 if (mode == 't') \
3449 state = !(PrivateModes & bit); \
3450 else \
3451 state = mode; \
3452 switch (state) { \
3453 case 's': \
3454 SavedModes |= (PrivateModes & bit); \
3455 continue; \
3456 break; \
3457 case 'r': \
3458 state = (SavedModes & bit) ? 1 : 0; \
3459 /* FALLTHROUGH */ \
3460 default: \
3461 PrivMode (state, bit); \
3462 }
3463
3464 case '?':
3465 for (i = 0; i < nargs; i++)
3466 switch (arg[i]) {
3467 case 1: /* application cursor keys */
3468 PrivCases(PrivMode_aplCUR);
3469 break;
3470
3471 /* case 2: - reset charsets to USASCII */
3472
3473 case 3: /* 80/132 */
3474 PrivCases(PrivMode_132);
3475 if (PrivateModes & PrivMode_132OK)
3476 set_width(state ? 132 : 80);
3477 break;
3478
3479 /* case 4: - smooth scrolling */
3480
3481 case 5: /* reverse video */
3482 PrivCases(PrivMode_rVideo);
3483 scr_rvideo_mode(state);
3484 break;
3485
3486 case 6: /* relative/absolute origins */
3487 PrivCases(PrivMode_relOrigin);
3488 scr_relative_origin(state);
3489 break;
3490
3491 case 7: /* autowrap */
3492 PrivCases(PrivMode_Autowrap);
3493 scr_autowrap(state);
3494 break;
3495
3496 /* case 8: - auto repeat, can't do on a per window basis */
3497
3498 case 9: /* X10 mouse reporting */
3499 PrivCases(PrivMode_MouseX10);
3500 /* orthogonal */
3501 if (PrivateModes & PrivMode_MouseX10)
3502 PrivateModes &= ~(PrivMode_MouseX11);
3503 break;
3504 # ifdef menuBar_esc
3505 case menuBar_esc:
3506 PrivCases(PrivMode_menuBar);
3507 map_menuBar(state);
3508 break;
3509 # endif
3510 #ifdef scrollBar_esc
3511 case scrollBar_esc:
3512 PrivCases(PrivMode_scrollBar);
3513 map_scrollBar(state);
3514 break;
3515 #endif
3516 case 25: /* visible/invisible cursor */
3517 PrivCases(PrivMode_VisibleCursor);
3518 scr_cursor_visible(state);
3519 break;
3520
3521 case 35:
3522 PrivCases(PrivMode_ShiftKeys);
3523 break;
3524
3525 case 40: /* 80 <--> 132 mode */
3526 PrivCases(PrivMode_132OK);
3527 break;
3528
3529 case 47: /* secondary screen */
3530 PrivCases(PrivMode_Screen);
3531 scr_change_screen(state);
3532 break;
3533
3534 case 66: /* application key pad */
3535 PrivCases(PrivMode_aplKP);
3536 break;
3537
3538 case 67:
3539 #ifndef NO_BACKSPACE_KEY
3540 if (PrivateModes & PrivMode_HaveBackSpace) {
3541 PrivCases(PrivMode_BackSpace);
3542 }
3543 #endif
3544 break;
3545
3546 case 1000: /* X11 mouse reporting */
3547 PrivCases(PrivMode_MouseX11);
3548 /* orthogonal */
3549 if (PrivateModes & PrivMode_MouseX11)
3550 PrivateModes &= ~(PrivMode_MouseX10);
3551 break;
3552 #if 0
3553 case 1001:
3554 break; /* X11 mouse highlighting */
3555 #endif
3556 case 1010: /* scroll to bottom on TTY output inhibit */
3557 PrivCases(PrivMode_TtyOutputInh);
3558 if (PrivateModes & PrivMode_TtyOutputInh)
3559 Options &= ~Opt_scrollTtyOutput;
3560 else
3561 Options |= Opt_scrollTtyOutput;
3562 break;
3563 case 1011: /* scroll to bottom on key press */
3564 PrivCases(PrivMode_Keypress);
3565 if (PrivateModes & PrivMode_Keypress)
3566 Options |= Opt_scrollKeypress;
3567 else
3568 Options &= ~Opt_scrollKeypress;
3569 break;
3570 }
3571 #undef PrivCases
3572 break;
3573 }
3574 }
3575 /*}}} */
3576
3577 /*{{{ process sgr sequences */
3578 /* PROTO */
3579 void
3580 process_sgr_mode(unsigned int nargs, int arg[])
3581 {
3582 unsigned int i;
3583
3584 if (nargs == 0) {
3585 scr_rendition(0, ~RS_None);
3586 return;
3587 }
3588 for (i = 0; i < nargs; i++)
3589 switch (arg[i]) {
3590 case 0:
3591 scr_rendition(0, ~RS_None);
3592 break;
3593 case 1:
3594 scr_rendition(1, RS_Bold);
3595 break;
3596 case 4:
3597 scr_rendition(1, RS_Uline);
3598 break;
3599 case 5:
3600 scr_rendition(1, RS_Blink);
3601 break;
3602 case 7:
3603 scr_rendition(1, RS_RVid);
3604 break;
3605 case 22:
3606 scr_rendition(0, RS_Bold);
3607 break;
3608 case 24:
3609 scr_rendition(0, RS_Uline);
3610 break;
3611 case 25:
3612 scr_rendition(0, RS_Blink);
3613 break;
3614 case 27:
3615 scr_rendition(0, RS_RVid);
3616 break;
3617
3618 case 30:
3619 case 31: /* set fg color */
3620 case 32:
3621 case 33:
3622 case 34:
3623 case 35:
3624 case 36:
3625 case 37:
3626 scr_color(minCOLOR + (arg[i] - 30), RS_Bold);
3627 break;
3628 case 39: /* default fg */
3629 scr_color(restoreFG, RS_Bold);
3630 break;
3631
3632 case 40:
3633 case 41: /* set bg color */
3634 case 42:
3635 case 43:
3636 case 44:
3637 case 45:
3638 case 46:
3639 case 47:
3640 scr_color(minCOLOR + (arg[i] - 40), RS_Blink);
3641 break;
3642 case 49: /* default bg */
3643 scr_color(restoreBG, RS_Blink);
3644 break;
3645 }
3646 }
3647 /*}}} */
3648
3649 /*{{{ process Rob Nation's own graphics mode sequences */
3650 /* PROTO */
3651 void
3652 process_graphics(void)
3653 {
3654 unsigned char ch, cmd = cmd_getc();
3655
3656 #ifndef RXVT_GRAPHICS
3657 if (cmd == 'Q') { /* query graphics */
3658 tt_printf((unsigned char *) "\033G0\n"); /* no graphics */
3659 return;
3660 }
3661 /* swallow other graphics sequences until terminating ':' */
3662 do
3663 ch = cmd_getc();
3664 while (ch != ':');
3665 #else
3666 int nargs;
3667 int args[NGRX_PTS];
3668 unsigned char *text = NULL;
3669
3670 if (cmd == 'Q') { /* query graphics */
3671 tt_printf((unsigned char *) "\033G1\n"); /* yes, graphics (color) */
3672 return;
3673 }
3674 for (nargs = 0; nargs < (sizeof(args) / sizeof(args[0])) - 1; ) {
3675 int neg;
3676
3677 ch = cmd_getc();
3678 neg = (ch == '-');
3679 if (neg || ch == '+')
3680 ch = cmd_getc();
3681
3682 for (args[nargs] = 0; isdigit(ch); ch = cmd_getc())
3683 args[nargs] = args[nargs] * 10 + (ch - '0');
3684 if (neg)
3685 args[nargs] = -args[nargs];
3686
3687 nargs++;
3688 args[nargs] = 0;
3689 if (ch != ';')
3690 break;
3691 }
3692
3693 if ((cmd == 'T') && (nargs >= 5)) {
3694 int i, len = args[4];
3695
3696 text = MALLOC((len + 1) * sizeof(char));
3697
3698 if (text != NULL) {
3699 for (i = 0; i < len; i++)
3700 text[i] = cmd_getc();
3701 text[len] = '\0';
3702 }
3703 }
3704 Gr_do_graphics(cmd, nargs, args, text);
3705 #ifdef USE_XIM
3706 IMSendSpot();
3707 #endif
3708 #endif
3709 }
3710 /*}}} */
3711
3712 /*{{{ Read and process output from the application */
3713 /* PROTO */
3714 void
3715 main_loop(void)
3716 {
3717 int ch;
3718 do {
3719 while ((ch = cmd_getc()) == 0) ; /* wait for something */
3720 /* fprintf( stderr, "%d: cmd_getc returned 0x%x(%c)\n", ++i, ch, isprint(ch)?ch:' ' ); */
3721 if (ch >= ' ' || ch == '\t' || ch == '\n' || ch == '\r') {
3722 /* Read a text string from the input buffer */
3723 int nlines = 0;
3724 unsigned char *str;
3725
3726 /*
3727 * point to the start of the string,
3728 * decrement first since it was post incremented in cmd_getc()
3729 */
3730 str = --cmdbuf_ptr;
3731 while (cmdbuf_ptr < cmdbuf_endp)
3732 {
3733 ch = *cmdbuf_ptr++;
3734 /* fprintf( stderr, "%d: cmd_getc returned 0x%x(%c)\n", ++i, ch, isprint(ch)?ch:' ' ); */
3735 if (ch >= ' ' || ch == '\t' )
3736 {
3737 /* nothing */
3738 } else if (ch == '\r')
3739 { /* we better flush that stuff to create an
3740 * impression that there is something going on */
3741 if( cmdbuf_ptr >= cmdbuf_endp ||
3742 (*cmdbuf_ptr != '\n' && *cmdbuf_ptr != '\r'))
3743 break;
3744 } else if (ch == '\n')
3745 {
3746 nlines++;
3747 if (++refresh_count >= (refresh_limit * (TermWin.nrow - 1)))
3748 break;
3749 } else { /* unprintable */
3750 cmdbuf_ptr--;
3751 break;
3752 }
3753 }
3754 scr_add_lines(str, nlines, (cmdbuf_ptr - str));
3755 if( ch == '\r' )
3756 {
3757 scr_refresh(refresh_type);
3758 }
3759 } else {
3760 switch (ch) {
3761 case 005: /* terminal Status */
3762 tt_printf((unsigned char *) VT100_ANS);
3763 break;
3764 case 007: /* bell */
3765 scr_bell();
3766 break;
3767 case '\b': /* backspace */
3768 scr_backspace();
3769 break;
3770 case 013: /* vertical tab, form feed */
3771 case 014:
3772 scr_index(UP);
3773 break;
3774 case 016: /* shift out - acs */
3775 scr_charset_choose(1);
3776 break;
3777 case 017: /* shift in - acs */
3778 scr_charset_choose(0);
3779 break;
3780 case 033: /* escape char */
3781 process_escape_seq();
3782 break;
3783 }
3784 }
3785 } while (ch != EOF);
3786 }
3787
3788 /* ---------------------------------------------------------------------- */
3789 /* Addresses pasting large amounts of data and rxvt hang
3790 * code pinched from xterm (v_write()) and applied originally to
3791 * rxvt-2.18 - Hops
3792 * Write data to the pty as typed by the user, pasted with the mouse,
3793 * or generated by us in response to a query ESC sequence.
3794 */
3795 /* PROTO */
3796 void
3797 tt_write(const unsigned char *d, int len)
3798 {
3799 int riten, p;
3800
3801 if (v_bufstr == NULL && len > 0) {
3802 v_buffer = v_bufstr = v_bufptr = MALLOC(len);
3803 v_bufend = v_buffer + len;
3804 }
3805 /*
3806 * Append to the block we already have. Always doing this simplifies the
3807 * code, and isn't too bad, either. If this is a short block, it isn't
3808 * too expensive, and if this is a long block, we won't be able to write
3809 * it all anyway.
3810 */
3811 if (len > 0) {
3812 if (v_bufend < v_bufptr + len) { /* we've run out of room */
3813 if (v_bufstr != v_buffer) {
3814 /* there is unused space, move everything down */
3815 /* possibly overlapping bcopy here */
3816 /* bcopy(v_bufstr, v_buffer, v_bufptr - v_bufstr); */
3817 memcpy(v_buffer, v_bufstr, v_bufptr - v_bufstr);
3818 v_bufptr -= v_bufstr - v_buffer;
3819 v_bufstr = v_buffer;
3820 }
3821 if (v_bufend < v_bufptr + len) {
3822 /* still won't fit: get more space */
3823 /* Don't use XtRealloc because an error is not fatal. */
3824 int size = v_bufptr - v_buffer;
3825
3826 /* save across realloc */
3827 v_buffer = REALLOC(v_buffer, size + len);
3828 if (v_buffer) {
3829 v_bufstr = v_buffer;
3830 v_bufptr = v_buffer + size;
3831 v_bufend = v_bufptr + len;
3832 } else {
3833 /* no memory: ignore entire write request */
3834 print_error("cannot allocate buffer space");
3835 v_buffer = v_bufstr; /* restore clobbered pointer */
3836 }
3837 }
3838 }
3839 if (v_bufend >= v_bufptr + len) { /* new stuff will fit */
3840 memcpy(v_bufptr, d, len); /* bcopy(d, v_bufptr, len); */
3841 v_bufptr += len;
3842 }
3843 }
3844 /*
3845 * Write out as much of the buffer as we can.
3846 * Be careful not to overflow the pty's input silo.
3847 * We are conservative here and only write a small amount at a time.
3848 *
3849 * If we can't push all the data into the pty yet, we expect write
3850 * to return a non-negative number less than the length requested
3851 * (if some data written) or -1 and set errno to EAGAIN,
3852 * EWOULDBLOCK, or EINTR (if no data written).
3853 *
3854 * (Not all systems do this, sigh, so the code is actually
3855 * a little more forgiving.)
3856 */
3857
3858 #define MAX_PTY_WRITE 128 /* 1/2 POSIX minimum MAX_INPUT */
3859
3860 if ((p = v_bufptr - v_bufstr) > 0) {
3861 riten = write(cmd_fd, v_bufstr, p < MAX_PTY_WRITE ? p : MAX_PTY_WRITE);
3862 if (riten < 0)
3863 riten = 0;
3864 v_bufstr += riten;
3865 if (v_bufstr >= v_bufptr) /* we wrote it all */
3866 v_bufstr = v_bufptr = v_buffer;
3867 }
3868 /*
3869 * If we have lots of unused memory allocated, return it
3870 */
3871 if (v_bufend - v_bufptr > 1024) { /* arbitrary hysteresis */
3872 /* save pointers across realloc */
3873 int start = v_bufstr - v_buffer;
3874 int size = v_bufptr - v_buffer;
3875 int allocsize = size ? size : 1;
3876
3877 v_buffer = REALLOC(v_buffer, allocsize);
3878 if (v_buffer) {
3879 v_bufstr = v_buffer + start;
3880 v_bufptr = v_buffer + size;
3881 v_bufend = v_buffer + allocsize;
3882 } else {
3883 /* should we print a warning if couldn't return memory? */
3884 v_buffer = v_bufstr - start; /* restore clobbered pointer */
3885 }
3886 }
3887 }
3888
3889 #ifdef USE_XIM
3890 /* PROTO */
3891 void
3892 setSize( XRectangle *size )
3893 {
3894 size->x = TermWin_internalBorder;
3895 size->y = TermWin_internalBorder;
3896 size->width = Width2Pixel (TermWin.ncol);
3897 size->height = Height2Pixel(TermWin.nrow);
3898 return;
3899 }
3900
3901 /* PROTO */
3902 void
3903 setColor( unsigned long *fg, unsigned long *bg )
3904 {
3905 *fg = PixColors[Color_fg];
3906 *bg = PixColors[Color_bg];
3907 return;
3908 }
3909 /* PROTO */
3910 void
3911 IMSendSpot( void )
3912 {
3913 XPoint spot;
3914 XVaNestedList preedit_attr;
3915 XIMStyle input_style;
3916
3917 if( Input_Context == NULL )
3918 return;
3919 else {
3920 XGetICValues(Input_Context,XNInputStyle,&input_style,NULL);
3921 if (!(input_style & XIMPreeditPosition))
3922 return;
3923 }
3924 setPosition( &spot );
3925
3926 preedit_attr = XVaCreateNestedList( 0, XNSpotLocation, &spot, NULL );
3927 XSetICValues( Input_Context, XNPreeditAttributes, preedit_attr, NULL );
3928 XFree( preedit_attr );
3929 return;
3930 }
3931
3932 /* PROTO */
3933 void
3934 setTermFontSet( void )
3935 {
3936 char *string;
3937 long length, i;
3938
3939 if( TermWin.fontset != NULL ){
3940 XFreeFontSet( Xdisplay, TermWin.fontset );
3941 TermWin.fontset = NULL;
3942 }
3943
3944 length = 0;
3945 for( i = 0 ; i < NFONTS ; i ++){
3946 if( rs_font[ i ] )
3947 length += strlen( rs_font[ i ] ) + 1;
3948 # ifdef MULTICHAR_SET
3949 if( rs_mfont[ i ] )
3950 length += strlen( rs_mfont[ i ] ) + 1;
3951 # endif
3952 }
3953 if( ( string = malloc( length ) ) != NULL ){
3954 char **missing_charsetlist, *def_string;
3955 int missing_charsetcount;
3956
3957 string[ 0 ] = '\0';
3958 for( i = 0 ; i < NFONTS ; i ++){
3959 if( rs_font[ i ] ){
3960 strcat( string, rs_font[ i ] );
3961 strcat( string, "," );
3962 }
3963 # ifdef MULTICHAR_SET
3964 if( rs_mfont[ i ] ){
3965 strcat( string, rs_mfont[ i ] );
3966 strcat( string, "," );
3967 }
3968 # endif
3969 }
3970 length = strlen( string );
3971 if( length > 0 && string[ length - 1 ] == ',' ){
3972 string[ length - 1 ] = '\0';
3973 length --;
3974 }
3975 if( length > 0 ){
3976 TermWin.fontset = XCreateFontSet
3977 ( Xdisplay, string,
3978 &missing_charsetlist, &missing_charsetcount, &def_string );
3979 }
3980 free( string );
3981 } else {
3982 TermWin.fontset = NULL;
3983 }
3984 return;
3985 }
3986
3987 /* PROTO */
3988 void
3989 setPreeditArea(XRectangle *preedit_rect, XRectangle *status_rect, XRectangle *needed_rect)
3990 {
3991 preedit_rect->x = needed_rect->width
3992 + (scrollbar_visible() && !(Options & Opt_scrollBar_right)
3993 ? (SB_WIDTH + sb_shadow * 2) : 0);
3994 preedit_rect->y = Height2Pixel(TermWin.nrow - 1)
3995 + ((menuBar.state == 1) ? menuBar_TotalHeight() : 0);
3996
3997 preedit_rect->width = Width2Pixel(TermWin.ncol + 1) - needed_rect->width
3998 + (!(Options & Opt_scrollBar_right)
3999 ? (SB_WIDTH + sb_shadow * 2) : 0);
4000 preedit_rect->height = Height2Pixel(1);
4001
4002 status_rect->x = (scrollbar_visible() && !(Options & Opt_scrollBar_right))
4003 ? (SB_WIDTH + sb_shadow * 2) : 0;
4004 status_rect->y = Height2Pixel(TermWin.nrow - 1)
4005 + ((menuBar.state == 1) ? menuBar_TotalHeight() : 0);
4006
4007 status_rect->width = needed_rect->width ? needed_rect->width
4008 : Width2Pixel(TermWin.ncol + 1);
4009 status_rect->height = Height2Pixel(1);
4010 }
4011
4012 /* PROTO */
4013 void
4014 IMDestroyCallback(XIM xim, XPointer client_data, XPointer call_data)
4015 {
4016 Input_Context = NULL;
4017 XRegisterIMInstantiateCallback(Xdisplay, NULL, NULL, NULL, IMInstantiateCallback, NULL);
4018 }
4019
4020
4021 /* PROTO */
4022 void
4023 IMInstantiateCallback(Display *display, XPointer client_data, XPointer call_data)
4024 {
4025 char *p, *s, buf[64], tmp[1024];
4026 char *end, *next_s;
4027 XIM xim = NULL;
4028 XIMStyle input_style = 0;
4029 XIMStyles *xim_styles = NULL;
4030 int found;
4031 XPoint spot;
4032 XRectangle rect, status_rect, needed_rect;
4033 unsigned long fg, bg;
4034 XVaNestedList preedit_attr = NULL;
4035 XVaNestedList status_attr = NULL;
4036 XIMCallback ximcallback;
4037
4038 Input_Context = NULL;
4039
4040 if (Input_Context)
4041 return;
4042
4043 ximcallback.callback = IMDestroyCallback;
4044 ximcallback.client_data = NULL;
4045
4046 if (rs_inputMethod && *rs_inputMethod) {
4047 STRNCPY(tmp, rs_inputMethod, sizeof(tmp) - 1);
4048 for (s = tmp; *s; s = next_s + 1) {
4049 for (; *s && isspace(*s); s++);
4050 if (!*s)
4051 break;
4052 for (end = s; (*end && (*end != ',')); end++);
4053 for (next_s = end--; ((end >= s) && isspace(*end)); end--);
4054 *(end + 1) = '\0';
4055
4056 if (*s) {
4057 STRCPY(buf, "@im=");
4058 strncat(buf, s, sizeof(buf) - 4 - 1);
4059 if ((p = XSetLocaleModifiers(buf)) != NULL
4060 && (xim = XOpenIM(Xdisplay, NULL, NULL, NULL)) != NULL)
4061 break;
4062 }
4063 if (!*next_s)
4064 break;
4065 }
4066 }
4067
4068 /* try with XMODIFIERS env. var. */
4069 if (xim == NULL && (p = XSetLocaleModifiers("")) != NULL)
4070 xim = XOpenIM(Xdisplay, NULL, NULL, NULL);
4071
4072 /* try with no modifiers base */
4073 if (xim == NULL && (p = XSetLocaleModifiers("@im=none")) != NULL)
4074 xim = XOpenIM(Xdisplay, NULL, NULL, NULL);
4075
4076 if (xim == NULL)
4077 return;
4078 XSetIMValues(xim, XNDestroyCallback, &ximcallback, NULL);
4079
4080 if (XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL)
4081 || !xim_styles) {
4082 print_error("input method doesn't support any style");
4083 XCloseIM(xim);
4084 return;
4085 }
4086 STRNCPY(tmp, (rs_preeditType ? rs_preeditType
4087 : "OverTheSpot,OffTheSpot,Root"),
4088 sizeof(tmp) - 1);
4089 for (found = 0, s = tmp; *s && !found; s = next_s + 1) {
4090 unsigned short i;
4091
4092 for (; *s && isspace(*s); s++);
4093 if (!*s)
4094 break;
4095 for (end = s; (*end && (*end != ',')); end++);
4096 for (next_s = end--; ((end >= s) && isspace(*end)); end--);
4097 *(end + 1) = '\0';
4098
4099 if (!strcmp(s, "OverTheSpot"))
4100 input_style = (XIMPreeditPosition | XIMStatusNothing);
4101 else if (!strcmp(s, "OffTheSpot"))
4102 input_style = (XIMPreeditArea | XIMStatusArea);
4103 else if (!strcmp(s, "Root"))
4104 input_style = (XIMPreeditNothing | XIMStatusNothing);
4105
4106 for (i = 0; i < xim_styles->count_styles; i++)
4107 if (input_style == xim_styles->supported_styles[i]) {
4108 found = 1;
4109 break;
4110 }
4111 }
4112 XFree(xim_styles);
4113
4114 if (found == 0) {
4115 print_error("input method doesn't support my preedit type");
4116 XCloseIM(xim);
4117 return;
4118 }
4119 if ((input_style != (XIMPreeditNothing | XIMStatusNothing))
4120 && (input_style != (XIMPreeditArea | XIMStatusArea))
4121 && (input_style != (XIMPreeditPosition | XIMStatusNothing))) {
4122 print_error("This program does not support the preedit type");
4123 XCloseIM(xim);
4124 return;
4125 }
4126 if (input_style & XIMPreeditPosition) {
4127 setSize(&rect);
4128 setPosition(&spot);
4129 setColor(&fg, &bg);
4130
4131 preedit_attr = XVaCreateNestedList(0, XNArea, &rect,
4132 XNSpotLocation, &spot,
4133 XNForeground, fg,
4134 XNBackground, bg,
4135 XNFontSet, TermWin.fontset,
4136 NULL);
4137 } else if (input_style & XIMPreeditArea) {
4138 setColor(&fg, &bg);
4139
4140 /*
4141 * The necessary width of preedit area is unknown
4142 * until create input context.
4143 */
4144 needed_rect.width = 0;
4145
4146 setPreeditArea(&rect, &status_rect, &needed_rect);
4147
4148 preedit_attr = XVaCreateNestedList(0, XNArea, &rect,
4149 XNForeground, fg,
4150 XNBackground, bg,
4151 XNFontSet, TermWin.fontset,
4152 NULL);
4153 status_attr = XVaCreateNestedList(0, XNArea, &status_rect,
4154 XNForeground, fg,
4155 XNBackground, bg,
4156 XNFontSet, TermWin.fontset,
4157 NULL);
4158 }
4159 Input_Context = XCreateIC(xim, XNInputStyle, input_style,
4160 XNClientWindow, TermWin.parent,
4161 XNFocusWindow, TermWin.parent,
4162 XNDestroyCallback, &ximcallback,
4163 preedit_attr ? XNPreeditAttributes : NULL,
4164 preedit_attr,
4165 status_attr ? XNStatusAttributes : NULL,
4166 status_attr,
4167 NULL);
4168 XFree(preedit_attr);
4169 XFree(status_attr);
4170 if (Input_Context == NULL) {
4171 print_error("Failed to create input context");
4172 XCloseIM(xim);
4173 }
4174 if (input_style & XIMPreeditArea)
4175 IMSetStatusPosition();
4176 }
4177
4178 /* PROTO */
4179 void
4180 IMSetStatusPosition(void)
4181 {
4182 XIMStyle input_style;
4183 XRectangle rect, status_rect, *needed_rect;
4184 XVaNestedList preedit_attr, status_attr;
4185
4186 if (Input_Context == NULL)
4187 return;
4188
4189 XGetICValues(Input_Context, XNInputStyle, &input_style, NULL);
4190 if (input_style & XIMPreeditArea) {
4191 status_attr = XVaCreateNestedList(0, XNAreaNeeded, &needed_rect, NULL);
4192 XGetICValues(Input_Context, XNStatusAttributes, status_attr, NULL);
4193 XFree(status_attr);
4194 rect.x = needed_rect->width;
4195 if (menuBar.state == 1) {
4196 rect.y = Height2Pixel(TermWin.nrow - 1) - menuBar_TotalHeight();
4197 } else {
4198 rect.y = Height2Pixel(TermWin.nrow - 1);
4199 }
4200 if (Options & Opt_scrollBar_right) {
4201 rect.width = Width2Pixel(TermWin.ncol + 1) - needed_rect->width;
4202 } else {
4203 rect.width = Width2Pixel(TermWin.ncol + 1) + SB_WIDTH + SHADOW * 2 - needed_rect->width;
4204 }
4205 rect.height = needed_rect->height;
4206 preedit_attr = XVaCreateNestedList(0, XNArea, &rect, NULL);
4207
4208 if (scrollbar_visible()) {
4209 if (Options & Opt_scrollBar_right) {
4210 status_rect.x = 0;
4211 } else {
4212 status_rect.x = SB_WIDTH + SHADOW * 2;
4213 }
4214 } else {
4215 status_rect.x = 0;
4216 }
4217 if (menuBar.state == 1) {
4218 status_rect.y = Height2Pixel(TermWin.nrow - 1) + menuBar_TotalHeight();
4219 } else {
4220 status_rect.y = Height2Pixel(TermWin.nrow - 1);
4221 }
4222 status_rect.width = needed_rect->width;
4223 status_rect.height = needed_rect->height;
4224 status_attr = XVaCreateNestedList(0, XNArea, &status_rect, NULL);
4225 XSetICValues(Input_Context,
4226 XNPreeditAttributes, preedit_attr,
4227 XNStatusAttributes, status_attr, NULL);
4228 XFree(preedit_attr);
4229 XFree(status_attr);
4230 }
4231 }
4232
4233 /* PROTO */
4234 void
4235 XProcessEvent( Display *display )
4236 {
4237 XEvent xev;
4238 XNextEvent( display, &xev );
4239 if( !XFilterEvent( &xev, xev.xany.window ) )
4240 process_x_event( &xev );
4241 return;
4242 }
4243
4244 #endif
4245
4246 /*}}} */
4247 /*----------------------- end-of-file (C source) -----------------------*/
4248