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 #if defined(USE_XIM) || !defined(NO_XLOCALE)
1490 len = 0;
1491 if (!XFilterEvent(ev, *(&ev->xkey.window))) {
1492 if (Input_Context != NULL) {
1493 Status status_return;
1494
1495 kbuf[0] = '\0';
1496 len = XmbLookupString(Input_Context, &ev->xkey, kbuf,
1497 sizeof(kbuf), &keysym,
1498 &status_return);
1499 } else {
1500 len = XLookupString(&ev->xkey, kbuf,
1501 sizeof(kbuf), &keysym,
1502 &compose);
1503 }
1504 }
1505 #else /* USE_XIM */
1506 len = XLookupString(&ev->xkey, (char *) kbuf, sizeof(kbuf), &keysym, &compose);
1507 /*
1508 * have unmapped Latin[2-4] entries -> Latin1
1509 * good for installations with correct fonts, but without XLOCAL
1510 */
1511 if (!len && (keysym >= 0x0100) && (keysym < 0x0400)) {
1512 len = 1;
1513 kbuf[0] = (keysym & 0xFF);
1514 }
1515 #endif /* USE_XIM */
1516
1517 if (len && (Options & Opt_scrollKeypress))
1518 TermWin.view_start = 0;
1519
1520 /* for some backwards compatibility */
1521 #if defined (HOTKEY_CTRL) || defined (HOTKEY_META)
1522 # ifdef HOTKEY_CTRL
1523 # define HOTKEY ctrl
1524 # else
1525 # ifdef HOTKEY_META
1526 # define HOTKEY meta
1527 # endif
1528 # endif
1529 if (HOTKEY) {
1530 if (keysym == ks_bigfont) {
1531 change_font(0, FONT_UP);
1532 return;
1533 } else if (keysym == ks_smallfont) {
1534 change_font(0, FONT_DN);
1535 return;
1536 }
1537 }
1538 # undef HOTKEY
1539 #endif
1540
1541 if (shft) {
1542 /* Shift + F1 - F10 generates F11 - F20 */
1543 if (keysym >= XK_F1 && keysym <= XK_F10) {
1544 keysym += (XK_F11 - XK_F1);
1545 shft = 0; /* turn off Shift */
1546 } else if (!ctrl && !meta && (PrivateModes & PrivMode_ShiftKeys)) {
1547 int lnsppg = TermWin.nrow * 4 / 5;
1548
1549 #ifdef PAGING_CONTEXT_LINES
1550 lnsppg = TermWin.nrow - PAGING_CONTEXT_LINES;
1551 #endif
1552
1553 switch (keysym) {
1554 /* normal XTerm key bindings */
1555 case XK_Prior: /* Shift+Prior = scroll back */
1556 if (TermWin.saveLines) {
1557 scr_page(UP, lnsppg);
1558 return;
1559 }
1560 break;
1561
1562 case XK_Up: /* Shift+XK_Up = scroll up one line */
1563 if (TermWin.saveLines) {
1564 scr_page(UP, 1);
1565 return;
1566 }
1567 break;
1568
1569 case XK_Down: /* Shift+XK_Down = scroll down one line */
1570 if (TermWin.saveLines) {
1571 scr_page(DN, 1);
1572 return;
1573 }
1574 break;
1575
1576 case XK_Next: /* Shift+Next = scroll forward */
1577 if (TermWin.saveLines) {
1578 scr_page(DN, lnsppg);
1579 return;
1580 }
1581 break;
1582
1583 case XK_Insert: /* Shift+Insert = paste mouse selection */
1584 selection_request(ev->xkey.time, ev->xkey.x, ev->xkey.y);
1585 return;
1586 break;
1587
1588 /* rxvt extras */
1589 case XK_KP_Add: /* Shift+KP_Add = bigger font */
1590 change_font(0, FONT_UP);
1591 return;
1592 break;
1593
1594 case XK_KP_Subtract: /* Shift+KP_Subtract = smaller font */
1595 change_font(0, FONT_DN);
1596 return;
1597 break;
1598 }
1599 }
1600 }
1601 #ifdef UNSHIFTED_SCROLLKEYS
1602 else if (!ctrl && !meta) {
1603 switch (keysym) {
1604 case XK_Prior:
1605 if (TermWin.saveLines) {
1606 scr_page(UP, TermWin.nrow * 4 / 5);
1607 return;
1608 }
1609 break;
1610
1611 case XK_Next:
1612 if (TermWin.saveLines) {
1613 scr_page(DN, TermWin.nrow * 4 / 5);
1614 return;
1615 }
1616 break;
1617 }
1618 }
1619 #endif
1620
1621 switch (keysym) {
1622 case XK_Print:
1623 #ifdef PRINTPIPE
1624 scr_printscreen(ctrl | shft);
1625 return;
1626 #endif
1627 break;
1628
1629 case XK_Mode_switch:
1630 #ifdef GREEK_SUPPORT
1631 greek_mode = !greek_mode;
1632 if (greek_mode) {
1633 xterm_seq(XTerm_title, (greek_getmode() == GREEK_ELOT928 ?
1634 "[Greek: iso]" : "[Greek: ibm]"));
1635 greek_reset();
1636 } else
1637 xterm_seq(XTerm_title, APL_NAME "-" VERSION);
1638 return;
1639 #endif
1640 break;
1641 }
1642
1643 if (keysym >= 0xFF00 && keysym <= 0xFFFF) {
1644 #ifdef KEYSYM_RESOURCE
1645 if (!(shft | ctrl) && KeySym_map[keysym - 0xFF00] != NULL) {
1646 const unsigned char *kbuf;
1647 unsigned int len;
1648
1649 kbuf = (KeySym_map[keysym - 0xFF00]);
1650 len = *kbuf++;
1651
1652 /* escape prefix */
1653 if (meta
1654 # ifdef META8_OPTION
1655 && (meta_char == 033)
1656 # endif
1657 ) {
1658 const unsigned char ch = '\033';
1659
1660 tt_write(&ch, 1);
1661 }
1662 tt_write(kbuf, len);
1663 return;
1664 } else
1665 #endif
1666 switch (keysym) {
1667 #ifndef NO_BACKSPACE_KEY
1668 case XK_BackSpace:
1669 if (PrivateModes & PrivMode_HaveBackSpace) {
1670 len = 1;
1671 kbuf[0] = (((PrivateModes & PrivMode_BackSpace) ?
1672 !(shft | ctrl) : (shft | ctrl)) ? '\b' : '\177');
1673 } else
1674 len = strlen(STRCPY(kbuf, rs_backspace_key));
1675 break;
1676 #endif
1677 #ifndef NO_DELETE_KEY
1678 case XK_Delete:
1679 len = strlen(STRCPY(kbuf, rs_delete_key));
1680 break;
1681 #endif
1682 case XK_Tab:
1683 if (shft) {
1684 len = 3;
1685 STRCPY(kbuf, "\033[Z");
1686 }
1687 break;
1688
1689 #ifdef XK_KP_Home
1690 case XK_KP_Home:
1691 /* allow shift to override */
1692 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1693 len = 3;
1694 STRCPY(kbuf, "\033Ow");
1695 break;
1696 }
1697 /* -> else FALL THROUGH */
1698 #endif
1699 case XK_Home:
1700 len = strlen(STRCPY(kbuf, KS_HOME));
1701 break;
1702
1703 #ifdef XK_KP_Left
1704 case XK_KP_Left: /* \033Ot or standard */
1705 case XK_KP_Up: /* \033Ox or standard */
1706 case XK_KP_Right: /* \033Ov or standard */
1707 case XK_KP_Down: /* \033Or or standard */
1708 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1709 len = 3;
1710 STRCPY(kbuf, "\033OZ");
1711 kbuf[2] = ("txvr"[keysym - XK_KP_Left]);
1712 break;
1713 } else {
1714 /* translate to std. cursor key */
1715 keysym = XK_Left + (keysym - XK_KP_Left);
1716 }
1717 /* FALL THROUGH */
1718 #endif
1719 case XK_Left: /* "\033[D" */
1720 case XK_Up: /* "\033[A" */
1721 case XK_Right: /* "\033[C" */
1722 case XK_Down: /* "\033[B" */
1723 len = 3;
1724 STRCPY(kbuf, "\033[@");
1725 kbuf[2] = ("DACB"[keysym - XK_Left]);
1726 /* do Shift first */
1727 if (shft) {
1728 kbuf[2] = ("dacb"[keysym - XK_Left]);
1729 } else if (ctrl) {
1730 kbuf[1] = 'O';
1731 kbuf[2] = ("dacb"[keysym - XK_Left]);
1732 } else if (PrivateModes & PrivMode_aplCUR) {
1733 kbuf[1] = 'O';
1734 }
1735 break;
1736
1737 #ifndef UNSHIFTED_SCROLLKEYS
1738 # ifdef XK_KP_Prior
1739 case XK_KP_Prior:
1740 /* allow shift to override */
1741 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1742 len = 3;
1743 STRCPY(kbuf, "\033Oy");
1744 break;
1745 }
1746 /* -> else FALL THROUGH */
1747 # endif
1748 case XK_Prior:
1749 len = 4;
1750 STRCPY(kbuf, "\033[5~");
1751 break;
1752 # ifdef XK_KP_Next
1753 case XK_KP_Next:
1754 /* allow shift to override */
1755 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1756 len = 3;
1757 STRCPY(kbuf, "\033Os");
1758 break;
1759 }
1760 /* -> else FALL THROUGH */
1761 # endif
1762 case XK_Next:
1763 len = 4;
1764 STRCPY(kbuf, "\033[6~");
1765 break;
1766 #endif
1767 #ifdef XK_KP_End
1768 case XK_KP_End:
1769 /* allow shift to override */
1770 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1771 len = 3;
1772 STRCPY(kbuf, "\033Oq");
1773 break;
1774 }
1775 /* -> else FALL THROUGH */
1776 #endif
1777 case XK_End:
1778 len = strlen(STRCPY(kbuf, KS_END));
1779 break;
1780
1781 case XK_Select:
1782 len = 4;
1783 STRCPY(kbuf, "\033[4~");
1784 break;
1785 #ifdef DXK_Remove /* support for DEC remove like key */
1786 case DXK_Remove: /* drop */
1787 #endif
1788 case XK_Execute:
1789 len = 4;
1790 STRCPY(kbuf, "\033[3~");
1791 break;
1792 case XK_Insert:
1793 len = 4;
1794 STRCPY(kbuf, "\033[2~");
1795 break;
1796
1797 case XK_Menu:
1798 len = 5;
1799 STRCPY(kbuf, "\033[29~");
1800 break;
1801 case XK_Find:
1802 len = 4;
1803 STRCPY(kbuf, "\033[1~");
1804 break;
1805 case XK_Help:
1806 len = 5;
1807 STRCPY(kbuf, "\033[28~");
1808 break;
1809
1810 case XK_KP_Enter:
1811 /* allow shift to override */
1812 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1813 len = 3;
1814 STRCPY(kbuf, "\033OM");
1815 } else {
1816 len = 1;
1817 kbuf[0] = '\r';
1818 }
1819 break;
1820
1821 #ifdef XK_KP_Begin
1822 case XK_KP_Begin:
1823 len = 3;
1824 STRCPY(kbuf, "\033Ou");
1825 break;
1826
1827 case XK_KP_Insert:
1828 len = 3;
1829 STRCPY(kbuf, "\033Op");
1830 break;
1831
1832 case XK_KP_Delete:
1833 len = 3;
1834 STRCPY(kbuf, "\033On");
1835 break;
1836 #endif
1837
1838 case XK_KP_F1: /* "\033OP" */
1839 case XK_KP_F2: /* "\033OQ" */
1840 case XK_KP_F3: /* "\033OR" */
1841 case XK_KP_F4: /* "\033OS" */
1842 len = 3;
1843 STRCPY(kbuf, "\033OP");
1844 kbuf[2] += (keysym - XK_KP_F1);
1845 break;
1846
1847 case XK_KP_Multiply: /* "\033Oj" : "*" */
1848 case XK_KP_Add: /* "\033Ok" : "+" */
1849 case XK_KP_Separator: /* "\033Ol" : "," */
1850 case XK_KP_Subtract: /* "\033Om" : "-" */
1851 case XK_KP_Decimal: /* "\033On" : "." */
1852 case XK_KP_Divide: /* "\033Oo" : "/" */
1853 case XK_KP_0: /* "\033Op" : "0" */
1854 case XK_KP_1: /* "\033Oq" : "1" */
1855 case XK_KP_2: /* "\033Or" : "2" */
1856 case XK_KP_3: /* "\033Os" : "3" */
1857 case XK_KP_4: /* "\033Ot" : "4" */
1858 case XK_KP_5: /* "\033Ou" : "5" */
1859 case XK_KP_6: /* "\033Ov" : "6" */
1860 case XK_KP_7: /* "\033Ow" : "7" */
1861 case XK_KP_8: /* "\033Ox" : "8" */
1862 case XK_KP_9: /* "\033Oy" : "9" */
1863 /* allow shift to override */
1864 if ((PrivateModes & PrivMode_aplKP) ? !shft : shft) {
1865 len = 3;
1866 STRCPY(kbuf, "\033Oj");
1867 kbuf[2] += (keysym - XK_KP_Multiply);
1868 } else {
1869 len = 1;
1870 kbuf[0] = ('*' + (keysym - XK_KP_Multiply));
1871 }
1872 break;
1873
1874 case XK_F1: /* "\033OP" */
1875 case XK_F2: /* "\033OQ" */
1876 case XK_F3: /* "\033OR" */
1877 case XK_F4: /* "\033OS" */
1878 len = 3;
1879 STRCPY(kbuf, "\033OP");
1880 kbuf[2] += (keysym - XK_F1);
1881 break;
1882
1883 #define FKEY(n, fkey) \
1884 len = 5; \
1885 sprintf((char *) kbuf,"\033[%02d~", (int)((n) + (keysym - fkey)))
1886 #if 0 /* old style keymappings : */
1887 case XK_F1: /* "\033[11~" */
1888 case XK_F2: /* "\033[12~" */
1889 case XK_F3: /* "\033[13~" */
1890 case XK_F4: /* "\033[14~" */
1891 FKEY(11, XK_F1);
1892 break;
1893 #endif
1894 case XK_F5: /* "\033[15~" */
1895 FKEY(15, XK_F5);
1896 break;
1897
1898 case XK_F6: /* "\033[17~" */
1899 case XK_F7: /* "\033[18~" */
1900 case XK_F8: /* "\033[19~" */
1901 case XK_F9: /* "\033[20~" */
1902 case XK_F10: /* "\033[21~" */
1903 FKEY(17, XK_F6);
1904 break;
1905
1906 case XK_F11: /* "\033[23~" */
1907 case XK_F12: /* "\033[24~" */
1908 case XK_F13: /* "\033[25~" */
1909 case XK_F14: /* "\033[26~" */
1910 FKEY(23, XK_F11);
1911 break;
1912
1913 case XK_F15: /* "\033[28~" */
1914 case XK_F16: /* "\033[29~" */
1915 FKEY(28, XK_F15);
1916 break;
1917
1918 case XK_F17: /* "\033[31~" */
1919 case XK_F18: /* "\033[32~" */
1920 case XK_F19: /* "\033[33~" */
1921 case XK_F20: /* "\033[34~" */
1922 case XK_F21: /* "\033[35~" */
1923 case XK_F22: /* "\033[36~" */
1924 case XK_F23: /* "\033[37~" */
1925 case XK_F24: /* "\033[38~" */
1926 case XK_F25: /* "\033[39~" */
1927 case XK_F26: /* "\033[40~" */
1928 case XK_F27: /* "\033[41~" */
1929 case XK_F28: /* "\033[42~" */
1930 case XK_F29: /* "\033[43~" */
1931 case XK_F30: /* "\033[44~" */
1932 case XK_F31: /* "\033[45~" */
1933 case XK_F32: /* "\033[46~" */
1934 case XK_F33: /* "\033[47~" */
1935 case XK_F34: /* "\033[48~" */
1936 case XK_F35: /* "\033[49~" */
1937 FKEY(31, XK_F17);
1938 break;
1939 #undef FKEY
1940 }
1941 /*
1942 * Pass meta for all function keys, if 'meta' option set
1943 */
1944 #ifdef META8_OPTION
1945 if (meta && (meta_char == 0x80) && len > 0) {
1946 kbuf[len - 1] |= 0x80;
1947 }
1948 #endif
1949 } else if (ctrl && keysym == XK_minus) {
1950 len = 1;
1951 kbuf[0] = '\037'; /* Ctrl-Minus generates ^_ (31) */
1952 } else {
1953 #ifdef META8_OPTION
1954 /* set 8-bit on */
1955 if (meta && (meta_char == 0x80)) {
1956 unsigned char *ch;
1957
1958 for (ch = kbuf; ch < kbuf + len; ch++)
1959 *ch |= 0x80;
1960 meta = 0;
1961 }
1962 #endif
1963 #ifdef GREEK_SUPPORT
1964 if (greek_mode)
1965 len = greek_xlat(kbuf, len);
1966 #endif
1967 /* nil */ ;
1968 }
1969
1970 if (len <= 0)
1971 return; /* not mapped */
1972
1973 /*
1974 * these modifications only affect the static keybuffer
1975 * pass Shift/Control indicators for function keys ending with `~'
1976 *
1977 * eg,
1978 * Prior = "ESC[5~"
1979 * Shift+Prior = "ESC[5~"
1980 * Ctrl+Prior = "ESC[5^"
1981 * Ctrl+Shift+Prior = "ESC[5@"
1982 * Meta adds an Escape prefix (with META8_OPTION, if meta == <escape>).
1983 */
1984 if (kbuf[0] == '\033' && kbuf[1] == '[' && kbuf[len - 1] == '~')
1985 kbuf[len - 1] = (shft ? (ctrl ? '@' : '$') : (ctrl ? '^' : '~'));
1986
1987 /* escape prefix */
1988 if (meta
1989 #ifdef META8_OPTION
1990 && (meta_char == 033)
1991 #endif
1992 ) {
1993 const unsigned char ch = '\033';
1994
1995 tt_write(&ch, 1);
1996 }
1997 #ifdef DEBUG_CMD
1998 if (debug_key) { /* Display keyboard buffer contents */
1999 char *p;
2000 int i;
2001
2002 fprintf(stderr, "key 0x%04X [%d]: `", (unsigned int)keysym, len);
2003 for (i = 0, p = kbuf; i < len; i++, p++)
2004 fprintf(stderr, (*p >= ' ' && *p < '\177' ? "%c" : "\\%03o"), *p);
2005 fprintf(stderr, "'\n");
2006 }
2007 #endif /* DEBUG_CMD */
2008 tt_write(kbuf, len);
2009 }
2010 /*}}} */
2011
2012 #if (MENUBAR_MAX)
2013 /*{{{ cmd_write(), cmd_getc() */
2014 /* attempt to `write' COUNT to the input buffer */
2015 /* PROTO */
2016 unsigned int
2017 cmd_write(const unsigned char *str, unsigned int count)
2018 {
2019 int n;
2020
2021 n = (count - (cmdbuf_ptr - cmdbuf_base));
2022 /* need to insert more chars that space available in the front */
2023 if (n > 0) {
2024 /* try and get more space from the end */
2025 unsigned char *src, *dst;
2026
2027 dst = (cmdbuf_base + sizeof(cmdbuf_base) - 1); /* max pointer */
2028
2029 if ((cmdbuf_ptr + n) > dst)
2030 n = (dst - cmdbuf_ptr); /* max # chars to insert */
2031
2032 if ((cmdbuf_endp + n) > dst)
2033 cmdbuf_endp = (dst - n); /* truncate end if needed */
2034
2035 /* equiv: memmove ((cmdbuf_ptr+n), cmdbuf_ptr, n); */
2036 src = cmdbuf_endp;
2037 dst = src + n;
2038 /* FIXME: anything special to avoid possible pointer wrap? */
2039 while (src >= cmdbuf_ptr)
2040 *dst-- = *src--;
2041
2042 /* done */
2043 cmdbuf_ptr += n;
2044 cmdbuf_endp += n;
2045 }
2046 while (count-- && cmdbuf_ptr > cmdbuf_base) {
2047 /* sneak one in */
2048 cmdbuf_ptr--;
2049 *cmdbuf_ptr = str[count];
2050 }
2051
2052 return 0;
2053 }
2054 #endif /* MENUBAR_MAX */
2055 /* cmd_getc() - Return next input character */
2056 /*
2057 * Return the next input character after first passing any keyboard input
2058 * to the command.
2059 */
2060 /* PROTO */
2061 unsigned char
2062 cmd_getc(void)
2063 {
2064 #define TIMEOUT_USEC 5000
2065 static short refreshed = 0;
2066 fd_set readfds;
2067 int retval;
2068
2069 struct timeval value;
2070 #if 0
2071 #ifdef __CYGWIN32__
2072 struct timeval value;
2073 #else
2074 struct itimerval value;
2075 #endif
2076 #endif
2077
2078 /* If there have been a lot of new lines, then update the screen
2079 * What the heck I'll cheat and only refresh less than every page-full.
2080 * the number of pages between refreshes is refresh_limit, which
2081 * is incremented here because we must be doing flat-out scrolling.
2082 *
2083 * refreshing should be correct for small scrolls, because of the
2084 * time-out */
2085 if (refresh_count >= (refresh_limit * (TermWin.nrow - 1))) {
2086 if (refresh_limit < REFRESH_PERIOD)
2087 refresh_limit++;
2088 refresh_count = 0;
2089 refreshed = 1;
2090 scr_refresh(refresh_type);
2091 }
2092 /* characters already read in */
2093 if (cmdbuf_ptr < cmdbuf_endp)
2094 goto Return_Char;
2095
2096 for (;;) {
2097 struct timeval *timeout = &value;
2098
2099 if (v_bufstr < v_bufptr) /* output any pending chars */
2100 tt_write(NULL, 0);
2101 while (XPending(Xdisplay)) { /* process pending X events */
2102 refreshed = 0;
2103 #ifdef USE_XIM
2104 XProcessEvent(Xdisplay);
2105 #else
2106 {
2107 XEvent ev;
2108 XNextEvent(Xdisplay, &ev);
2109 /*fprintf( stderr, "%s:%d Received event %d\n", __FUNCTION__, __LINE__, ev.type );*/
2110 process_x_event(&ev);
2111 }
2112 #endif
2113 /* in case button actions pushed chars to cmdbuf */
2114 if (cmdbuf_ptr < cmdbuf_endp)
2115 goto Return_Char;
2116 }
2117 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
2118 if (scrollbar_isUp()) {
2119 if (!scroll_arrow_delay-- && scr_page(UP, 1)) {
2120 scroll_arrow_delay = SCROLLBAR_CONTINUOUS_DELAY;
2121 refreshed = 0;
2122 refresh_type |= SMOOTH_REFRESH;
2123 }
2124 } else if (scrollbar_isDn()) {
2125 if (!scroll_arrow_delay-- && scr_page(DN, 1)) {
2126 scroll_arrow_delay = SCROLLBAR_CONTINUOUS_DELAY;
2127 refreshed = 0;
2128 refresh_type |= SMOOTH_REFRESH;
2129 }
2130 }
2131 #endif /* NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING */
2132
2133 /* Nothing to do! */
2134 FD_ZERO(&readfds);
2135 FD_SET(cmd_fd, &readfds);
2136 FD_SET(Xfd, &readfds);
2137
2138 if( refreshed && last_update_background_request_sec == 0
2139 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
2140 && !(scrollbar_isUpDn())
2141 #endif
2142 )
2143 timeout = NULL ;
2144 else if( !refreshed )
2145 {
2146 timeout->tv_usec = TIMEOUT_USEC;
2147 timeout->tv_sec = 0;
2148 }else if( last_update_background_request_sec )
2149 {
2150 time_t curr_t_sec, curr_t_usec, wait_usec = 0 ;
2151 timer_get_time (&curr_t_sec, &curr_t_usec);
2152 if( curr_t_sec == last_update_background_request_sec )
2153 {
2154 if( curr_t_usec < last_update_background_request_usec )
2155 wait_usec = last_update_background_request_usec - curr_t_usec ;
2156 }else if( curr_t_sec < last_update_background_request_sec )
2157 {
2158 wait_usec = (last_update_background_request_sec - curr_t_sec)*1000000 ;
2159 wait_usec += last_update_background_request_usec ;
2160 wait_usec -= curr_t_usec ;
2161 }
2162 if( wait_usec == 0 )
2163 wait_usec = TIMEOUT_USEC ;
2164 timeout->tv_usec = wait_usec;
2165 timeout->tv_sec = 0 ; /*UPDATE_BACKGROUND_TIMEOUT_SEC;*/
2166 }
2167 /*fprintf( stderr, "%s:%d select(%d, cmd_fd = %d, Xfd = %d, %p)...", __FUNCTION__, __LINE__, num_fds, cmd_fd,Xfd ,timeout ); */
2168 retval = select(max(cmd_fd,Xfd)+1, &readfds, NULL, NULL, timeout );
2169 /* fprintf( stderr, "Done(retval = %d).\n", retval); */
2170 /* See if we can read from the application */
2171 if (FD_ISSET(cmd_fd, &readfds)) {
2172 int n;
2173 unsigned int count;
2174
2175 cmdbuf_ptr = cmdbuf_endp = cmdbuf_base;
2176 for (count = BUFSIZ; count; count -= n, cmdbuf_endp += n)
2177 if ((n = read(cmd_fd, cmdbuf_endp, count)) > 0)
2178 continue;
2179 else if (n == 0 || (n < 0 && errno == EAGAIN))
2180 break;
2181 else {
2182 #if !defined(HAVE_ATEXIT) && !defined(__sun__)
2183 clean_exit();
2184 #endif
2185 exit(EXIT_SUCCESS);
2186 }
2187 /* some characters read in */
2188 if (count != BUFSIZ)
2189 goto Return_Char;
2190 }
2191 /* select statement timed out - better update the screen */
2192 if (retval == 0)
2193 {
2194 refresh_count = 0;
2195 refresh_limit = 1;
2196 if( last_update_background_request_sec > 0 )
2197 {
2198 time_t curr_t_sec, curr_t_usec;
2199 timer_get_time (&curr_t_sec, &curr_t_usec);
2200 if( ( last_update_background_request_sec == curr_t_sec &&
2201 last_update_background_request_usec < curr_t_usec) ||
2202 last_update_background_request_sec < curr_t_sec ||
2203 last_update_background_request_sec-1 > curr_t_sec )
2204 {
2205 /* TODO update background pixmap */
2206 RenderPixmap(1);
2207 refresh_transparent_scrollbar();
2208 scr_clear();
2209 scr_touch();
2210 refreshed = True ;
2211 }
2212 }
2213 if (!refreshed)
2214 {
2215 refreshed = 1;
2216 scr_refresh(refresh_type);
2217 scrollbar_show(1);
2218 #ifdef USE_XIM
2219 IMSendSpot();
2220 #endif
2221 }
2222 }
2223 }
2224 /* NOTREACHED */
2225 return 0;
2226
2227 Return_Char:
2228 refreshed = 0;
2229 return (*cmdbuf_ptr++);
2230 }
2231 /*}}} */
2232
2233 /*
2234 * the 'essential' information for reporting Mouse Events
2235 * pared down from XButtonEvent
2236 */
2237 static struct {
2238 int clicks;
2239 Time time; /* milliseconds */
2240 unsigned int state; /* key or button mask */
2241 unsigned int button; /* detail */
2242 } MEvent = {
2243 0, CurrentTime, 0, AnyButton
2244 };
2245
2246 /* PROTO */
2247 void
2248 mouse_report(XButtonEvent * ev)
2249 {
2250 int button_number, key_state = 0;
2251 int x, y;
2252
2253 x = ev->x;
2254 y = ev->y;
2255 pixel_position(&x, &y);
2256
2257 button_number = ((MEvent.button == AnyButton) ? 3 :
2258 (MEvent.button - Button1));
2259
2260 if (PrivateModes & PrivMode_MouseX10) {
2261 /*
2262 * do not report ButtonRelease
2263 * no state info allowed
2264 */
2265 key_state = 0;
2266 if (button_number == 3)
2267 return;
2268 } else {
2269 /* let's be explicit here ... from <X11/X.h>
2270 * #define ShiftMask (1<<0)
2271 * #define ControlMask (1<<2)
2272 * #define Mod1Mask (1<<3)
2273 *
2274 * and XTerm mouse reporting needs these values:
2275 * 4 = Shift
2276 * 8 = Meta
2277 * 16 = Control
2278 * plus will add in our own Double-Click reporting
2279 * 32 = Double Click
2280 */
2281 key_state = (((MEvent.state & (ShiftMask | ControlMask))
2282 + ((MEvent.state & Mod1Mask) ? 2 : 0)
2283 #ifdef MOUSE_REPORT_DOUBLECLICK
2284 + (MEvent.clicks > 1 ? 8 : 0)
2285 #endif
2286 ) << 2);
2287 /* Report mouse wheel events. */
2288 if (ev->button == Button4 || ev->button == Button5) {
2289 key_state |= 1 << 6;
2290 button_number = ev->button - Button4;
2291 }
2292 }
2293
2294 #ifdef DEBUG_MOUSEREPORT
2295 fprintf(stderr, "Mouse [");
2296 if (key_state & 16)
2297 fputc('C', stderr);
2298 if (key_state & 4)
2299 fputc('S', stderr);
2300 if (key_state & 2)
2301 fputc('A', stderr);
2302 if (key_state & 32)
2303 fputc('2', stderr);
2304 fprintf(stderr, "]: <%d>, %d/%d\n",
2305 button_number,
2306 x + 1,
2307 y + 1);
2308 #else
2309 tt_printf((unsigned char *) "\033[M%c%c%c",
2310 (32 + button_number + key_state),
2311 (32 + x + 1),
2312 (32 + y + 1));
2313 #endif
2314 }
2315
2316 /*{{{ process an X event */
2317 /* PROTO */
2318 void
2319 process_x_event(XEvent * ev)
2320 {
2321 static int bypass_keystate = 0;
2322 int reportmode;
2323 static int csrO = 0; /* Hops - csr offset in thumb/slider */
2324
2325
2326 #if !defined(NO_DEBUG_OUTPUT)
2327 fprintf(stderr, "****************************************************************\n");
2328 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);
2329 #endif
2330
2331 /* to give proper Scroll behaviour */
2332 switch (ev->type) {
2333 case KeyPress:
2334 lookup_key(ev);
2335 break;
2336
2337 case DestroyNotify :
2338 if( ev->xdestroywindow.window == TermWin.vt || ev->xdestroywindow.window == TermWin.parent )
2339 {
2340 #ifdef __CYGWIN__
2341 /* cygwin does not kill shell properly */
2342 /* fprintf( stderr, "cmd_pid = %d\n", cmd_pid ); */
2343 kill( cmd_pid, SIGKILL );
2344 #endif
2345 exit(EXIT_SUCCESS);
2346 }
2347 break ;
2348
2349 case ClientMessage:
2350 if (ev->xclient.format == 32 && ev->xclient.data.l[0] == wmDeleteWindow)
2351 {
2352 #ifdef __CYGWIN__
2353 /* cygwin does not kill shell properly */
2354 /* fprintf( stderr, "cmd_pid = %d\n", cmd_pid ); */
2355 kill( cmd_pid, SIGKILL );
2356 #endif
2357 exit(EXIT_SUCCESS);
2358 }
2359 #ifdef OFFIX_DND
2360 /* OffiX Dnd (drag 'n' drop) protocol */
2361 if (ev->xclient.message_type == DndProtocol &&
2362 ((ev->xclient.data.l[0] == DndFile) ||
2363 (ev->xclient.data.l[0] == DndDir) ||
2364 (ev->xclient.data.l[0] == DndLink))) {
2365 /* Get Dnd data */
2366 Atom ActualType;
2367 int ActualFormat;
2368 unsigned char *data;
2369 unsigned long Size, RemainingBytes;
2370
2371 XGetWindowProperty(Xdisplay, Xroot,
2372 DndSelection,
2373 0L, 1000000L,
2374 False, AnyPropertyType,
2375 &ActualType, &ActualFormat,
2376 &Size, &RemainingBytes,
2377 &data);
2378 XChangeProperty(Xdisplay, Xroot,
2379 XA_CUT_BUFFER0, XA_STRING,
2380 8, PropModeReplace,
2381 data, strlen(data));
2382 selection_paste(Xroot, XA_CUT_BUFFER0, True);
2383 XSetInputFocus(Xdisplay, Xroot, RevertToNone, CurrentTime);
2384 }
2385 #endif /* OFFIX_DND */
2386 break;
2387
2388 case MappingNotify:
2389 XRefreshKeyboardMapping(&(ev->xmapping));
2390 break;
2391
2392 /* Here's my conclusiion:
2393 * If the window is completely unobscured, use bitblt's
2394 * to scroll. Even then, they're only used when doing partial
2395 * screen scrolling. When partially obscured, we have to fill
2396 * in the GraphicsExpose parts, which means that after each refresh,
2397 * we need to wait for the graphics expose or Noexpose events,
2398 * which ought to make things real slow!
2399 */
2400 case VisibilityNotify:
2401 switch (ev->xvisibility.state) {
2402 case VisibilityUnobscured:
2403 refresh_type = FAST_REFRESH;
2404 break;
2405
2406 case VisibilityPartiallyObscured:
2407 refresh_type = SLOW_REFRESH;
2408 break;
2409
2410 default:
2411 refresh_type = NO_REFRESH;
2412 break;
2413 }
2414 break;
2415
2416 case FocusIn:
2417 if (!TermWin.focus) {
2418 TermWin.focus = 1;
2419 #ifdef OFF_FOCUS_FADING
2420 if( rs_fade != NULL )
2421 {
2422 PixColors = &(PixColorsFocused[0]);
2423 on_colors_changed(Color_bg);
2424 }
2425 #endif
2426 #if !defined(USE_XIM) && !defined(NO_XLOCALE)
2427 if (Input_Context != NULL)
2428 XSetICFocus(Input_Context);
2429 #endif
2430 }
2431 break;
2432
2433 case FocusOut:
2434 if (TermWin.focus) {
2435 TermWin.focus = 0;
2436 #ifdef OFF_FOCUS_FADING
2437 if( rs_fade != NULL )
2438 {
2439 PixColors = &(PixColorsUnFocused[0]);
2440 on_colors_changed(Color_bg);
2441 }
2442 #endif
2443 #if !defined(USE_XIM) && !defined(NO_XLOCALE)
2444 if (Input_Context != NULL)
2445 XUnsetICFocus(Input_Context);
2446 #endif
2447 }
2448 break;
2449
2450 case ConfigureNotify:
2451 while( XCheckTypedWindowEvent( Xdisplay, ev->xconfigure.window, ConfigureNotify, ev ) );
2452 resize_window(ev);
2453 menubar_expose();
2454 break;
2455
2456 case SelectionClear:
2457 selection_clear();
2458 break;
2459
2460 case SelectionNotify:
2461 selection_paste(ev->xselection.requestor, ev->xselection.property, True);
2462 break;
2463
2464 case SelectionRequest:
2465 selection_send(&(ev->xselectionrequest));
2466 break;
2467
2468 case PropertyNotify:
2469 #ifdef DEBUG_BACKGROUND_PMAP
2470 {
2471 char *prop_name = XGetAtomName( Xdisplay, ev->xproperty.atom );
2472 fprintf( stderr, "PropertyNotify : %s(%lX)\n", prop_name, ev->xproperty.atom );
2473 if( prop_name )
2474 XFree( prop_name );
2475 }
2476 #endif
2477 if( ev->xproperty.window == Xroot )
2478 {
2479 #ifdef HAVE_AFTERSTEP
2480 if (ev->xproperty.atom == _AS_STYLE )
2481 {
2482 const char *mystyle_name ;
2483 if( rs_mystyle )
2484 mystyle_name = rs_mystyle ;
2485 else
2486 mystyle_name = GetDefaultMyStyle() ;
2487
2488 mystyle_handle_property_event( ev );
2489 set_mystyle( mystyle_find( mystyle_name ), True );
2490 break;
2491 }else if( ev->xproperty.atom == _XROOTPMAP_ID )
2492 {
2493 if( TermWin.background.trgType == BGT_MyStyle )
2494 {
2495 if( !TransparentMS(TermWin.background.mystyle) )
2496 {
2497 #ifdef DEBUG_BACKGROUND_PMAP
2498 fprintf( stderr, "texture type = %d\n", TermWin.background.mystyle->texture_type );
2499 #endif
2500 break ;
2501 }
2502 destroy_asimage( &Scr.RootImage );
2503 }
2504 }
2505 #endif
2506 #ifdef TRANSPARENT
2507 if( ev->xproperty.atom == _XROOTPMAP_ID )
2508 {
2509 if( IsTransparentPixmap() )
2510 {
2511 Pixmap p;
2512 if( read_32bit_property (Xroot, _XROOTPMAP_ID, &p) )
2513 {
2514 if( p != TermWin.background.srcPixmap )
2515 SetSrcPixmap( p );
2516 }else
2517 ValidateSrcPixmap(True);
2518
2519 #ifdef DEBUG_BACKGROUND_PMAP
2520 fprintf( stderr, "root pmap changed to = %lX\n", TermWin.background.srcPixmap );
2521 #endif
2522
2523 if( TransparentPixmapNeedsUpdate() )
2524 request_background_update();
2525 }else if( get_flags(Options, Opt_transparent) )
2526 {
2527 scr_clear();
2528 scr_touch();
2529 }
2530 }
2531 #endif
2532 if( ev->xproperty.atom == _XA_NET_SUPPORTING_WM_CHECK )
2533 {
2534 check_extended_wm_hints_support();
2535 #ifdef DEBUG_BACKGROUND_PMAP
2536 fprintf( stderr, "WM change. Desktops are %ssupported\n", get_flags( ExtWM.flags, WM_SupportsDesktops )?"":"not " );
2537 #endif
2538 }else if( ev->xproperty.atom == _XA_NET_CURRENT_DESKTOP )
2539 {
2540 if( !read_32bit_property (Xroot, _XA_NET_CURRENT_DESKTOP, &ExtWM.current_desktop) )
2541 clear_flags( ExtWM.flags, WM_SupportsDesktops );
2542 else if( get_flags( ExtWM.flags, WM_ClaimSupportsDesktops ) )
2543 set_flags( ExtWM.flags, WM_SupportsDesktops );
2544
2545
2546 #ifdef DEBUG_BACKGROUND_PMAP
2547 fprintf( stderr, "Curr Desk change to %ld. Desktops are %ssupported\n", ExtWM.current_desktop, get_flags( ExtWM.flags, WM_SupportsDesktops )?"":"not " );
2548 #endif
2549 /* Don't do it here - wait for background change :
2550 * if( TransparentPixmapNeedsUpdate() )
2551 request_background_update();
2552 */
2553 }
2554 }
2555 if( ev->xproperty.window == TermWin.parent )
2556 {
2557 if( ev->xproperty.atom == _XA_NET_WM_DESKTOP )
2558 {
2559 #ifdef DEBUG_BACKGROUND_PMAP
2560 fprintf( stderr, "Aterm Desk change from %ld. Desktops are %ssupported\n", ExtWM.aterm_desktop, get_flags( ExtWM.flags, WM_SupportsDesktops )?"":"not " );
2561 #endif
2562 if( !read_32bit_property (TermWin.parent, _XA_NET_WM_DESKTOP, &ExtWM.aterm_desktop) )
2563 clear_flags( ExtWM.flags, WM_SupportsDesktops );
2564 #ifdef DEBUG_BACKGROUND_PMAP
2565 fprintf( stderr, "Aterm Desk change to %ld. Desktops are %ssupported\n", ExtWM.aterm_desktop, get_flags( ExtWM.flags, WM_SupportsDesktops )?"":"not " );
2566 #endif
2567 if( TransparentPixmapNeedsUpdate() )
2568 request_background_update();
2569 }else if( ev->xproperty.atom == _XA_NET_WM_STATE )
2570 {
2571 if( check_extended_wm_state() )
2572 if( TransparentPixmapNeedsUpdate() )
2573 request_background_update();
2574 #ifdef DEBUG_BACKGROUND_PMAP
2575 fprintf( stderr, "Aterm state change to: %sSticky, %sShaded, %sHidden\n",
2576 get_flags( ExtWM.flags, WM_AtermStateSticky )?"":"not ",
2577 get_flags( ExtWM.flags, WM_AtermStateShaded )?"":"not ",
2578 get_flags( ExtWM.flags, WM_AtermStateHidden )?"":"not " );
2579 #endif
2580
2581 }
2582 }
2583 break;
2584 case UnmapNotify:
2585 TermWin.bMapped = 0 ;
2586 /* fprintf(stderr, "\n aterm is UnMapped(event)");*/
2587 break;
2588
2589 case MapNotify:
2590 /* {
2591 XWindowAttributes attr ;
2592
2593 XGetWindowAttributes( Xdisplay, ParentWin[0], &attr );
2594 TermWin.bMapped = (attr.map_state == IsViewable)?1:0 ;
2595
2596 fprintf(stderr, "\n aterm is %s", (TermWin.bMapped)?"Mapped":"UnMapped");
2597 }*/
2598 /* fprintf(stderr, "\n aterm is %s", (TermWin.bMapped)?"Mapped":"UnMapped");*/
2599 TermWin.bMapped = 1 ;
2600 #ifdef TRANSPARENT
2601 if ( TransparentPixmapNeedsUpdate() )
2602 request_background_update();
2603 #endif
2604 #if 0
2605 refresh_transparent_scrollbar();
2606 if( Options & Opt_transparent)
2607 {
2608 Pixmap tmp = GetRootPixmap(None);
2609 if( tmp != TermWin.background.srcPixmap && TermWin.background.srcPixmap != None )
2610 {
2611 if( TermWin.background.trgType != BGT_None )
2612 {
2613 SetSrcPixmap(tmp);
2614 RenderPixmap(0);
2615 }
2616 scr_clear();
2617 scr_touch();
2618 }
2619 }
2620 #endif
2621 break ;
2622 case ReparentNotify:
2623 #ifdef TRANSPARENT
2624 {
2625 int n;
2626 XWindowAttributes attr ;
2627 int (*oldXErrorHandler) (Display *, XErrorEvent *) =
2628 XSetErrorHandler (pixmap_error_handler);
2629
2630 for( n = 1 ; n < PARENTS_NUM ; n ++ ) ParentWin[n] = None;
2631 /*
2632 * Make the frame window set by the window manager have
2633 * the root background. Some window managers put few nested frame
2634 * windows for each client, so we have to take care about that.
2635 */
2636 for( ParentWinNum = 1 ;ParentWinNum<PARENTS_NUM; ParentWinNum++)
2637 {
2638 Window root;
2639 Window parent;
2640 Window *list;
2641
2642 XQueryTree(Xdisplay, ParentWin[ParentWinNum-1], &root, &parent, &list, &n);
2643 XFree(list);
2644 if (parent == Xroot) break;
2645 ParentWin[ParentWinNum] = parent;
2646 /* we want to get all map/unmap events from this window as well*/
2647 /* XSelectInput(Xdisplay, parent, StructureNotifyMask); */
2648
2649 if (Options & Opt_transparent)
2650 XSetWindowBackgroundPixmap(Xdisplay, parent, ParentRelative);
2651 }
2652 if( XGetWindowAttributes( Xdisplay, ParentWin[0], &attr ))
2653 TermWin.bMapped = (attr.map_state == IsViewable)?1:0 ;
2654
2655 XSetErrorHandler (oldXErrorHandler);
2656 scr_clear();
2657 }
2658 #endif /* TRANSPARENT */
2659 break;
2660 case GraphicsExpose:
2661 case Expose:
2662 if (ev->xany.window == TermWin.vt) {
2663 #if !defined(NO_DEBUG_OUTPUT)
2664 fprintf(stderr, "exposed area = %dx%d%+d%+d\n", ev->xexpose.x, ev->xexpose.y,
2665 ev->xexpose.width, ev->xexpose.height);
2666 #endif
2667 scr_expose(ev->xexpose.x, ev->xexpose.y,
2668 ev->xexpose.width, ev->xexpose.height);
2669 } else {
2670 XEvent unused_xevent;
2671
2672 while (XCheckTypedWindowEvent(Xdisplay, ev->xany.window,
2673 Expose,
2674 &unused_xevent)) ;
2675 while (XCheckTypedWindowEvent(Xdisplay, ev->xany.window,
2676 GraphicsExpose,
2677 &unused_xevent)) ;
2678 if (isScrollbarWindow(ev->xany.window)) {
2679 scrollbar_setNone();
2680 scrollbar_show(0);
2681 }
2682 if (menubar_visible() && isMenuBarWindow(ev->xany.window))
2683 menubar_expose();
2684 Gr_expose(ev->xany.window);
2685 }
2686 break;
2687
2688 case ButtonPress:
2689 bypass_keystate = (ev->xbutton.state & (Mod1Mask | ShiftMask));
2690 reportmode = (bypass_keystate ?
2691 0 : (PrivateModes & PrivMode_mouse_report));
2692
2693 if (ev->xany.window == TermWin.vt) {
2694 if (ev->xbutton.subwindow != None)
2695 Gr_ButtonPress(ev->xbutton.x, ev->xbutton.y);
2696 else {
2697 if (reportmode) {
2698 /* mouse report from vt window */
2699 /* save the xbutton state (for ButtonRelease) */
2700 MEvent.state = ev->xbutton.state;
2701 #ifdef MOUSE_REPORT_DOUBLECLICK
2702 if (ev->xbutton.button == MEvent.button
2703 && (ev->xbutton.time - MEvent.time < MULTICLICK_TIME)) {
2704 /* same button, within alloted time */
2705 MEvent.clicks++;
2706 if (MEvent.clicks > 1) {
2707 /* only report double clicks */
2708 MEvent.clicks = 2;
2709 mouse_report(&(ev->xbutton));
2710
2711 /* don't report the release */
2712 MEvent.clicks = 0;
2713 MEvent.button = AnyButton;
2714 }
2715 } else {
2716 /* different button, or time expired */
2717 MEvent.clicks = 1;
2718 MEvent.button = ev->xbutton.button;
2719 mouse_report(&(ev->xbutton));
2720 }
2721 #else
2722 MEvent.button = ev->xbutton.button;
2723 mouse_report(&(ev->xbutton));
2724 #endif /* MOUSE_REPORT_DOUBLECLICK */
2725 } else {
2726 if (ev->xbutton.button != MEvent.button)
2727 MEvent.clicks = 0;
2728 switch (ev->xbutton.button) {
2729 case Button1:
2730 if (MEvent.button == Button1
2731 && (ev->xbutton.time - MEvent.time < MULTICLICK_TIME))
2732 MEvent.clicks++;
2733 else
2734 MEvent.clicks = 1;
2735 selection_click(MEvent.clicks, ev->xbutton.x,
2736 ev->xbutton.y);
2737 MEvent.button = Button1;
2738 break;
2739
2740 case Button3:
2741 if (MEvent.button == Button3
2742 && (ev->xbutton.time - MEvent.time < MULTICLICK_TIME))
2743 selection_rotate(ev->xbutton.x, ev->xbutton.y);
2744 else
2745 selection_extend(ev->xbutton.x, ev->xbutton.y, 1);
2746 MEvent.button = Button3;
2747 break;
2748 }
2749 }
2750 MEvent.time = ev->xbutton.time;
2751 return;
2752 }
2753 }
2754 if (isScrollbarWindow(ev->xany.window)) {
2755 scrollbar_setNone();
2756 /*
2757 * Rxvt-style scrollbar:
2758 * move up if mouse is above slider
2759 * move dn if mouse is below slider
2760 *
2761 * XTerm-style scrollbar:
2762 * Move display proportional to pointer location
2763 * pointer near top -> scroll one line
2764 * pointer near bot -> scroll full page
2765 */
2766 #ifndef NO_SCROLLBAR_REPORT
2767 if (reportmode) {
2768 /*
2769 * Mouse report disabled scrollbar:
2770 * arrow buttons - send up/down
2771 * click on scrollbar - send pageup/down
2772 */
2773 if (scrollbar_upButton(ev->xbutton.y))
2774 tt_printf((unsigned char *) "\033[A");
2775 else if (scrollbar_dnButton(ev->xbutton.y))
2776 tt_printf((unsigned char *) "\033[B");
2777 else
2778 switch (ev->xbutton.button) {
2779 case Button2:
2780 tt_printf((unsigned char *) "\014");
2781 break;
2782 case Button1:
2783 tt_printf((unsigned char *) "\033[6~");
2784 break;
2785 case Button3:
2786 tt_printf((unsigned char *) "\033[5~");
2787 break;
2788 }
2789 } else
2790 #endif /* NO_SCROLLBAR_REPORT */
2791 {
2792 if (scrollbar_upButton(ev->xbutton.y)) {
2793 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
2794 scroll_arrow_delay = SCROLLBAR_INITIAL_DELAY;
2795 #endif
2796 if (scr_page(UP, 1))
2797 scrollbar_setUp();
2798 } else if (scrollbar_dnButton(ev->xbutton.y)) {
2799 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
2800 scroll_arrow_delay = SCROLLBAR_INITIAL_DELAY;
2801 #endif
2802 if (scr_page(DN, 1))
2803 scrollbar_setDn();
2804 } else
2805 switch (ev->xbutton.button) {
2806 case Button2:
2807 #ifndef FUNKY_SCROLL_BEHAVIOUR
2808 csrO = (scrollBar.bot - scrollBar.top) / 2;
2809 /* align to thumb center */
2810 #else
2811 # ifndef XTERM_SCROLLBAR
2812 if (scrollbar_above_slider(ev->xbutton.y) ||
2813 scrollbar_below_slider(ev->xbutton.y))
2814 # endif
2815 #endif /* !FUNKY_SCROLL_BEHAVIOUR */
2816 scr_move_to(scrollbar_position(ev->xbutton.y) - csrO,
2817 scrollbar_size());
2818 scrollbar_setMotion();
2819 break;
2820
2821 case Button1:
2822 #ifndef FUNKY_SCROLL_BEHAVIOUR
2823 csrO = ev->xbutton.y - scrollBar.top;
2824 /* ptr ofset in thumb */
2825 #endif
2826 /*drop */
2827
2828 case Button3:
2829 #ifndef XTERM_SCROLLBAR
2830 if (scrollbar_above_slider(ev->xbutton.y))
2831 # ifdef RXVT_SCROLL_FULL
2832 scr_page(UP, TermWin.nrow - 1);
2833 # else
2834 scr_page(UP, TermWin.nrow / 4);
2835 # endif
2836 else if (scrollbar_below_slider(ev->xbutton.y))
2837 # ifdef RXVT_SCROLL_FULL
2838 scr_page(DN, TermWin.nrow - 1);
2839 # else
2840 scr_page(DN, TermWin.nrow / 4);
2841 # endif
2842 else
2843 scrollbar_setMotion();
2844 #else /* XTERM_SCROLLBAR */
2845 scr_page((ev->xbutton.button == Button1 ? DN : UP),
2846 (TermWin.nrow *
2847 scrollbar_position(ev->xbutton.y) /
2848 scrollbar_size())
2849 );
2850 #endif /* XTERM_SCROLLBAR */
2851 break;
2852 }
2853 }
2854 return;
2855 }
2856 if (isMenuBarWindow(ev->xany.window)) {
2857 menubar_control(&(ev->xbutton));
2858 return;
2859 }
2860 break;
2861
2862 case ButtonRelease:
2863 csrO = 0; /* reset csr Offset */
2864 reportmode = (bypass_keystate ?
2865 0 : (PrivateModes & PrivMode_mouse_report));
2866
2867 if (scrollbar_isUpDn()) {
2868 scrollbar_setNone();
2869 scrollbar_show(0);
2870 #ifndef NO_SCROLLBAR_BUTTON_CONTINUAL_SCROLLING
2871 refresh_type &= ~SMOOTH_REFRESH;
2872 #endif
2873 }
2874 if (ev->xany.window == TermWin.vt) {
2875 if (ev->xbutton.subwindow != None)
2876 Gr_ButtonRelease(ev->xbutton.x, ev->xbutton.y);
2877 else {
2878 if (reportmode) {
2879 /* Don't report release events for the mouse wheel */
2880 if (ev->xbutton.button == Button4 || ev->xbutton.button == Button5)
2881 return;
2882 /* mouse report from vt window */
2883 #ifdef MOUSE_REPORT_DOUBLECLICK
2884 /* only report the release of 'slow' single clicks */
2885 if (MEvent.button != AnyButton &&
2886 (ev->xbutton.button != MEvent.button ||
2887 (ev->xbutton.time - MEvent.time > MULTICLICK_TIME / 2))
2888 ) {
2889 MEvent.clicks = 0;
2890 MEvent.button = AnyButton;
2891 mouse_report(&(ev->xbutton));
2892 }
2893 #else /* MOUSE_REPORT_DOUBLECLICK */
2894 MEvent.button = AnyButton;
2895 mouse_report(&(ev->xbutton));
2896 #endif /* MOUSE_REPORT_DOUBLECLICK */
2897 return;
2898 }
2899 /*
2900 * dumb hack to compensate for the failure of click-and-drag
2901 * when overriding mouse reporting
2902 */
2903 if ((PrivateModes & PrivMode_mouse_report) &&
2904 (bypass_keystate) &&
2905 (ev->xbutton.button == Button1) &&
2906 (MEvent.clicks <= 1))
2907 selection_extend(ev->xbutton.x, ev->xbutton.y, 0);
2908
2909 switch (ev->xbutton.button) {
2910 case Button1:
2911 case Button3:
2912 selection_make(ev->xbutton.time, ev->xbutton.state);
2913 break;
2914
2915 case Button2:
2916 selection_request(ev->xbutton.time,
2917 ev->xbutton.x, ev->xbutton.y);
2918 break;
2919 #ifndef NO_MOUSE_WHEEL
2920 case Button4:
2921 scr_page(UP, (ev->xbutton.state & ShiftMask) ? 1 : 5);
2922 break;
2923 case Button5:
2924 scr_page(DN, (ev->xbutton.state & ShiftMask) ? 1 : 5);
2925 break;
2926 #endif
2927 }
2928 }
2929 } else if (isMenuBarWindow(ev->xany.window)) {
2930 menubar_control(&(ev->xbutton));
2931 }
2932 break;
2933
2934 case MotionNotify:
2935 if (isMenuBarWindow(ev->xany.window)) {
2936 menubar_control(&(ev->xbutton));
2937 break;
2938 }
2939 if ((PrivateModes & PrivMode_mouse_report) && !(bypass_keystate))
2940 break;
2941
2942 if (ev->xany.window == TermWin.vt) {
2943 if ((ev->xbutton.state & (Button1Mask | Button3Mask))) {
2944 Window unused_root, unused_child;
2945 int unused_root_x, unused_root_y;
2946 unsigned int unused_mask;
2947
2948 while (XCheckTypedWindowEvent(Xdisplay, TermWin.vt,
2949 MotionNotify, ev)) ;
2950 XQueryPointer(Xdisplay, TermWin.vt,
2951 &unused_root, &unused_child,
2952 &unused_root_x, &unused_root_y,
2953 &(ev->xbutton.x), &(ev->xbutton.y),
2954 &unused_mask);
2955 #ifdef MOUSE_THRESHOLD
2956 /* deal with a `jumpy' mouse */
2957 if ((ev->xmotion.time - MEvent.time) > MOUSE_THRESHOLD)
2958 #endif
2959 selection_extend((ev->xbutton.x), (ev->xbutton.y),
2960 (ev->xbutton.state & Button3Mask) ? 2 : 0);
2961 }
2962 } else if ((ev->xany.window == scrollBar.win) && scrollbar_isMotion()) {
2963 Window unused_root, unused_child;
2964 int unused_root_x, unused_root_y;
2965 unsigned int unused_mask;
2966
2967 while (XCheckTypedWindowEvent(Xdisplay, scrollBar.win,
2968 MotionNotify, ev)) ;
2969 XQueryPointer(Xdisplay, scrollBar.win,
2970 &unused_root, &unused_child,
2971 &unused_root_x, &unused_root_y,
2972 &(ev->xbutton.x), &(ev->xbutton.y),
2973 &unused_mask);
2974 scr_move_to(scrollbar_position(ev->xbutton.y) - csrO,
2975 scrollbar_size());
2976 scr_refresh(refresh_type);
2977 refresh_count = refresh_limit = 0;
2978 scrollbar_show(1);
2979 #ifdef USE_XIM
2980 IMSendSpot();
2981 #endif
2982 }
2983 break;
2984 }
2985 }
2986 /*}}} */
2987
2988 /*
2989 * Send printf() formatted output to the command.
2990 * Only use for small ammounts of data.
2991 */
2992 /* PROTO */
2993 void
2994 tt_printf(const unsigned char *fmt,...)
2995 {
2996 static unsigned char buf[TT_PRINTF_LIMIT];
2997 va_list arg_ptr;
2998
2999 va_start(arg_ptr, fmt);
3000 vsprintf((char *) buf, (char *) fmt, arg_ptr);
3001 va_end(arg_ptr);
3002 tt_write(buf, strlen(buf));
3003 }
3004
3005 /*}}} */
3006
3007 /*{{{ print pipe */
3008 /*----------------------------------------------------------------------*/
3009 #ifdef PRINTPIPE
3010 /* PROTO */
3011 FILE *
3012 popen_printer(void)
3013 {
3014 FILE *stream = popen(rs_print_pipe, "w");
3015
3016 if (stream == NULL)
3017 print_error("can't open printer pipe");
3018 return stream;
3019 }
3020
3021 /* PROTO */
3022 int
3023 pclose_printer(FILE * stream)
3024 {
3025 fflush(stream);
3026 /* pclose() reported not to work on SunOS 4.1.3 */
3027 #if defined (__sun__) /* TODO: RESOLVE THIS */
3028 /* pclose works provided SIGCHLD handler uses waitpid */
3029 return pclose(stream); /* return fclose (stream); */
3030 #else
3031 return pclose(stream);
3032 #endif
3033 }
3034
3035 /*
3036 * simulate attached vt100 printer
3037 */
3038 /* PROTO */
3039 void
3040 process_print_pipe(void)
3041 {
3042 int done;
3043 FILE *fd;
3044
3045 if ((fd = popen_printer()) == NULL)
3046 return;
3047
3048 /*
3049 * Send all input to the printer until either ESC[4i or ESC[?4i
3050 * is received.
3051 */
3052 for (done = 0; !done;) {
3053 unsigned char buf[8];
3054 unsigned char ch;
3055 unsigned int i, len;
3056
3057 if ((ch = cmd_getc()) != '\033') {
3058 if (putc(ch, fd) == EOF)
3059 break; /* done = 1 */
3060 } else {
3061 len = 0;
3062 buf[len++] = ch;
3063
3064 if ((buf[len++] = cmd_getc()) == '[') {
3065 if ((ch = cmd_getc()) == '?') {
3066 buf[len++] = '?';
3067 ch = cmd_getc();
3068 }
3069 if ((buf[len++] = ch) == '4') {
3070 if ((buf[len++] = cmd_getc()) == 'i')
3071 break; /* done = 1 */
3072 }
3073 }
3074 for (i = 0; i < len; i++)
3075 if (putc(buf[i], fd) == EOF) {
3076 done = 1;
3077 break;
3078 }
3079 }
3080 }
3081 pclose_printer(fd);
3082 }
3083 #endif /* PRINTPIPE */
3084 /*}}} */
3085
3086 /*{{{ process escape sequences */
3087 /* PROTO */
3088 void
3089 process_escape_seq(void)
3090 {
3091 unsigned char ch = cmd_getc();
3092
3093 switch (ch) {
3094 /* case 1: do_tek_mode (); break; */
3095 case '#':
3096 if (cmd_getc() == '8')
3097 scr_E();
3098 break;
3099 case '(':
3100 scr_charset_set(0, cmd_getc());
3101 break;
3102 case ')':
3103 scr_charset_set(1, cmd_getc());
3104 break;
3105 case '*':
3106 scr_charset_set(2, cmd_getc());
3107 break;
3108 case '+':
3109 scr_charset_set(3, cmd_getc());
3110 break;
3111 #ifdef MULTICHAR_SET
3112 case '$':
3113 scr_charset_set(-2, cmd_getc());
3114 break;
3115 #endif
3116 case '7':
3117 scr_cursor(SAVE);
3118 break;
3119 case '8':
3120 scr_cursor(RESTORE);
3121 break;
3122 case '=':
3123 case '>':
3124 PrivMode((ch == '='), PrivMode_aplKP);
3125 break;
3126 case '@':
3127 (void)cmd_getc();
3128 break;
3129 case 'D':
3130 scr_index(UP);
3131 break;
3132 case 'E':
3133 scr_add_lines((unsigned char *) "\n\r", 1, 2);
3134 break;
3135 case 'G':
3136 process_graphics();
3137 break;
3138 case 'H':
3139 scr_set_tab(1);
3140 break;
3141 case 'M':
3142 scr_index(DN);
3143 break;
3144 /*case 'N': scr_single_shift (2); break; */
3145 /*case 'O': scr_single_shift (3); break; */
3146 case 'Z':
3147 tt_printf((unsigned char *) ESCZ_ANSWER);
3148 break; /* steal obsolete ESC [ c */
3149 case '[':
3150 process_csi_seq();
3151 break;
3152 case ']':
3153 process_xterm_seq();
3154 break;
3155 case 'c':
3156 scr_poweron();
3157 break;
3158 case 'n':
3159 scr_charset_choose(2);
3160 break;
3161 case 'o':
3162 scr_charset_choose(3);
3163 break;
3164 }
3165 }
3166 /*}}} */
3167
3168 /*{{{ process CSI (code sequence introducer) sequences `ESC[' */
3169 /* PROTO */
3170 void
3171 process_csi_seq(void)
3172 {
3173 unsigned char ch, priv;
3174 unsigned int nargs;
3175 int arg[ESC_ARGS];
3176
3177 nargs = 0;
3178 arg[0] = 0;
3179 arg[1] = 0;
3180
3181 priv = 0;
3182 ch = cmd_getc();
3183 if (ch >= '<' && ch <= '?') {
3184 priv = ch;
3185 ch = cmd_getc();
3186 }
3187 /* read any numerical arguments */
3188 do {
3189 int n;
3190
3191 for (n = 0; isdigit(ch); ch = cmd_getc())
3192 n = n * 10 + (ch - '0');
3193
3194 if (nargs < ESC_ARGS)
3195 arg[nargs++] = n;
3196 if (ch == '\b') {
3197 scr_backspace();
3198 } else if (ch == 033) {
3199 process_escape_seq();
3200 return;
3201 } else if (ch < ' ') {
3202 scr_add_lines(&ch, 0, 1);
3203 return;
3204 }
3205 if (ch < '@')
3206 ch = cmd_getc();
3207 }
3208 while (ch >= ' ' && ch < '@');
3209 if (ch == 033) {
3210 process_escape_seq();
3211 return;
3212 } else if (ch < ' ')
3213 return;
3214
3215 switch (ch) {
3216 #ifdef PRINTPIPE
3217 case 'i': /* printing */
3218 switch (arg[0]) {
3219 case 0:
3220 scr_printscreen(0);
3221 break;
3222 case 5:
3223 process_print_pipe();
3224 break;
3225 }
3226 break;
3227 #endif
3228 case 'A':
3229 case 'e': /* up <n> */
3230 scr_gotorc((arg[0] ? -arg[0] : -1), 0, RELATIVE);
3231 break;
3232 case 'B': /* down <n> */
3233 scr_gotorc((arg[0] ? +arg[0] : +1), 0, RELATIVE);
3234 break;
3235 case 'C':
3236 case 'a': /* right <n> */
3237 scr_gotorc(0, (arg[0] ? +arg[0] : +1), RELATIVE);
3238 break;
3239 case 'D': /* left <n> */
3240 scr_gotorc(0, (arg[0] ? -arg[0] : -1), RELATIVE);
3241 break;
3242 case 'E': /* down <n> & to first column */
3243 scr_gotorc((arg[0] ? +arg[0] : +1), 0, R_RELATIVE);
3244 break;
3245 case 'F': /* up <n> & to first column */
3246 scr_gotorc((arg[0] ? -arg[0] : -1), 0, R_RELATIVE);
3247 break;
3248 case 'G':
3249 case '`': /* move to col <n> */
3250 scr_gotorc(0, (arg[0] ? arg[0] - 1 : +1), R_RELATIVE);
3251 break;
3252 case 'd': /* move to row <n> */
3253 scr_gotorc((arg[0] ? arg[0] - 1 : +1), 0, C_RELATIVE);
3254 break;
3255 case 'H':
3256 case 'f': /* position cursor */
3257 switch (nargs) {
3258 case 0:
3259 scr_gotorc(0, 0, 0);
3260 break;
3261 case 1:
3262 scr_gotorc((arg[0] ? arg[0] - 1 : 0), 0, 0);
3263 break;
3264 default:
3265 scr_gotorc(arg[0] - 1, arg[1] - 1, 0);
3266 break;
3267 }
3268 break;
3269 case 'I':
3270 scr_tab(arg[0] ? +arg[0] : +1);
3271 break;
3272 case 'Z':
3273 scr_tab(arg[0] ? -arg[0] : -1);
3274 break;
3275 case 'J':
3276 scr_erase_screen(arg[0]);
3277 break;
3278 case 'K':
3279 scr_erase_line(arg[0]);
3280 break;
3281 case '@':
3282 scr_insdel_chars((arg[0] ? arg[0] : 1), INSERT);
3283 break;
3284 case 'L':
3285 scr_insdel_lines((arg[0] ? arg[0] : 1), INSERT);
3286 break;
3287 case 'M':
3288 scr_insdel_lines((arg[0] ? arg[0] : 1), DELETE);
3289 break;
3290 case 'X':
3291 scr_insdel_chars((arg[0] ? arg[0] : 1), ERASE);
3292 break;
3293 case 'P':
3294 scr_insdel_chars((arg[0] ? arg[0] : 1), DELETE);
3295 break;
3296
3297 case 'c':
3298 tt_printf((unsigned char *) VT100_ANS);
3299 break;
3300 case 'm':
3301 process_sgr_mode(nargs, arg);
3302 break;
3303 case 'n': /* request for information */
3304 switch (arg[0]) {
3305 case 5:
3306 tt_printf((unsigned char *) "\033[0n");
3307 break; /* ready */
3308 case 6:
3309 scr_report_position();
3310 break;
3311 #if defined (ENABLE_DISPLAY_ANSWER)
3312 case 7:
3313 if( strlen(display_name) < TT_PRINTF_LIMIT-2 )
3314 tt_printf((unsigned char *) "%s\n", display_name);
3315 break;
3316 #endif
3317 case 8:
3318 xterm_seq(XTerm_title, APL_NAME "-" VERSION);
3319 break;
3320 }
3321 break;
3322 case 'r': /* set top and bottom margins */
3323 if (priv != '?') {
3324 if (nargs < 2 || arg[0] >= arg[1])
3325 scr_scroll_region(0, 10000);
3326 else
3327 scr_scroll_region(arg[0] - 1, arg[1] - 1);
3328 break;
3329 }
3330 /* drop */
3331 case 's':
3332 case 't':
3333 if(arg[0] == 21)
3334 tt_printf((unsigned char *) "\033]l%s\033\\", rs_title);
3335 break;
3336 case 'h':
3337 case 'l':
3338 process_terminal_mode(ch, priv, nargs, arg);
3339 break;
3340 case 'g':
3341 switch (arg[0]) {
3342 case 0:
3343 scr_set_tab(0);
3344 break; /* delete tab */
3345 case 3:
3346 scr_set_tab(-1);
3347 break; /* clear all tabs */
3348 }
3349 break;
3350 case 'W':
3351 switch (arg[0]) {
3352 case 0:
3353 scr_set_tab(1);
3354 break; /* = ESC H */
3355 case 2:
3356 scr_set_tab(0);
3357 break; /* = ESC [ 0 g */
3358 case 5:
3359 scr_set_tab(-1);
3360 break; /* = ESC [ 3 g */
3361 }
3362 break;
3363 }
3364 }
3365 /*}}} */
3366
3367 /*{{{ process xterm text parameters sequences `ESC ] Ps ; Pt BEL' */
3368 /* PROTO */
3369 void
3370 process_xterm_seq(void)
3371 {
3372 unsigned char ch, string[STRING_MAX];
3373 int arg;
3374
3375 ch = cmd_getc();
3376 for (arg = 0; isdigit(ch); ch = cmd_getc())
3377 arg = arg * 10 + (ch - '0');
3378
3379 if (ch == ';') {
3380 int n = 0;
3381
3382 while ((ch = cmd_getc()) != 007) {
3383 if (ch) {
3384 if (ch == '\t')
3385 ch = ' '; /* translate '\t' to space */
3386 else if (ch < ' ')
3387 return; /* control character - exit */
3388
3389 if (n < sizeof(string) - 1)
3390 string[n++] = ch;
3391 }
3392 }
3393 string[n] = '\0';
3394 /*
3395 * menubar_dispatch() violates the constness of the string,
3396 * so do it here
3397 */
3398 if (arg == XTerm_Menu)
3399 menubar_dispatch((char *) string);
3400 else
3401 xterm_seq(arg, (char *) string);
3402 }
3403 }
3404 /*}}} */
3405
3406 /*{{{ process DEC private mode sequences `ESC [ ? Ps mode' */
3407 /*
3408 * mode can only have the following values:
3409 * 'l' = low
3410 * 'h' = high
3411 * 's' = save
3412 * 'r' = restore
3413 * 't' = toggle
3414 * so no need for fancy checking
3415 */
3416 /* PROTO */
3417 void
3418 process_terminal_mode(int mode, int priv, unsigned int nargs, int arg[])
3419 {
3420 unsigned int i;
3421 int state;
3422
3423 if (nargs == 0)
3424 return;
3425
3426 /* make lo/hi boolean */
3427 switch (mode) {
3428 case 'l':
3429 mode = 0;
3430 break;
3431 case 'h':
3432 mode = 1;
3433 break;
3434 }
3435
3436 switch (priv) {
3437 case 0:
3438 if (mode && mode != 1)
3439 return; /* only do high/low */
3440 for (i = 0; i < nargs; i++)
3441 switch (arg[i]) {
3442 case 4:
3443 scr_insert_mode(mode);
3444 break;
3445 /* case 38: TEK mode */
3446 }
3447 break;
3448
3449 #define PrivCases(bit) \
3450 if (mode == 't') \
3451 state = !(PrivateModes & bit); \
3452 else \
3453 state = mode; \
3454 switch (state) { \
3455 case 's': \
3456 SavedModes |= (PrivateModes & bit); \
3457 continue; \
3458 break; \
3459 case 'r': \
3460 state = (SavedModes & bit) ? 1 : 0; \
3461 /* FALLTHROUGH */ \
3462 default: \
3463 PrivMode (state, bit); \
3464 }
3465
3466 case '?':
3467 for (i = 0; i < nargs; i++)
3468 switch (arg[i]) {
3469 case 1: /* application cursor keys */
3470 PrivCases(PrivMode_aplCUR);
3471 break;
3472
3473 /* case 2: - reset charsets to USASCII */
3474
3475 case 3: /* 80/132 */
3476 PrivCases(PrivMode_132);
3477 if (PrivateModes & PrivMode_132OK)
3478 set_width(state ? 132 : 80);
3479 break;
3480
3481 /* case 4: - smooth scrolling */
3482
3483 case 5: /* reverse video */
3484 PrivCases(PrivMode_rVideo);
3485 scr_rvideo_mode(state);
3486 break;
3487
3488 case 6: /* relative/absolute origins */
3489 PrivCases(PrivMode_relOrigin);
3490 scr_relative_origin(state);
3491 break;
3492
3493 case 7: /* autowrap */
3494 PrivCases(PrivMode_Autowrap);
3495 scr_autowrap(state);
3496 break;
3497
3498 /* case 8: - auto repeat, can't do on a per window basis */
3499
3500 case 9: /* X10 mouse reporting */
3501 PrivCases(PrivMode_MouseX10);
3502 /* orthogonal */
3503 if (PrivateModes & PrivMode_MouseX10)
3504 PrivateModes &= ~(PrivMode_MouseX11);
3505 break;
3506 # ifdef menuBar_esc
3507 case menuBar_esc:
3508 PrivCases(PrivMode_menuBar);
3509 map_menuBar(state);
3510 break;
3511 # endif
3512 #ifdef scrollBar_esc
3513 case scrollBar_esc:
3514 PrivCases(PrivMode_scrollBar);
3515 map_scrollBar(state);
3516 break;
3517 #endif
3518 case 25: /* visible/invisible cursor */
3519 PrivCases(PrivMode_VisibleCursor);
3520 scr_cursor_visible(state);
3521 break;
3522
3523 case 35:
3524 PrivCases(PrivMode_ShiftKeys);
3525 break;
3526
3527 case 40: /* 80 <--> 132 mode */
3528 PrivCases(PrivMode_132OK);
3529 break;
3530
3531 case 47: /* secondary screen */
3532 PrivCases(PrivMode_Screen);
3533 scr_change_screen(state);
3534 break;
3535
3536 case 66: /* application key pad */
3537 PrivCases(PrivMode_aplKP);
3538 break;
3539
3540 case 67:
3541 #ifndef NO_BACKSPACE_KEY
3542 if (PrivateModes & PrivMode_HaveBackSpace) {
3543 PrivCases(PrivMode_BackSpace);
3544 }
3545 #endif
3546 break;
3547
3548 case 1000: /* X11 mouse reporting */
3549 PrivCases(PrivMode_MouseX11);
3550 /* orthogonal */
3551 if (PrivateModes & PrivMode_MouseX11)
3552 PrivateModes &= ~(PrivMode_MouseX10);
3553 break;
3554 #if 0
3555 case 1001:
3556 break; /* X11 mouse highlighting */
3557 #endif
3558 case 1010: /* scroll to bottom on TTY output inhibit */
3559 PrivCases(PrivMode_TtyOutputInh);
3560 if (PrivateModes & PrivMode_TtyOutputInh)
3561 Options &= ~Opt_scrollTtyOutput;
3562 else
3563 Options |= Opt_scrollTtyOutput;
3564 break;
3565 case 1011: /* scroll to bottom on key press */
3566 PrivCases(PrivMode_Keypress);
3567 if (PrivateModes & PrivMode_Keypress)
3568 Options |= Opt_scrollKeypress;
3569 else
3570 Options &= ~Opt_scrollKeypress;
3571 break;
3572 }
3573 #undef PrivCases
3574 break;
3575 }
3576 }
3577 /*}}} */
3578
3579 /*{{{ process sgr sequences */
3580 /* PROTO */
3581 void
3582 process_sgr_mode(unsigned int nargs, int arg[])
3583 {
3584 unsigned int i;
3585
3586 if (nargs == 0) {
3587 scr_rendition(0, ~RS_None);
3588 return;
3589 }
3590 for (i = 0; i < nargs; i++)
3591 switch (arg[i]) {
3592 case 0:
3593 scr_rendition(0, ~RS_None);
3594 break;
3595 case 1:
3596 scr_rendition(1, RS_Bold);
3597 break;
3598 case 4:
3599 scr_rendition(1, RS_Uline);
3600 break;
3601 case 5:
3602 scr_rendition(1, RS_Blink);
3603 break;
3604 case 7:
3605 scr_rendition(1, RS_RVid);
3606 break;
3607 case 22:
3608 scr_rendition(0, RS_Bold);
3609 break;
3610 case 24:
3611 scr_rendition(0, RS_Uline);
3612 break;
3613 case 25:
3614 scr_rendition(0, RS_Blink);
3615 break;
3616 case 27:
3617 scr_rendition(0, RS_RVid);
3618 break;
3619
3620 case 30:
3621 case 31: /* set fg color */
3622 case 32:
3623 case 33:
3624 case 34:
3625 case 35:
3626 case 36:
3627 case 37:
3628 scr_color(minCOLOR + (arg[i] - 30), RS_Bold);
3629 break;
3630 case 39: /* default fg */
3631 scr_color(restoreFG, RS_Bold);
3632 break;
3633
3634 case 40:
3635 case 41: /* set bg color */
3636 case 42:
3637 case 43:
3638 case 44:
3639 case 45:
3640 case 46:
3641 case 47:
3642 scr_color(minCOLOR + (arg[i] - 40), RS_Blink);
3643 break;
3644 case 49: /* default bg */
3645 scr_color(restoreBG, RS_Blink);
3646 break;
3647 }
3648 }
3649 /*}}} */
3650
3651 /*{{{ process Rob Nation's own graphics mode sequences */
3652 /* PROTO */
3653 void
3654 process_graphics(void)
3655 {
3656 unsigned char ch, cmd = cmd_getc();
3657
3658 #ifndef RXVT_GRAPHICS
3659 if (cmd == 'Q') { /* query graphics */
3660 tt_printf((unsigned char *) "\033G0\n"); /* no graphics */
3661 return;
3662 }
3663 /* swallow other graphics sequences until terminating ':' */
3664 do
3665 ch = cmd_getc();
3666 while (ch != ':');
3667 #else
3668 int nargs;
3669 int args[NGRX_PTS];
3670 unsigned char *text = NULL;
3671
3672 if (cmd == 'Q') { /* query graphics */
3673 tt_printf((unsigned char *) "\033G1\n"); /* yes, graphics (color) */
3674 return;
3675 }
3676 for (nargs = 0; nargs < (sizeof(args) / sizeof(args[0])) - 1; ) {
3677 int neg;
3678
3679 ch = cmd_getc();
3680 neg = (ch == '-');
3681 if (neg || ch == '+')
3682 ch = cmd_getc();
3683
3684 for (args[nargs] = 0; isdigit(ch); ch = cmd_getc())
3685 args[nargs] = args[nargs] * 10 + (ch - '0');
3686 if (neg)
3687 args[nargs] = -args[nargs];
3688
3689 nargs++;
3690 args[nargs] = 0;
3691 if (ch != ';')
3692 break;
3693 }
3694
3695 if ((cmd == 'T') && (nargs >= 5)) {
3696 int i, len = args[4];
3697
3698 text = MALLOC((len + 1) * sizeof(char));
3699
3700 if (text != NULL) {
3701 for (i = 0; i < len; i++)
3702 text[i] = cmd_getc();
3703 text[len] = '\0';
3704 }
3705 }
3706 Gr_do_graphics(cmd, nargs, args, text);
3707 #ifdef USE_XIM
3708 IMSendSpot();
3709 #endif
3710 #endif
3711 }
3712 /*}}} */
3713
3714 /*{{{ Read and process output from the application */
3715 /* PROTO */
3716 void
3717 main_loop(void)
3718 {
3719 int ch;
3720 do {
3721 while ((ch = cmd_getc()) == 0) ; /* wait for something */
3722 /* fprintf( stderr, "%d: cmd_getc returned 0x%x(%c)\n", ++i, ch, isprint(ch)?ch:' ' ); */
3723 if (ch >= ' ' || ch == '\t' || ch == '\n' || ch == '\r') {
3724 /* Read a text string from the input buffer */
3725 int nlines = 0;
3726 unsigned char *str;
3727
3728 /*
3729 * point to the start of the string,
3730 * decrement first since it was post incremented in cmd_getc()
3731 */
3732 str = --cmdbuf_ptr;
3733 while (cmdbuf_ptr < cmdbuf_endp)
3734 {
3735 ch = *cmdbuf_ptr++;
3736 /* fprintf( stderr, "%d: cmd_getc returned 0x%x(%c)\n", ++i, ch, isprint(ch)?ch:' ' ); */
3737 if (ch >= ' ' || ch == '\t' )
3738 {
3739 /* nothing */
3740 } else if (ch == '\r')
3741 { /* we better flush that stuff to create an
3742 * impression that there is something going on */
3743 if( cmdbuf_ptr >= cmdbuf_endp ||
3744 (*cmdbuf_ptr != '\n' && *cmdbuf_ptr != '\r'))
3745 break;
3746 } else if (ch == '\n')
3747 {
3748 nlines++;
3749 if (++refresh_count >= (refresh_limit * (TermWin.nrow - 1)))
3750 break;
3751 } else { /* unprintable */
3752 cmdbuf_ptr--;
3753 break;
3754 }
3755 }
3756 scr_add_lines(str, nlines, (cmdbuf_ptr - str));
3757 if( ch == '\r' )
3758 {
3759 scr_refresh(refresh_type);
3760 }
3761 } else {
3762 switch (ch) {
3763 case 005: /* terminal Status */
3764 tt_printf((unsigned char *) VT100_ANS);
3765 break;
3766 case 007: /* bell */
3767 scr_bell();
3768 break;
3769 case '\b': /* backspace */
3770 scr_backspace();
3771 break;
3772 case 013: /* vertical tab, form feed */
3773 case 014:
3774 scr_index(UP);
3775 break;
3776 case 016: /* shift out - acs */
3777 scr_charset_choose(1);
3778 break;
3779 case 017: /* shift in - acs */
3780 scr_charset_choose(0);
3781 break;
3782 case 033: /* escape char */
3783 process_escape_seq();
3784 break;
3785 }
3786 }
3787 } while (ch != EOF);
3788 }
3789
3790 /* ---------------------------------------------------------------------- */
3791 /* Addresses pasting large amounts of data and rxvt hang
3792 * code pinched from xterm (v_write()) and applied originally to
3793 * rxvt-2.18 - Hops
3794 * Write data to the pty as typed by the user, pasted with the mouse,
3795 * or generated by us in response to a query ESC sequence.
3796 */
3797 /* PROTO */
3798 void
3799 tt_write(const unsigned char *d, int len)
3800 {
3801 int riten, p;
3802
3803 if (v_bufstr == NULL && len > 0) {
3804 v_buffer = v_bufstr = v_bufptr = MALLOC(len);
3805 v_bufend = v_buffer + len;
3806 }
3807 /*
3808 * Append to the block we already have. Always doing this simplifies the
3809 * code, and isn't too bad, either. If this is a short block, it isn't
3810 * too expensive, and if this is a long block, we won't be able to write
3811 * it all anyway.
3812 */
3813 if (len > 0) {
3814 if (v_bufend < v_bufptr + len) { /* we've run out of room */
3815 if (v_bufstr != v_buffer) {
3816 /* there is unused space, move everything down */
3817 /* possibly overlapping bcopy here */
3818 /* bcopy(v_bufstr, v_buffer, v_bufptr - v_bufstr); */
3819 memcpy(v_buffer, v_bufstr, v_bufptr - v_bufstr);
3820 v_bufptr -= v_bufstr - v_buffer;
3821 v_bufstr = v_buffer;
3822 }
3823 if (v_bufend < v_bufptr + len) {
3824 /* still won't fit: get more space */
3825 /* Don't use XtRealloc because an error is not fatal. */
3826 int size = v_bufptr - v_buffer;
3827
3828 /* save across realloc */
3829 v_buffer = REALLOC(v_buffer, size + len);
3830 if (v_buffer) {
3831 v_bufstr = v_buffer;
3832 v_bufptr = v_buffer + size;
3833 v_bufend = v_bufptr + len;
3834 } else {
3835 /* no memory: ignore entire write request */
3836 print_error("cannot allocate buffer space");
3837 v_buffer = v_bufstr; /* restore clobbered pointer */
3838 }
3839 }
3840 }
3841 if (v_bufend >= v_bufptr + len) { /* new stuff will fit */
3842 memcpy(v_bufptr, d, len); /* bcopy(d, v_bufptr, len); */
3843 v_bufptr += len;
3844 }
3845 }
3846 /*
3847 * Write out as much of the buffer as we can.
3848 * Be careful not to overflow the pty's input silo.
3849 * We are conservative here and only write a small amount at a time.
3850 *
3851 * If we can't push all the data into the pty yet, we expect write
3852 * to return a non-negative number less than the length requested
3853 * (if some data written) or -1 and set errno to EAGAIN,
3854 * EWOULDBLOCK, or EINTR (if no data written).
3855 *
3856 * (Not all systems do this, sigh, so the code is actually
3857 * a little more forgiving.)
3858 */
3859
3860 #define MAX_PTY_WRITE 128 /* 1/2 POSIX minimum MAX_INPUT */
3861
3862 if ((p = v_bufptr - v_bufstr) > 0) {
3863 riten = write(cmd_fd, v_bufstr, p < MAX_PTY_WRITE ? p : MAX_PTY_WRITE);
3864 if (riten < 0)
3865 riten = 0;
3866 v_bufstr += riten;
3867 if (v_bufstr >= v_bufptr) /* we wrote it all */
3868 v_bufstr = v_bufptr = v_buffer;
3869 }
3870 /*
3871 * If we have lots of unused memory allocated, return it
3872 */
3873 if (v_bufend - v_bufptr > 1024) { /* arbitrary hysteresis */
3874 /* save pointers across realloc */
3875 int start = v_bufstr - v_buffer;
3876 int size = v_bufptr - v_buffer;
3877 int allocsize = size ? size : 1;
3878
3879 v_buffer = REALLOC(v_buffer, allocsize);
3880 if (v_buffer) {
3881 v_bufstr = v_buffer + start;
3882 v_bufptr = v_buffer + size;
3883 v_bufend = v_buffer + allocsize;
3884 } else {
3885 /* should we print a warning if couldn't return memory? */
3886 v_buffer = v_bufstr - start; /* restore clobbered pointer */
3887 }
3888 }
3889 }
3890
3891 #ifdef USE_XIM
3892 /* PROTO */
3893 void
3894 setSize( XRectangle *size )
3895 {
3896 size->x = TermWin_internalBorder;
3897 size->y = TermWin_internalBorder;
3898 size->width = Width2Pixel (TermWin.ncol);
3899 size->height = Height2Pixel(TermWin.nrow);
3900 return;
3901 }
3902
3903 /* PROTO */
3904 void
3905 setColor( unsigned long *fg, unsigned long *bg )
3906 {
3907 *fg = PixColors[Color_fg];
3908 *bg = PixColors[Color_bg];
3909 return;
3910 }
3911 /* PROTO */
3912 void
3913 IMSendSpot( void )
3914 {
3915 XPoint spot;
3916 XVaNestedList preedit_attr;
3917 XIMStyle input_style;
3918
3919 if( Input_Context == NULL )
3920 return;
3921 else {
3922 XGetICValues(Input_Context,XNInputStyle,&input_style,NULL);
3923 if (!(input_style & XIMPreeditPosition))
3924 return;
3925 }
3926 setPosition( &spot );
3927
3928 preedit_attr = XVaCreateNestedList( 0, XNSpotLocation, &spot, NULL );
3929 XSetICValues( Input_Context, XNPreeditAttributes, preedit_attr, NULL );
3930 XFree( preedit_attr );
3931 return;
3932 }
3933
3934 /* PROTO */
3935 void
3936 setTermFontSet( void )
3937 {
3938 char *string;
3939 long length, i;
3940
3941 if( TermWin.fontset != NULL ){
3942 XFreeFontSet( Xdisplay, TermWin.fontset );
3943 TermWin.fontset = NULL;
3944 }
3945
3946 length = 0;
3947 for( i = 0 ; i < NFONTS ; i ++){
3948 if( rs_font[ i ] )
3949 length += strlen( rs_font[ i ] ) + 1;
3950 # ifdef MULTICHAR_SET
3951 if( rs_mfont[ i ] )
3952 length += strlen( rs_mfont[ i ] ) + 1;
3953 # endif
3954 }
3955 if( ( string = malloc( length ) ) != NULL ){
3956 char **missing_charsetlist, *def_string;
3957 int missing_charsetcount;
3958
3959 string[ 0 ] = '\0';
3960 for( i = 0 ; i < NFONTS ; i ++){
3961 if( rs_font[ i ] ){
3962 strcat( string, rs_font[ i ] );
3963 strcat( string, "," );
3964 }
3965 # ifdef MULTICHAR_SET
3966 if( rs_mfont[ i ] ){
3967 strcat( string, rs_mfont[ i ] );
3968 strcat( string, "," );
3969 }
3970 # endif
3971 }
3972 length = strlen( string );
3973 if( length > 0 && string[ length - 1 ] == ',' ){
3974 string[ length - 1 ] = '\0';
3975 length --;
3976 }
3977 if( length > 0 ){
3978 TermWin.fontset = XCreateFontSet
3979 ( Xdisplay, string,
3980 &missing_charsetlist, &missing_charsetcount, &def_string );
3981 }
3982 free( string );
3983 } else {
3984 TermWin.fontset = NULL;
3985 }
3986 return;
3987 }
3988
3989 /* PROTO */
3990 void
3991 setPreeditArea(XRectangle *preedit_rect, XRectangle *status_rect, XRectangle *needed_rect)
3992 {
3993 preedit_rect->x = needed_rect->width
3994 + (scrollbar_visible() && !(Options & Opt_scrollBar_right)
3995 ? (SB_WIDTH + sb_shadow * 2) : 0);
3996 preedit_rect->y = Height2Pixel(TermWin.nrow - 1)
3997 + ((menuBar.state == 1) ? menuBar_TotalHeight() : 0);
3998
3999 preedit_rect->width = Width2Pixel(TermWin.ncol + 1) - needed_rect->width
4000 + (!(Options & Opt_scrollBar_right)
4001 ? (SB_WIDTH + sb_shadow * 2) : 0);
4002 preedit_rect->height = Height2Pixel(1);
4003
4004 status_rect->x = (scrollbar_visible() && !(Options & Opt_scrollBar_right))
4005 ? (SB_WIDTH + sb_shadow * 2) : 0;
4006 status_rect->y = Height2Pixel(TermWin.nrow - 1)
4007 + ((menuBar.state == 1) ? menuBar_TotalHeight() : 0);
4008
4009 status_rect->width = needed_rect->width ? needed_rect->width
4010 : Width2Pixel(TermWin.ncol + 1);
4011 status_rect->height = Height2Pixel(1);
4012 }
4013
4014 /* PROTO */
4015 void
4016 IMDestroyCallback(XIM xim, XPointer client_data, XPointer call_data)
4017 {
4018 Input_Context = NULL;
4019 XRegisterIMInstantiateCallback(Xdisplay, NULL, NULL, NULL, IMInstantiateCallback, NULL);
4020 }
4021
4022
4023 /* PROTO */
4024 void
4025 IMInstantiateCallback(Display *display, XPointer client_data, XPointer call_data)
4026 {
4027 char *p, *s, buf[64], tmp[1024];
4028 char *end, *next_s;
4029 XIM xim = NULL;
4030 XIMStyle input_style = 0;
4031 XIMStyles *xim_styles = NULL;
4032 int found;
4033 XPoint spot;
4034 XRectangle rect, status_rect, needed_rect;
4035 unsigned long fg, bg;
4036 XVaNestedList preedit_attr = NULL;
4037 XVaNestedList status_attr = NULL;
4038 XIMCallback ximcallback;
4039
4040 Input_Context = NULL;
4041
4042 if (Input_Context)
4043 return;
4044
4045 ximcallback.callback = IMDestroyCallback;
4046 ximcallback.client_data = NULL;
4047
4048 if (rs_inputMethod && *rs_inputMethod) {
4049 STRNCPY(tmp, rs_inputMethod, sizeof(tmp) - 1);
4050 for (s = tmp; *s; s = next_s + 1) {
4051 for (; *s && isspace(*s); s++);
4052 if (!*s)
4053 break;
4054 for (end = s; (*end && (*end != ',')); end++);
4055 for (next_s = end--; ((end >= s) && isspace(*end)); end--);
4056 *(end + 1) = '\0';
4057
4058 if (*s) {
4059 STRCPY(buf, "@im=");
4060 strncat(buf, s, sizeof(buf) - 4 - 1);
4061 if ((p = XSetLocaleModifiers(buf)) != NULL
4062 && (xim = XOpenIM(Xdisplay, NULL, NULL, NULL)) != NULL)
4063 break;
4064 }
4065 if (!*next_s)
4066 break;
4067 }
4068 }
4069
4070 /* try with XMODIFIERS env. var. */
4071 if (xim == NULL && (p = XSetLocaleModifiers("")) != NULL)
4072 xim = XOpenIM(Xdisplay, NULL, NULL, NULL);
4073
4074 /* try with no modifiers base */
4075 if (xim == NULL && (p = XSetLocaleModifiers("@im=none")) != NULL)
4076 xim = XOpenIM(Xdisplay, NULL, NULL, NULL);
4077
4078 if (xim == NULL)
4079 return;
4080 XSetIMValues(xim, XNDestroyCallback, &ximcallback, NULL);
4081
4082 if (XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL)
4083 || !xim_styles) {
4084 print_error("input method doesn't support any style");
4085 XCloseIM(xim);
4086 return;
4087 }
4088 STRNCPY(tmp, (rs_preeditType ? rs_preeditType
4089 : "OverTheSpot,OffTheSpot,Root"),
4090 sizeof(tmp) - 1);
4091 for (found = 0, s = tmp; *s && !found; s = next_s + 1) {
4092 unsigned short i;
4093
4094 for (; *s && isspace(*s); s++);
4095 if (!*s)
4096 break;
4097 for (end = s; (*end && (*end != ',')); end++);
4098 for (next_s = end--; ((end >= s) && isspace(*end)); end--);
4099 *(end + 1) = '\0';
4100
4101 if (!strcmp(s, "OverTheSpot"))
4102 input_style = (XIMPreeditPosition | XIMStatusNothing);
4103 else if (!strcmp(s, "OffTheSpot"))
4104 input_style = (XIMPreeditArea | XIMStatusArea);
4105 else if (!strcmp(s, "Root"))
4106 input_style = (XIMPreeditNothing | XIMStatusNothing);
4107
4108 for (i = 0; i < xim_styles->count_styles; i++)
4109 if (input_style == xim_styles->supported_styles[i]) {
4110 found = 1;
4111 break;
4112 }
4113 }
4114 XFree(xim_styles);
4115
4116 if (found == 0) {
4117 print_error("input method doesn't support my preedit type");
4118 XCloseIM(xim);
4119 return;
4120 }
4121 if ((input_style != (XIMPreeditNothing | XIMStatusNothing))
4122 && (input_style != (XIMPreeditArea | XIMStatusArea))
4123 && (input_style != (XIMPreeditPosition | XIMStatusNothing))) {
4124 print_error("This program does not support the preedit type");
4125 XCloseIM(xim);
4126 return;
4127 }
4128 if (input_style & XIMPreeditPosition) {
4129 setSize(&rect);
4130 setPosition(&spot);
4131 setColor(&fg, &bg);
4132
4133 preedit_attr = XVaCreateNestedList(0, XNArea, &rect,
4134 XNSpotLocation, &spot,
4135 XNForeground, fg,
4136 XNBackground, bg,
4137 XNFontSet, TermWin.fontset,
4138 NULL);
4139 } else if (input_style & XIMPreeditArea) {
4140 setColor(&fg, &bg);
4141
4142 /*
4143 * The necessary width of preedit area is unknown
4144 * until create input context.
4145 */
4146 needed_rect.width = 0;
4147
4148 setPreeditArea(&rect, &status_rect, &needed_rect);
4149
4150 preedit_attr = XVaCreateNestedList(0, XNArea, &rect,
4151 XNForeground, fg,
4152 XNBackground, bg,
4153 XNFontSet, TermWin.fontset,
4154 NULL);
4155 status_attr = XVaCreateNestedList(0, XNArea, &status_rect,
4156 XNForeground, fg,
4157 XNBackground, bg,
4158 XNFontSet, TermWin.fontset,
4159 NULL);
4160 }
4161 Input_Context = XCreateIC(xim, XNInputStyle, input_style,
4162 XNClientWindow, TermWin.parent,
4163 XNFocusWindow, TermWin.parent,
4164 XNDestroyCallback, &ximcallback,
4165 preedit_attr ? XNPreeditAttributes : NULL,
4166 preedit_attr,
4167 status_attr ? XNStatusAttributes : NULL,
4168 status_attr,
4169 NULL);
4170 XFree(preedit_attr);
4171 XFree(status_attr);
4172 if (Input_Context == NULL) {
4173 print_error("Failed to create input context");
4174 XCloseIM(xim);
4175 }
4176 if (input_style & XIMPreeditArea)
4177 IMSetStatusPosition();
4178 }
4179
4180 /* PROTO */
4181 void
4182 IMSetStatusPosition(void)
4183 {
4184 XIMStyle input_style;
4185 XRectangle rect, status_rect, *needed_rect;
4186 XVaNestedList preedit_attr, status_attr;
4187
4188 if (Input_Context == NULL)
4189 return;
4190
4191 XGetICValues(Input_Context, XNInputStyle, &input_style, NULL);
4192 if (input_style & XIMPreeditArea) {
4193 status_attr = XVaCreateNestedList(0, XNAreaNeeded, &needed_rect, NULL);
4194 XGetICValues(Input_Context, XNStatusAttributes, status_attr, NULL);
4195 XFree(status_attr);
4196 rect.x = needed_rect->width;
4197 if (menuBar.state == 1) {
4198 rect.y = Height2Pixel(TermWin.nrow - 1) - menuBar_TotalHeight();
4199 } else {
4200 rect.y = Height2Pixel(TermWin.nrow - 1);
4201 }
4202 if (Options & Opt_scrollBar_right) {
4203 rect.width = Width2Pixel(TermWin.ncol + 1) - needed_rect->width;
4204 } else {
4205 rect.width = Width2Pixel(TermWin.ncol + 1) + SB_WIDTH + SHADOW * 2 - needed_rect->width;
4206 }
4207 rect.height = needed_rect->height;
4208 preedit_attr = XVaCreateNestedList(0, XNArea, &rect, NULL);
4209
4210 if (scrollbar_visible()) {
4211 if (Options & Opt_scrollBar_right) {
4212 status_rect.x = 0;
4213 } else {
4214 status_rect.x = SB_WIDTH + SHADOW * 2;
4215 }
4216 } else {
4217 status_rect.x = 0;
4218 }
4219 if (menuBar.state == 1) {
4220 status_rect.y = Height2Pixel(TermWin.nrow - 1) + menuBar_TotalHeight();
4221 } else {
4222 status_rect.y = Height2Pixel(TermWin.nrow - 1);
4223 }
4224 status_rect.width = needed_rect->width;
4225 status_rect.height = needed_rect->height;
4226 status_attr = XVaCreateNestedList(0, XNArea, &status_rect, NULL);
4227 XSetICValues(Input_Context,
4228 XNPreeditAttributes, preedit_attr,
4229 XNStatusAttributes, status_attr, NULL);
4230 XFree(preedit_attr);
4231 XFree(status_attr);
4232 }
4233 }
4234
4235 /* PROTO */
4236 void
4237 XProcessEvent( Display *display )
4238 {
4239 XEvent xev;
4240 XNextEvent( display, &xev );
4241 if( !XFilterEvent( &xev, xev.xany.window ) )
4242 process_x_event( &xev );
4243 return;
4244 }
4245
4246 #endif
4247
4248 /*}}} */
4249 /*----------------------- end-of-file (C source) -----------------------*/
4250