mirror of
https://github.com/RTimothyEdwards/magic.git
synced 2026-08-28 17:15:13 +02:00
Initial commit at Tue Apr 25 08:41:48 EDT 2017 by tim on stravinsky
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
/* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <sys/time.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
Reference in New Issue
Block a user