Moved X input event handling out of EVENT_WaitNetEvent and into

service thread callback; moved EVENT_WaitNetEvent out of the EVENT_
driver.  Changed winContext to hold HWND instead of WND *.
oldstable
Ulrich Weigand 1999-05-22 18:57:17 +00:00 committed by Alexandre Julliard
parent c758201397
commit 7581f85617
6 changed files with 151 additions and 329 deletions

View File

@ -31,16 +31,11 @@ extern BOOL TIMER_GetTimerMsg( MSG *msg, HWND hwnd,
/* event.c */
typedef struct tagEVENT_DRIVER {
BOOL (*pInit)(void);
void (*pAddIO)(int, unsigned);
void (*pDeleteIO)(int, unsigned);
BOOL (*pWaitNetEvent)(BOOL, BOOL);
void (*pSynchronize)(void);
void (*pSynchronize)(BOOL);
BOOL (*pCheckFocus)(void);
BOOL (*pQueryPointer)(DWORD *, DWORD *, DWORD *);
void (*pDummyMotionNotify)(void);
BOOL (*pPending)(void);
BOOL16 (*pIsUserIdle)(void);
void (*pWakeUp)(void);
void (*pUserRepaintDisable)(BOOL);
} EVENT_DRIVER;
extern EVENT_DRIVER *EVENT_Driver;
@ -48,12 +43,11 @@ extern EVENT_DRIVER *EVENT_Driver;
extern void EVENT_AddIO( int fd, unsigned flag );
extern void EVENT_DeleteIO( int fd, unsigned flag );
extern BOOL EVENT_Init( void );
extern BOOL EVENT_WaitNetEvent( BOOL sleep, BOOL peek );
extern void EVENT_Synchronize(void);
extern void EVENT_WaitNetEvent( void );
extern void EVENT_Synchronize( BOOL bProcessEvents );
extern BOOL EVENT_CheckFocus( void );
extern BOOL EVENT_QueryPointer(DWORD *posX, DWORD *posY, DWORD *state);
extern void EVENT_DummyMotionNotify(void);
extern BOOL EVENT_Pending(void);
extern void EVENT_WakeUp(void);
/* input.c */

View File

@ -342,16 +342,11 @@ extern int X11DRV_DESKTOP_GetScreenDepth(struct tagDESKTOP *pDesktop);
extern struct tagEVENT_DRIVER X11DRV_EVENT_Driver;
extern BOOL X11DRV_EVENT_Init(void);
extern void X11DRV_EVENT_AddIO(int fd, unsigned flag);
extern void X11DRV_EVENT_DeleteIO(int fd, unsigned flag);
extern BOOL X11DRV_EVENT_WaitNetEvent(BOOL sleep, BOOL peek);
extern void X11DRV_EVENT_Synchronize(void);
extern void X11DRV_EVENT_Synchronize( BOOL bProcessEvents );
extern BOOL X11DRV_EVENT_CheckFocus( void );
extern BOOL X11DRV_EVENT_QueryPointer(DWORD *posX, DWORD *posY, DWORD *state);
extern void X11DRV_EVENT_DummyMotionNotify(void);
extern BOOL X11DRV_EVENT_Pending(void);
extern BOOL16 X11DRV_EVENT_IsUserIdle(void);
extern void X11DRV_EVENT_WakeUp(void);
extern void X11DRV_EVENT_UserRepaintDisable( BOOL bDisable );
/* X11 keyboard driver */

View File

@ -5,12 +5,22 @@
*
*/
#include <unistd.h>
#include "message.h"
#include "winsock.h"
#include "debugtools.h"
DECLARE_DEBUG_CHANNEL(event)
/**********************************************************************/
EVENT_DRIVER *EVENT_Driver = NULL;
/* EVENT_WaitNetEvent() master fd sets */
static fd_set __event_io_set[3];
static int __event_max_fd = 0;
static int __wakeup_pipe[2];
/***********************************************************************
* EVENT_Init
*
@ -18,7 +28,24 @@ EVENT_DRIVER *EVENT_Driver = NULL;
*/
BOOL EVENT_Init(void)
{
return EVENT_Driver->pInit();
int i;
for( i = 0; i < 3; i++ )
FD_ZERO( __event_io_set + i );
/* this pipe is used to be able to wake-up the scheduler(WaitNetEvent) by
a 32 bit thread, this will become obsolete when the input thread will be
implemented */
pipe(__wakeup_pipe);
/* make the pipe non-blocking */
fcntl(__wakeup_pipe[0], F_SETFL, O_NONBLOCK);
fcntl(__wakeup_pipe[1], F_SETFL, O_NONBLOCK);
FD_SET( __wakeup_pipe[0], &__event_io_set[EVENT_IO_READ] );
__event_max_fd = __wakeup_pipe[0];
__event_max_fd++;
return EVENT_Driver->pInit();
}
/***********************************************************************
@ -26,7 +53,8 @@ BOOL EVENT_Init(void)
*/
void EVENT_AddIO(int fd, unsigned io_type)
{
EVENT_Driver->pAddIO(fd, io_type);
FD_SET( fd, &__event_io_set[io_type] );
if( __event_max_fd <= fd ) __event_max_fd = fd + 1;
}
/***********************************************************************
@ -34,29 +62,80 @@ void EVENT_AddIO(int fd, unsigned io_type)
*/
void EVENT_DeleteIO(int fd, unsigned io_type)
{
EVENT_Driver->pDeleteIO(fd, io_type);
FD_CLR( fd, &__event_io_set[io_type] );
}
/***********************************************************************
* EVENT_WakeUp
*
* Wake up the scheduler (EVENT_WaitNetEvent). Use by 32 bit thread
* when thew want signaled an event to a 16 bit task. This function
* will become obsolete when an Asynchronous thread will be implemented
*/
void EVENT_WakeUp(void)
{
if (write (__wakeup_pipe[1], "A", 1) != 1)
ERR_(event)("unable to write in wakeup_pipe\n");
}
/***********************************************************************
* EVENT_ReadWakeUpPipe
*
* Empty the wake up pipe
*/
void EVENT_ReadWakeUpPipe(void)
{
char tmpBuf[10];
ssize_t ret;
/* Flush the wake-up pipe, it's just dummy data for waking-up this
thread. This will be obsolete when the input thread will be done */
while ( (ret = read(__wakeup_pipe[0], &tmpBuf, 10)) == 10 );
}
/***********************************************************************
* EVENT_WaitNetEvent
*
* Wait for a network event, optionally sleeping until one arrives.
* Return TRUE if an event is pending, FALSE on timeout or error
* (for instance lost connection with the server).
* Sleep until a network event arrives, or until woken.
*/
BOOL EVENT_WaitNetEvent(BOOL sleep, BOOL peek)
void EVENT_WaitNetEvent( void )
{
return EVENT_Driver->pWaitNetEvent(sleep, peek);
int num_pending;
fd_set io_set[3];
memcpy( io_set, __event_io_set, sizeof(io_set) );
num_pending = select( __event_max_fd, &io_set[EVENT_IO_READ],
&io_set[EVENT_IO_WRITE],
&io_set[EVENT_IO_EXCEPT], NULL );
if ( num_pending == -1 )
{
/* Error - signal, invalid arguments, out of memory */
return;
}
/* Flush the wake-up pipe, it's just dummy data for waking-up this
thread. This will be obsolete when the input thread will be done */
if ( FD_ISSET( __wakeup_pipe[0], &io_set[EVENT_IO_READ] ) )
{
num_pending--;
EVENT_ReadWakeUpPipe();
}
/* Winsock asynchronous services */
WINSOCK_HandleIO( &__event_max_fd, num_pending, io_set, __event_io_set );
}
/***********************************************************************
* EVENT_Synchronize
*
* Synchronize with the X server. Should not be used too often.
*/
void EVENT_Synchronize(void)
void EVENT_Synchronize( BOOL bProcessEvents )
{
EVENT_Driver->pSynchronize();
EVENT_Driver->pSynchronize( bProcessEvents );
}
/**********************************************************************
@ -75,7 +154,6 @@ BOOL EVENT_QueryPointer(DWORD *posX, DWORD *posY, DWORD *state)
return EVENT_Driver->pQueryPointer(posX, posY, state);
}
/***********************************************************************
* EVENT_DummyMotionNotify
*
@ -86,32 +164,3 @@ void EVENT_DummyMotionNotify(void)
EVENT_Driver->pDummyMotionNotify();
}
/**********************************************************************
* EVENT_Pending
*/
BOOL EVENT_Pending()
{
return EVENT_Driver->pPending();
}
/***********************************************************************
* IsUserIdle (USER.333)
*
* Check if we have pending X events.
*/
BOOL16 WINAPI IsUserIdle16(void)
{
return EVENT_Driver->pIsUserIdle();
}
/***********************************************************************
* EVENT_WakeUp
*
* Wake up the scheduler (EVENT_WaitNetEvent). Use by 32 bit thread
* when thew want signaled an event to a 16 bit task. This function
* will become obsolete when an Asynchronous thread will be implemented
*/
void EVENT_WakeUp(void)
{
EVENT_Driver->pWakeUp();
}

View File

@ -16,7 +16,6 @@
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include "callback.h"
#include "class.h"
#include "clipboard.h"
@ -32,7 +31,8 @@
#include "queue.h"
#include "shell.h"
#include "winpos.h"
#include "winsock.h"
#include "services.h"
#include "file.h"
#include "windef.h"
#include "x11drv.h"
@ -66,12 +66,6 @@ extern void X11DRV_KEYBOARD_HandleEvent(WND *pWnd, XKeyEvent *event);
#define DndURL 128 /* KDE drag&drop */
/* EVENT_WaitNetEvent() master fd sets */
static fd_set __event_io_set[3];
static int __event_max_fd = 0;
static int __event_x_connection = 0;
static int __wakeup_pipe[2];
static const char * const event_names[] =
{
@ -85,6 +79,7 @@ static const char * const event_names[] =
"ClientMessage", "MappingNotify"
};
static void CALLBACK EVENT_ProcessAllEvents( ULONG_PTR arg );
static void EVENT_ProcessEvent( XEvent *event );
/* Event handlers */
@ -111,216 +106,40 @@ static void EVENT_GetGeometry( Window win, int *px, int *py,
unsigned int *pwidth, unsigned int *pheight );
static BOOL bUserRepaintDisabled = TRUE;
/***********************************************************************
* EVENT_Init
*
* Initialize network IO.
*/
BOOL X11DRV_EVENT_Init(void)
{
int i;
for( i = 0; i < 3; i++ )
FD_ZERO( __event_io_set + i );
__event_max_fd = __event_x_connection = ConnectionNumber(display);
FD_SET( __event_x_connection, &__event_io_set[EVENT_IO_READ] );
/* this pipe is used to be able to wake-up the scheduler(WaitNetEvent) by
a 32 bit thread, this will become obsolete when the input thread will be
implemented */
pipe(__wakeup_pipe);
/* make the pipe non-blocking */
fcntl(__wakeup_pipe[0], F_SETFL, O_NONBLOCK);
fcntl(__wakeup_pipe[1], F_SETFL, O_NONBLOCK);
FD_SET( __wakeup_pipe[0], &__event_io_set[EVENT_IO_READ] );
if (__wakeup_pipe[0] > __event_max_fd)
__event_max_fd = __wakeup_pipe[0];
__event_max_fd++;
return TRUE;
}
/***********************************************************************
* X11DRV_EVENT_AddIO
*/
void X11DRV_EVENT_AddIO( int fd, unsigned io_type )
{
FD_SET( fd, &__event_io_set[io_type] );
if( __event_max_fd <= fd ) __event_max_fd = fd + 1;
}
/***********************************************************************
* X11DRV_EVENT_DeleteIO
*/
void X11DRV_EVENT_DeleteIO( int fd, unsigned io_type )
{
FD_CLR( fd, &__event_io_set[io_type] );
}
/***********************************************************************
* X11DRV_EVENT_IsUserIdle
*/
BOOL16 X11DRV_EVENT_IsUserIdle(void)
{
struct timeval timeout = {0, 0};
fd_set check_set;
FD_ZERO(&check_set);
FD_SET(__event_x_connection, &check_set);
if( select(__event_x_connection + 1, &check_set, NULL, NULL, &timeout) > 0 )
/* Install the X event processing callback */
SERVICE_AddObject( FILE_DupUnixHandle( ConnectionNumber(display),
GENERIC_READ | SYNCHRONIZE ),
EVENT_ProcessAllEvents, 0 );
return TRUE;
return FALSE;
}
/***********************************************************************
* EVENT_ReadWakeUpPipe
*
* Empty the wake up pipe
*/
void EVENT_ReadWakeUpPipe(void)
{
char tmpBuf[10];
ssize_t ret;
EnterCriticalSection(&X11DRV_CritSection);
/* Flush the wake-up pipe, it's just dummy data for waking-up this
thread. This will be obsolete when the input thread will be done */
while ( (ret = read(__wakeup_pipe[0], &tmpBuf, 10)) == 10 );
LeaveCriticalSection(&X11DRV_CritSection);
}
/***********************************************************************
* X11DRV_EVENT_WaitNetEvent
*
* Wait for a network event, optionally sleeping until one arrives.
* Returns TRUE if an event is pending that cannot be processed in
* 'peek' mode, FALSE otherwise.
* EVENT_ProcessAllEvents
*/
BOOL X11DRV_EVENT_WaitNetEvent( BOOL sleep, BOOL peek )
static void CALLBACK EVENT_ProcessAllEvents( ULONG_PTR arg )
{
XEvent event;
int pending = TSXPending(display);
XEvent event;
if (!pending && sleep)
TRACE_(event)( "called.\n" );
EnterCriticalSection( &X11DRV_CritSection );
while ( XPending( display ) )
{
int num_pending;
fd_set io_set[3];
XNextEvent( display, &event );
memcpy( io_set, __event_io_set, sizeof(io_set) );
#ifdef CONFIG_IPC
sigsetjmp(env_wait_x, 1);
stop_wait_op= CONT;
if (DDE_GetRemoteMessage()) {
while(DDE_GetRemoteMessage())
;
return FALSE;
}
stop_wait_op = STOP_WAIT_X;
/* The code up to the next "stop_wait_op = CONT" must be reentrant */
num_pending = select( __event_max_fd, &io_set[EVENT_IO_READ],
&io_set[EVENT_IO_WRITE],
&io_set[EVENT_IO_EXCEPT], NULL );
if ( num_pending == -1 )
{
/* Error - signal, invalid arguments, out of memory */
stop_wait_op = CONT;
return FALSE;
}
stop_wait_op = CONT;
#else /* CONFIG_IPC */
num_pending = select( __event_max_fd, &io_set[EVENT_IO_READ],
&io_set[EVENT_IO_WRITE],
&io_set[EVENT_IO_EXCEPT], NULL );
if ( num_pending == -1 )
{
/* Error - signal, invalid arguments, out of memory */
return FALSE;
}
#endif /* CONFIG_IPC */
/* Flush the wake-up pipe, it's just dummy data for waking-up this
thread. This will be obsolete when the input thread will be done */
if ( FD_ISSET( __wakeup_pipe[0], &io_set[EVENT_IO_READ] ) )
{
num_pending--;
EVENT_ReadWakeUpPipe();
}
/* Winsock asynchronous services */
if( FD_ISSET( __event_x_connection, &io_set[EVENT_IO_READ]) )
{
num_pending--;
if( num_pending )
WINSOCK_HandleIO( &__event_max_fd, num_pending, io_set, __event_io_set );
}
else /* no X events */
{
WINSOCK_HandleIO( &__event_max_fd, num_pending, io_set, __event_io_set );
return FALSE;
}
LeaveCriticalSection( &X11DRV_CritSection );
EVENT_ProcessEvent( &event );
EnterCriticalSection( &X11DRV_CritSection );
}
/* Process current X event (and possibly others that occurred in the meantime) */
EnterCriticalSection(&X11DRV_CritSection);
while (XPending( display ))
{
#ifdef CONFIG_IPC
if (DDE_GetRemoteMessage())
{
LeaveCriticalSection(&X11DRV_CritSection);
while(DDE_GetRemoteMessage()) ;
return FALSE;
}
#endif /* CONFIG_IPC */
XNextEvent( display, &event );
if( peek )
{
/* Check only for those events which can be processed
* internally. */
if( event.type == MotionNotify ||
event.type == ButtonPress || event.type == ButtonRelease ||
event.type == KeyPress || event.type == KeyRelease ||
event.type == SelectionRequest || event.type == SelectionClear ||
event.type == ClientMessage )
{
LeaveCriticalSection(&X11DRV_CritSection);
EVENT_ProcessEvent( &event );
EnterCriticalSection(&X11DRV_CritSection);
continue;
}
if ( event.type == NoExpose )
continue;
XPutBackEvent(display, &event);
LeaveCriticalSection(&X11DRV_CritSection);
return TRUE;
}
else
{
LeaveCriticalSection(&X11DRV_CritSection);
EVENT_ProcessEvent( &event );
EnterCriticalSection(&X11DRV_CritSection);
}
}
LeaveCriticalSection(&X11DRV_CritSection);
return FALSE;
LeaveCriticalSection( &X11DRV_CritSection );
}
/***********************************************************************
@ -328,24 +147,20 @@ BOOL X11DRV_EVENT_WaitNetEvent( BOOL sleep, BOOL peek )
*
* Synchronize with the X server. Should not be used too often.
*/
void X11DRV_EVENT_Synchronize()
void X11DRV_EVENT_Synchronize( BOOL bProcessEvents )
{
XEvent event;
/* Use of the X critical section is needed or we have a small
* race between XPending() and XNextEvent().
*/
EnterCriticalSection( &X11DRV_CritSection );
XSync( display, False );
while (XPending( display ))
{
XNextEvent( display, &event );
/* unlock X critsection for EVENT_ProcessEvent() might switch tasks */
LeaveCriticalSection( &X11DRV_CritSection );
EVENT_ProcessEvent( &event );
EnterCriticalSection( &X11DRV_CritSection );
}
LeaveCriticalSection( &X11DRV_CritSection );
TSXSync( display, False );
if ( bProcessEvents )
EVENT_ProcessAllEvents( 0 );
}
/***********************************************************************
* EVENT_UserRepaintDisable
*/
void X11DRV_EVENT_UserRepaintDisable( BOOL bDisabled )
{
bUserRepaintDisabled = bDisabled;
}
/***********************************************************************
@ -355,9 +170,10 @@ void X11DRV_EVENT_Synchronize()
*/
static void EVENT_ProcessEvent( XEvent *event )
{
WND *pWnd;
HWND hWnd;
TRACE_(event)( "called.\n" );
switch (event->type)
{
case SelectionNotify: /* all of these should be caught by XCheckTypedWindowEvent() */
@ -377,7 +193,7 @@ static void EVENT_ProcessEvent( XEvent *event )
}
if ( TSXFindContext( display, event->xany.window, winContext,
(char **)&pWnd ) != 0) {
(char **)&hWnd ) != 0) {
if ( event->type == ClientMessage) {
/* query window (drag&drop event contains only drag window) */
Window root, child;
@ -385,30 +201,20 @@ static void EVENT_ProcessEvent( XEvent *event )
unsigned u;
TSXQueryPointer( display, X11DRV_GetXRootWindow(), &root, &child,
&root_x, &root_y, &child_x, &child_y, &u);
if (TSXFindContext( display, child, winContext, (char **)&pWnd ) != 0)
if (TSXFindContext( display, child, winContext, (char **)&hWnd ) != 0)
return;
} else {
pWnd = NULL; /* Not for a registered window */
hWnd = 0; /* Not for a registered window */
}
}
WIN_LockWndPtr(pWnd);
if(!pWnd)
hWnd = 0;
else
hWnd = pWnd->hwndSelf;
if ( !pWnd && event->xany.window != X11DRV_GetXRootWindow() )
if ( !hWnd && event->xany.window != X11DRV_GetXRootWindow() )
ERR_(event)("Got event %s for unknown Window %08lx\n",
event_names[event->type], event->xany.window );
else
TRACE_(event)("Got event %s for hwnd %04x\n",
event_names[event->type], hWnd );
WIN_ReleaseWndPtr(pWnd);
switch(event->type)
{
@ -440,40 +246,42 @@ static void EVENT_ProcessEvent( XEvent *event )
break;
case FocusIn:
if (!hWnd) return;
if (!hWnd || bUserRepaintDisabled) return;
EVENT_FocusIn( hWnd, (XFocusChangeEvent*)event );
break;
case FocusOut:
if (!hWnd) return;
if (!hWnd || bUserRepaintDisabled) return;
EVENT_FocusOut( hWnd, (XFocusChangeEvent*)event );
break;
case Expose:
if (bUserRepaintDisabled) return;
EVENT_Expose( hWnd, (XExposeEvent *)event );
break;
case GraphicsExpose:
if (bUserRepaintDisabled) return;
EVENT_GraphicsExpose( hWnd, (XGraphicsExposeEvent *)event );
break;
case ConfigureNotify:
if (!hWnd) return;
if (!hWnd || bUserRepaintDisabled) return;
EVENT_ConfigureNotify( hWnd, (XConfigureEvent*)event );
break;
case SelectionRequest:
if (!hWnd) return;
if (!hWnd || bUserRepaintDisabled) return;
EVENT_SelectionRequest( hWnd, (XSelectionRequestEvent *)event );
break;
case SelectionClear:
if (!hWnd) return;
if (!hWnd || bUserRepaintDisabled) return;
EVENT_SelectionClear( hWnd, (XSelectionClearEvent*) event );
break;
case ClientMessage:
if (!hWnd) return;
if (!hWnd || bUserRepaintDisabled) return;
EVENT_ClientMessage( hWnd, (XClientMessageEvent *) event );
break;
@ -487,12 +295,12 @@ static void EVENT_ProcessEvent( XEvent *event )
break;
case MapNotify:
if (!hWnd) return;
if (!hWnd || bUserRepaintDisabled) return;
EVENT_MapNotify( hWnd, (XMapEvent *)event );
break;
case UnmapNotify:
if (!hWnd) return;
if (!hWnd || bUserRepaintDisabled) return;
EVENT_UnmapNotify( hWnd, (XUnmapEvent *)event );
break;
@ -1525,24 +1333,4 @@ void EVENT_UnmapNotify( HWND hWnd, XUnmapEvent *event )
}
/**********************************************************************
* X11DRV_EVENT_Pending
*/
BOOL X11DRV_EVENT_Pending()
{
return TSXPending(display);
}
/**********************************************************************
* X11DRV_EVENT_WakeUp
*/
void X11DRV_EVENT_WakeUp(void)
{
/* wake-up EVENT_WaitNetEvent function, a 32 bit thread post an event
for a 16 bit task */
if (write (__wakeup_pipe[1], "A", 1) != 1)
ERR_(event)("unable to write in wakeup_pipe\n");
}
#endif /* !defined(X_DISPLAY_MISSING) */

View File

@ -43,16 +43,11 @@ DESKTOP_DRIVER X11DRV_DESKTOP_Driver =
EVENT_DRIVER X11DRV_EVENT_Driver =
{
X11DRV_EVENT_Init,
X11DRV_EVENT_AddIO,
X11DRV_EVENT_DeleteIO,
X11DRV_EVENT_WaitNetEvent,
X11DRV_EVENT_Synchronize,
X11DRV_EVENT_CheckFocus,
X11DRV_EVENT_QueryPointer,
X11DRV_EVENT_DummyMotionNotify,
X11DRV_EVENT_Pending,
X11DRV_EVENT_IsUserIdle,
X11DRV_EVENT_WakeUp
X11DRV_EVENT_UserRepaintDisable
};
KEYBOARD_DRIVER X11DRV_KEYBOARD_Driver =

View File

@ -106,7 +106,8 @@ static void X11DRV_WND_RegisterWindow(WND *wndPtr)
TSXSetWMProtocols( display, X11DRV_WND_GetXWindow(wndPtr), &wmDeleteWindow, 1 );
if (!winContext) winContext = TSXUniqueContext();
TSXSaveContext( display, X11DRV_WND_GetXWindow(wndPtr), winContext, (char *) wndPtr );
TSXSaveContext( display, X11DRV_WND_GetXWindow(wndPtr),
winContext, (char *) wndPtr->hwndSelf );
}
/**********************************************************************
@ -616,7 +617,7 @@ void X11DRV_WND_SetFocus(WND *wndPtr)
if (X11DRV_PALETTE_PaletteFlags & X11DRV_PALETTE_PRIVATE)
TSXInstallColormap( display, X11DRV_PALETTE_PaletteXColormap );
EVENT_Synchronize();
EVENT_Synchronize( TRUE );
}
/*****************************************************************
@ -665,7 +666,7 @@ void X11DRV_WND_SurfaceCopy(WND* wndPtr, DC *dcPtr, INT dx, INT dy,
TSXSetGraphicsExposures( display, physDev->gc, False );
if (bUpdate) /* Make sure exposure events have been processed */
EVENT_Synchronize();
EVENT_Synchronize( TRUE );
}
/***********************************************************************