/* grX11thread.c -
 *
 *     ********************************************************************* 
 *     * Copyright (C) 1985, 1990 Regents of the University of California. * 
 *     * Permission to use, copy, modify, and distribute this              * 
 *     * software and its documentation for any purpose and without        * 
 *     * fee is hereby granted, provided that the above copyright          * 
 *     * notice appear in all copies.  The University of California        * 
 *     * makes no representations about the suitability of this            * 
 *     * software for any purpose.  It is provided "as is" without         * 
 *     * express or implied warranty.  Export of this software outside     * 
 *     * of the United States of America may require an export license.    * 
 *     *********************************************************************
 *
 * This file contains functions to run the X11 event loop as a separate
 * thread.  Used by X11 and OpenGL running under X11.  Compiled only if
 * option "THREADS" is set in "make config".
 */

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <signal.h>

#include <X11/keysym.h>
#include <pthread.h>

#include "misc/magic.h"
#include "utils/geometry.h"
#include "graphics/graphics.h"
#include "windows/windows.h"
#include "graphics/graphicsInt.h"
#include "grX11Int.h"

Display *grXdpy;

int writePipe;		/* As seen from child */
int readPipe;

pthread_t xloop_thread;

/*------------------------------------------------------*/
/* ParseEvent is copied from X11Helper.c		*/
/*------------------------------------------------------*/

/*
 * 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.
 */

ParseEvent (event, parentID)
    XEvent *event;
    int parentID;
{
    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 | Mod1Mask)
				& KeyPressedEvent->state;

	if (nbytes == 0)  	/* No ASCII equivalent to string */
	{
	    keywstate = (keymod << 16) | (keysym & 0xffff);

	    write(writePipe, event, sizeof(XEvent));
	    write(writePipe, &keywstate, sizeof(int));
	}
	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));
		}
	    }
	}
    }
    else if (event->type == DestroyNotify)
    {
	/* do nothing? */
    }
    else  /* All event types other than KeyPress */
    {
	write(writePipe, event, sizeof(XEvent));
    }
}

/*---------------------------------------------------------
 * xloop_begin:  Threaded version of XHelper7 (X11Helper.c)
 *
 *---------------------------------------------------------
 */

int
xloop_begin(window)
    Window window;
{
    XEvent xevent;
    int parentID;

    parentID = (int)getppid();

    /* Set this thread to cancel immediately when requested */
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    XLockDisplay(grXdpy);
    XSelectInput(grXdpy, window,
	    KeyPressMask|ButtonPressMask|ButtonReleaseMask|ExposureMask|
	    StructureNotifyMask|OwnerGrabButtonMask);
    XSync(grXdpy,1);
    XUnlockDisplay(grXdpy);

    while (1) {
	XNextEvent(grXdpy, &xevent);
/*	XLockDisplay(grXdpy); */
	ParseEvent(&xevent, parentID);
/*	XUnlockDisplay(grXdpy); */
    }
}  

/*----------------------------------------------------------------------*/
/* xloop_create function creates a thread for the X event loop.         */
/* Returns 0 on success of pthread_create, non-zero on failure.         */
/*----------------------------------------------------------------------*/

int
xloop_create(window)
    Window window;
{
    return pthread_create(&xloop_thread, NULL, (void *)xloop_begin,
		(void *)window);
}

/*----------------------------------------------------------------------*/
/* Kill the X event loop thread.					*/
/*----------------------------------------------------------------------*/

void
xloop_end()
{
    pthread_cancel(xloop_thread);
    pthread_join(xloop_thread, NULL);
}

/*----------------------------------------------------------------------*/
