/* X11Helper.c -- This "helper" process, forked off by the magic process, * looks like a kludge (and is), but is the only way to ensure that X11 * is able to run its event-driven protocol (through calls to XNextEvent) * while magic runs its interrupt-driven protocol (through calls to * select()). Both are infinite (blocking) loops and must execute in * parallel. Xlib is not thread-safe, so running this as a thread * instead of a forked process is a bad idea (confuses X11 and locks up). * * In magic version 7.1, the executable name for X11Helper has been * changed to XHelper7 to avoid compatibility conflicts with earlier * versions of magic. */ #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif #include #include #include /* * Portability stuff */ #if (defined(MIPSEB) && defined(SYSTYPE_BSD43)) || ibm032 # define SIG_RETURNS_INT #endif /* Some machines have signal handlers returning an int, while other machines * have it returning a void. If you have a machine that requires ints put * it in the list of machines in utils/magic.h. */ #ifdef SIG_RETURNS_INT #define sigRetVal int #else #define sigRetVal void #endif void sigSetAction(int, sigRetVal (*)(int)); sigRetVal TimeOut(int); void SetTimeOut(); void ParseEvent(XEvent *); /* * Declaration of global variables and procedure prototypes */ int readPipe,writePipe; /* pipe file descriptor to magic process */ int parentID; /* process id of parent */ Display *grXdpy; /* X11 display */ sigRetVal MapWindow(); /* * Main program: * This infinite loop is a rewrite of XtMainLoop() which includes both * the ability to handle interrupts and communicate with magic's main * process via the I/O pipe. The program exits when killed by the * GrClose() routine in magic. * * This process will look every TIMEOUT minutes to see if its parent process * (magic) still exists. This keeps the helper processes from becoming * an orphan process if magic crashes or otherwise fails to execute * GrClose() before exiting. */ #define TIMEOUT 10 /* timeout period (minutes) */ int main (argc, argv) int argc; char **argv; { XEvent xevent; if (argc <= 1) { fprintf(stderr, "%s: expecting two file-descriptor numbers.\n\ \t(This program should be run only by magic itself.)\n", argv[0]); exit(1); } sscanf(argv[1], "%d %d", &readPipe,&writePipe); grXdpy = XOpenDisplay(0); if (grXdpy == (Display *) 0) { fprintf(stderr,"%s: XOpenDisplay failed.\n", argv[0]); exit(1); } parentID = getppid(); /* sigSetAction(SIGINT, SIG_IGN); */ /* sigSetAction(SIGQUIT, SIG_IGN); */ sigSetAction(SIGTERM, MapWindow); #ifdef SIGTSTP sigSetAction(SIGTSTP, SIG_IGN); #endif #ifdef SIGCONT sigSetAction(SIGCONT, SIG_IGN); #endif SetTimeOut(); /* set timer for TIMEOUT minutes */ while (1) { XNextEvent(grXdpy, &xevent); ParseEvent(&xevent); } } /* * ParseEvent(XEvent *event): * Pass XEvents on to the magic main process by writing into the * connecting pipe. Keystrokes must be handled such that magic * can treat untranslated keyboard input from stdin the same way * that it treats translated keyboard input through X11. * Use XLookupString() to get an ASCII character out of the * keycode, but also pass back the event structure so we can * pull out key modifier information in grX11Stdin(). * * Pass the event and key value back through the I/O pipe so that * the keypress triggers the select() mechanism in magic's textio * routine. */ void ParseEvent (event) XEvent *event; { if (event->type == KeyPress) { XKeyPressedEvent *KeyPressedEvent = (XKeyPressedEvent *) event; char inChar[10], c, *p; KeySym keysym; int keywstate; /* KeySym with prepended state information */ int keymod; int nbytes; nbytes = XLookupString(KeyPressedEvent, inChar, sizeof(inChar), &keysym, NULL); if (IsModifierKey(keysym)) return; /* Don't send modifiers as events */ keymod = (LockMask | ControlMask | ShiftMask) & KeyPressedEvent->state; #ifdef __APPLE__ if (KeyPressedEvent->state & (Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask)) keymod |= Mod1Mask; #else keymod |= (Mod1Mask & KeyPressedEvent->state); #endif if (nbytes == 0) /* No ASCII equivalent to string */ { keywstate = (keymod << 16) | (keysym & 0xffff); write(writePipe, event, sizeof(XEvent)); write(writePipe, &keywstate, sizeof(int)); #ifndef USE_IO_PROBE kill(parentID, SIGIO); #endif } else if (!strncmp(XKeysymToString(keysym), "KP_", 3)) { /* keypad key (special case---would like to */ /* differentiate between shift-KP-# and # itself) */ keymod &= ~ShiftMask; keywstate = (keymod << 16) | (keysym & 0xffff); write(writePipe, event, sizeof(XEvent)); write(writePipe, &keywstate, sizeof(int)); #ifndef USE_IO_PROBE kill(parentID, SIGIO); #endif } else /* For keys with ASCII equivalent */ { /* If Control or Shift is used alone, it should be removed from */ /* the modifier list, since the ASCII value subsumes the Control */ /* and Shift functions. However, Control + Shift + key should */ /* be retained, as it cannot be expressed in ASCII. */ if (!(keymod & (LockMask | Mod1Mask))) { if (!(keymod & ControlMask)) keymod &= ~ShiftMask; else if (!(keymod & ShiftMask)) keymod &= ~ControlMask; } p = inChar; while (nbytes--) { if ((c = *p++) == 3) /* Ctrl-C interrupt */ { kill(parentID, SIGINT); } else { /* When Control modifier is present, use the capital */ /* letter value instead of the control value. */ if ((keymod & ControlMask) && (c < 32)) c += 'A' - 1; keywstate = (keymod << 16) | ((int)c & 0xff); write(writePipe, event, sizeof(XEvent)); write(writePipe, &keywstate, sizeof(int)); #ifndef USE_IO_PROBE kill(parentID, SIGIO); #endif } } } } else if (event->type == DestroyNotify) { /* If this is the last window to go, it will be too late to catch */ /* (i.e., read on pipe will never happen). */ /* Reset timer for 1 second and check presence of parent */ struct itimerval checktimer; getitimer(ITIMER_REAL, &checktimer); if (checktimer.it_value.tv_sec == 0 && checktimer.it_value.tv_usec == 0) TimeOut(0); else /* Set timer to 1 second */ { checktimer.it_value.tv_sec = 1; checktimer.it_value.tv_usec = 0; } setitimer(ITIMER_REAL, &checktimer, NULL); } else /* All event types other than KeyPress */ { write(writePipe, event, sizeof(XEvent)); #ifndef USE_IO_PROBE kill(parentID, SIGIO); #endif } } /* * SetTimeOut(): * Initiate an interval timer for an interval of TIMEOUT minutes. */ void SetTimeOut() { struct itimerval checktimer; checktimer.it_value.tv_sec = 60 * TIMEOUT; checktimer.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &checktimer, NULL); sigSetAction(SIGALRM, TimeOut); /* Timeout signal handler */ } /* * TimeOut(): * Check parent process to see if it still exists. Commit suicide if * orphaned (it's a harsh, harsh world). Reset the timer for another * TIMEOUT minutes, otherwise. */ sigRetVal TimeOut(int signo) { int tmpid; if ((tmpid = getppid()) != parentID) { fprintf(stderr, X11HELP_PROG ": parent (ID %d) not found. Exiting.\n", parentID); exit(1); } SetTimeOut(); /* Renew the timer and signal handler */ } /* * MapWindow(): * On startup of any new magic window, magic writes the X11 window ID * into the communication pipe and manually generates a SIGTERM signal. * The interrupt handler calls MapWindow() on receiving SIGTERM, and * the window ID is read from the I/O pipe. MapWindow() then activates * the window by declaring what events it will interpret for that * window (keystrokes, mouse button, and expose and resize events) */ sigRetVal MapWindow(int signo) { Window window; if (read(readPipe, (char *)&window, sizeof(Window)) == sizeof(Window)) { XSelectInput(grXdpy, window, KeyPressMask|ButtonPressMask|ButtonReleaseMask| ExposureMask|StructureNotifyMask| VisibilityChangeMask|OwnerGrabButtonMask); XSync(grXdpy,1); } else fprintf(stderr, X11HELP_PROG ": read on pipe failed\n"); } /* * This code duplicated from signals/signals.c so that this program is * independent of the rest of the magic code. */ void sigSetAction(int signo, sigRetVal (*handler)(int)) { #if defined(SYSV) || defined(CYGWIN) struct sigaction sa; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(signo, &sa, (struct sigaction *)NULL); #else struct sigvec sv; sv.sv_handler = handler; sv.sv_mask = 0; sv.sv_flags = 0; sigvec(signo, &sv, (struct sigvec *)NULL); #endif }