File: cpw_event.c

/***************************************************************************/
/*                                                                         */
/*  cpw_event.c                                                            */
/*                                                                         */
/*    The Cpw library event stack interface and management.                */
/*                                                                         */
/*  Copyright 2001-2002 by                                                 */
/*  Jim Mathies,                                                           */
/*                                                                         */
/*  This file is part of the Cpw project, and may only be used,            */
/*  modified, and distributed under the terms of the Cpw project           */
/*  license.  By continuing to use, modify, or distribute this file        */
/*  you indicate that you have read the license and understand and         */
/*  accept it fully.                                                       */
/*                                                                         */
/*  File Platform: cross                                                   */
/*                                                                         */
/***************************************************************************/

#include "cpw_event.h"
#include "cpw_error.h"
#include "cpw_windowlist.h"
#include CPW_LOCALHOST_H

/* #define DEBUGEVENTRAW 1  prints stack s/r data to stdout */
/* #define DEBUGUSEREVENTS 1  prints every event sent to the application */

/*************************************************************************/
/*                                                                       */
/*   init and exit.                                                      */
/*                                                                       */
/*************************************************************************/

bool cpw_event_init( pCpw cpw )
{
    /* allocate the stack's memory */

    cpw->stack = cpwmalloc( sizeof( CpwEventStack ) );

    if ( cpw->stack == 0 ) { 
        cpw_error_set( cpw, cpw_error_outofmemory );
        return false; 
    }

    memset( cpw->stack, 0, sizeof( CpwEventStack ) );
    return true;
}

void cpw_event_exit( pCpw cpw )
{
    cpwFilterEvent( cpw, CPW_DISPLAY_EVENT, true );
    cpwFilterEvent( cpw, CPW_RESHAPE_EVENT, true );

    /* flush pending events include window close events */

    while ( cpw_event_sendevent( cpw ) ) {}

    /* free event stack memory */

    cpwfree( cpw->stack );
}

/*************************************************************************/
/*                                                                       */
/*   global event handler setter api.                                    */
/*                                                                       */
/*************************************************************************/

bool cpwCreateCallback( pCpw cpw, CpwCreateCallback createCallback )
{
    cpw->createCallback = (pVoid)createCallback;
    return true;
}

bool cpwDisplayCallback( pCpw cpw, CpwDisplayCallback displayCallback )
{
    cpw->displayCallback = (pVoid)displayCallback;
    return true;
}

bool cpwReshapeCallback( pCpw cpw, CpwReshapeCallback reshapeCallback )
{
    cpw->reshapeCallback = (pVoid)reshapeCallback;
    return true;
}

bool cpwPositionCallback( pCpw cpw, CpwPositionCallback positionCallback )
{
    cpw->positionCallback = (pVoid)positionCallback;
    return true;
}

bool cpwKeyboardCallback( pCpw cpw, CpwKeyboardCallback keyCallback )
{
    cpw->keyCallback = (pVoid)keyCallback;
    return true;
}

bool cpwSystemKeyboardCallback( pCpw cpw, CpwSystemKeyboardCallback skeyCallback )
{
    cpw->skeyCallback = (pVoid)skeyCallback;
    return true;
}

bool cpwVisibilityCallback( pCpw cpw, CpwVisibilityCallback visibilityCallback )
{
    cpw->visibilityCallback = (pVoid)visibilityCallback;
    return true;
}

bool cpwMouseClickCallback( pCpw cpw, CpwMouseClickCallback mouseclickCallback )
{
    cpw->mouseclickCallback = (pVoid)mouseclickCallback;
    return true;
}

bool cpwMouseDragCallback( pCpw cpw, CpwMouseDragCallback mousedragCallback )
{
    cpw->mousedragCallback = (pVoid)mousedragCallback;
    return true;
}

bool cpwMouseMoveCallback( pCpw cpw, CpwMouseMoveCallback mousemoveCallback )
{
    cpw->mousemoveCallback = (pVoid)mousemoveCallback;
    return true;
}

bool cpwMouseEntryCallback( pCpw cpw, CpwMouseEntryCallback mouseentryCallback )
{
    cpw->mouseentryCallback = (pVoid)mouseentryCallback;
    return true;
}

/*************************************************************************/
/*                                                                       */
/*   window specifc event handler setter api.                            */
/*                                                                       */
/*************************************************************************/

bool cpwWindowCreateCallback( pCpw cpw, CpwCreateCallback createCallback, uint_32 id )
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );

    if ( window == 0 ) {
        cpw_error_set( cpw, cpw_error_invalidwindowid );
        return false;
    }

    window->createCallback = (pVoid)createCallback;
    return true;
}

bool cpwWindowDisplayCallback( pCpw cpw, CpwDisplayCallback displayCallback, uint_32 id )
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );

    if ( window == 0 ) {
        cpw_error_set( cpw, cpw_error_invalidwindowid );
        return false;
    }

    window->displayCallback = (pVoid)displayCallback;
    return true;
}

bool cpwWindowReshapeCallback( pCpw cpw, CpwReshapeCallback reshapeCallback, uint_32 id )
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );

    if ( window == 0 ) {
        cpw_error_set( cpw, cpw_error_invalidwindowid );
        return false;
    }

    window->reshapeCallback = (pVoid)reshapeCallback;
    return true;
}

bool cpwWindowPositionCallback( pCpw cpw, CpwPositionCallback positionCallback, uint_32 id )
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );

    if ( window == 0 ) {
        cpw_error_set( cpw, cpw_error_invalidwindowid );
        return false;
    }

    window->positionCallback = (pVoid)positionCallback;
    return true;
}

bool cpwWindowKeyboardCallback( pCpw cpw, CpwKeyboardCallback keyCallback, uint_32 id )
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );

    if ( window == 0 ) {
        cpw_error_set( cpw, cpw_error_invalidwindowid );
        return false;
    }

    window->keyCallback = (pVoid)keyCallback;
    return true;
}

bool cpwWindowSystemKeyboardCallback( pCpw cpw, CpwSystemKeyboardCallback skeyCallback, uint_32 id )
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );

    if ( window == 0 ) {
        cpw_error_set( cpw, cpw_error_invalidwindowid );
        return false;
    }

    window->skeyCallback = (pVoid)skeyCallback;
    return true;
}

bool cpwWindowVisibilityCallback( pCpw cpw, CpwVisibilityCallback visibilityCallback, uint_32 id )
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );

    if ( window == 0 ) {
        cpw_error_set( cpw, cpw_error_invalidwindowid );
        return false;
    }

    window->visibilityCallback = (pVoid)visibilityCallback;
    return true;
}

bool cpwWindowMouseClickCallback( pCpw cpw, CpwMouseClickCallback mouseclickCallback, uint_32 id )
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );

    if ( window == 0 ) {
        cpw_error_set( cpw, cpw_error_invalidwindowid );
        return false;
    }

    window->mouseclickCallback = (pVoid)mouseclickCallback;
    return true;
}

bool cpwWindowMouseDragCallback( pCpw cpw, CpwMouseDragCallback mousedragCallback, uint_32 id )
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );

    if ( window == 0 ) {
        cpw_error_set( cpw, cpw_error_invalidwindowid );
        return false;
    }

    window->mousedragCallback = (pVoid)mousedragCallback;
    return true;
}

bool cpwWindowMouseMoveCallback( pCpw cpw, CpwMouseMoveCallback mousemoveCallback, uint_32 id )
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );

    if ( window == 0 ) {
        cpw_error_set( cpw, cpw_error_invalidwindowid );
        return false;
    }

    window->mousemoveCallback = (pVoid)mousemoveCallback;
    return true;
}

bool cpwWindowMouseEntryCallback( pCpw cpw, CpwMouseEntryCallback mouseentryCallback, uint_32 id )
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );

    if ( window == 0 ) {
        cpw_error_set( cpw, cpw_error_invalidwindowid );
        return false;
    }

    window->mouseentryCallback = (pVoid)mouseentryCallback;
    return true;
}

/*************************************************************************/
/*                                                                       */
/*   event stack manipulation api.                                       */
/*                                                                       */
/*************************************************************************/

/* set or clear an event filter flag */

bool cpwFilterEvent( pCpw cpw, CpwWindowEvent eventType, bool flag )
{
    bool old;
    CpwEventStack * es = (CpwEventStack*)cpw->stack;

    if ( eventType < 0 || eventType > CPW_MAXEVENTVALUE ) return false;

    old = es->filter.flag[eventType];
    es->filter.flag[eventType] = flag;

    return old;
}

/* return the next event */

CpwWindowEvent cpwNextEvent( pCpw cpw )
{
    CpwEventStack * es = (CpwEventStack*)cpw->stack;

    /* look ahead by one ( user is calling from a callback ) */

    if ( es->stack[es->nxtindex+1].used != 0 )
        return es->stack[es->nxtindex+1].event;

    return CPW_NO_EVENT;
}

/* return the next event for a particular window */

CpwWindowEvent cpwNextWindowEvent( pCpw cpw, uint_32 id ) 
{
    uint_8 index;
    CpwEventStack * es = (CpwEventStack*)cpw->stack;

    /* look ahead by one ( user is calling from a callback ) */

    index = es->nxtindex+1;

    while ( es->stack[index].used != 0 ) {

        if ( es->stack[index].winid == id )
            return es->stack[index].event;

        if ( index == es->insindex ) 
            break;

        index++;
    } 

    return CPW_NO_EVENT;
}

/* reset the event stack */

bool cpwClearEvents( pCpw cpw )
{
    CpwEventStack * es = (CpwEventStack*)cpw->stack;

    memset( es, 0, sizeof( CpwEventStack ) );

    es->insindex = 0;
    es->nxtindex = 0;

    return true;
}

/* clear a single type of event from pending events */

bool cpwClearEvent( pCpw cpw, CpwWindowEvent event ) 
{
    uint_8 index;
    CpwEventStack * es = (CpwEventStack*)cpw->stack;

    index = es->nxtindex;

    while ( es->stack[index].used != 0 ) {

        if ( es->stack[index].event == event )
            es->stack[index].filt = true;

        if ( index == es->insindex ) 
            break;

        index++;
    } 

    return true;
}

/* clear all events from pending events for a window */

bool cpwClearWindowEvents( pCpw cpw, uint_32 id ) 
{
    uint_8 index;
    CpwEventStack * es = (CpwEventStack*)cpw->stack;

    index = es->nxtindex;

    while ( es->stack[index].used != 0 ) {

        if ( es->stack[index].winid == id )
            es->stack[index].filt = true;

        if ( index == es->insindex ) 
            break;

        index++;
    } 

    return true;
}

/* clear a single type of event from pending events for a window */

bool cpwClearWindowEvent( pCpw cpw, CpwWindowEvent event, uint_32 id ) 
{
    uint_8 index;
    CpwEventStack * es = (CpwEventStack*)cpw->stack;

    index = es->nxtindex;

    while ( es->stack[index].used != 0 ) {

        if ( es->stack[index].event == event && es->stack[index].winid == id )
            es->stack[index].filt = true;

        if ( index == es->insindex ) 
            break;

        index++;
    } 

    return true;
}

/* pop the next event off the stack */

bool cpwPopEvent( pCpw cpw )
{
    CpwEventStack * es = (CpwEventStack*)cpw->stack;

    /* look ahead by one ( user is calling from a callback ) */

    if ( es->stack[es->nxtindex+1].used != 0 ) {
        es->stack[es->nxtindex+1].filt = true;
        return true;
    }

    return false;
}

/* return the number of pending events */

int_32 cpwStackDepth( pCpw cpw )
{
    CpwEventStack * es = (CpwEventStack*)cpw->stack;
    if ( es->nxtindex <= es->insindex ) 
        return ( es->insindex - es->nxtindex );
    else
        return ( es->nxtindex - es->insindex );
}

/*************************************************************************/
/*                                                                       */
/*   sending events to the application                                   */
/*                                                                       */
/*************************************************************************/

/* send window event helper */

void cpw_event_create( pCpw cpw, uint_32 id, bool flag ) 
{
    #ifdef CPW_COMPILEOPT_WINDOWSPECIFICEVENTS

    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    if ( window->createCallback ) {
        cpw_window_makecurrent( cpw, id );
        window->createCallback( cpw, id, flag );
    } else
    
    #endif /* CPW_COMPILEOPT_WINDOWSPECIFICEVENTS */

    if ( cpw->createCallback ) {
        CpwCreateCallback callback = (CpwCreateCallback)cpw->createCallback;
        cpw_window_makecurrent( cpw, id );
        callback( cpw, id, flag );
    } else 
        defaultCreateCallback( cpw, id, flag );

    #ifdef DEBUGUSEREVENTS
    if ( flag ) printf( "w create (%u)\n", id ); else printf( "w destroy (%u)\n", id );
    #endif
}

/* send display event helper */

void cpw_event_display( pCpw cpw, uint_32 id ) 
{
    #ifdef CPW_COMPILEOPT_WINDOWSPECIFICEVENTS

    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    if ( window->displayCallback ) {
        cpw_window_makecurrent( cpw, id );
        window->displayCallback( cpw, id );
    } else
    
    #endif /* CPW_COMPILEOPT_WINDOWSPECIFICEVENTS */

    if ( cpw->displayCallback ) {
        CpwDisplayCallback callback = (CpwDisplayCallback)cpw->displayCallback;
        cpw_window_makecurrent( cpw, id );
        callback( cpw, id );
    } else 
        defaultDisplayCallback( cpw, id );

    #ifdef DEBUGUSEREVENTS
    printf( "w draw (%u: %dx%d)\n", id, window->width, window->height );
    #endif
}

/* send reshape event helper */

void cpw_event_reshape( pCpw cpw, uint_32 id, uint_32 w, uint_32 h ) 
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    window->width   = w;
    window->height  = h;

    #ifdef CPW_COMPILEOPT_WINDOWSPECIFICEVENTS

    if ( window->createCallback ) {
        cpw_window_makecurrent( cpw, id );
        window->reshapeCallback( cpw, id, w, h );
    } else
    
    #endif /* CPW_COMPILEOPT_WINDOWSPECIFICEVENTS */

    if ( cpw->reshapeCallback ) {
        CpwReshapeCallback callback = (CpwReshapeCallback)cpw->reshapeCallback;
        cpw_window_makecurrent( cpw, id );
        callback( cpw, id, w, h );
    } else 
        defaultReshapeCallback( cpw, id, w, h );

    #ifdef DEBUGUSEREVENTS
    printf( "w reshape (%u: %dx%d)\n", id, w, h );
    #endif
}

/* send mouseclick event helper */

void cpw_event_mouseclick( pCpw cpw, uint_32 id, uint_32 state, uint_32 button,
                           uint_32 x, uint_32 y ) 
{
    /* check for popup menu attachments */

    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    if ( window->buttonmenuid[button] > 0 ) {

        if ( state == CPW_BUTTON_UP ) return;
        
        #ifdef DEBUGUSEREVENTS
        printf( "m click popup (%u)\n", id );
        #endif

        cpw_menus_popupselected( cpw, window, window->buttonmenuid[button], x, y );

        #ifdef DEBUGUSEREVENTS
        printf( "m click popup done (%u)\n", id );
        #endif
        return;
    }

    #ifdef CPW_COMPILEOPT_WINDOWSPECIFICEVENTS

    if ( window->mouseclickCallback ) {
        window->mouseclickCallback( cpw, id, state, button, x, y );
    } else
    
    #endif /* CPW_COMPILEOPT_WINDOWSPECIFICEVENTS */

    /* otherwise, send the event to the application */

    if ( cpw->mouseclickCallback ) {
        CpwMouseClickCallback callback = (CpwMouseClickCallback)cpw->mouseclickCallback;
        callback( cpw, id, state, button, x, y );
    } else 
        defaultMouseClickCallback( cpw, id, state, button, x, y );

    #ifdef DEBUGUSEREVENTS
    printf( "m click (%u)\n", id );
    #endif
}

/* send mouse drag event helper */

void cpw_event_mousedrag( pCpw cpw, uint_32 id, uint_32 button, int_32 x, int_32 y ) 
{
    #ifdef CPW_COMPILEOPT_WINDOWSPECIFICEVENTS

    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    if ( window->mousedragCallback ) {
        window->mousedragCallback( cpw, id, button, x, y );
    } else
    
    #endif /* CPW_COMPILEOPT_WINDOWSPECIFICEVENTS */

    if ( cpw->mousedragCallback ) {
        CpwMouseDragCallback callback = (CpwMouseDragCallback)cpw->mousedragCallback;
        callback( cpw, id, button, x, y );
    } else 
        defaultMouseDragCallback( cpw, id, button, x, y );

    #ifdef DEBUGUSEREVENTS
    printf( "m drag (%u)\n", id );
    #endif
}

/* send mouse motion event helper */

void cpw_event_mousemove( pCpw cpw, uint_32 id, uint_32 x, uint_32 y ) 
{
    #ifdef CPW_COMPILEOPT_WINDOWSPECIFICEVENTS

    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    if ( window->mousemoveCallback ) {
        window->mousemoveCallback( cpw, id, x, y );
    } else
    
    #endif /* CPW_COMPILEOPT_WINDOWSPECIFICEVENTS */

    if ( cpw->mousemoveCallback ) {
        CpwMouseMoveCallback callback = (CpwMouseMoveCallback)cpw->mousemoveCallback;
        callback( cpw, id, x, y );
    } else 
        defaultMouseMoveCallback( cpw, id, x, y );

    #ifdef DEBUGUSEREVENTS
    /*printf( "m move (%u)\n", id );*/
    #endif
}

/* send keyboard event helper */

void cpw_event_keyboard( pCpw cpw, uint_32 id, uint_32 keycode, bool shift, uint_32 state, 
                         uint_32 x, uint_32 y ) 
{
    #ifdef CPW_COMPILEOPT_WINDOWSPECIFICEVENTS

    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    if ( window->keyCallback ) {
        window->keyCallback( cpw, id, 
          cpw_keymouse_translatekeyconstant( keycode, shift ), state, x, y );
    } else
    
    #endif /* CPW_COMPILEOPT_WINDOWSPECIFICEVENTS */

    if ( cpw->keyCallback ) {
        CpwKeyboardCallback callback = (CpwKeyboardCallback)cpw->keyCallback;
        callback( cpw, id, 
            cpw_keymouse_translatekeyconstant( keycode, shift ), state, x, y );
    } else 
        defaultKeyboardCallback( cpw, id, keycode, state, x, y );

    #ifdef DEBUGUSEREVENTS
    printf( "key (%u)\n", id );
    #endif
}

/* send system keyboard event helper */

void cpw_event_systemkeyboard( pCpw cpw, uint_32 id, uint_32 key, uint_32 state, 
                         uint_32 x, uint_32 y ) 
{
    #ifdef CPW_COMPILEOPT_WINDOWSPECIFICEVENTS

    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    if ( window->skeyCallback ) {
        window->skeyCallback( cpw, id, key, state, x, y );
    } else
    
    #endif /* CPW_COMPILEOPT_WINDOWSPECIFICEVENTS */

    if ( cpw->skeyCallback ) {
        CpwSystemKeyboardCallback callback = (CpwSystemKeyboardCallback)cpw->skeyCallback;
        callback( cpw, id, key, state, x, y );
    } else 
        defaultSystemKeyboardCallback( cpw, id, key, state, x, y );

    #ifdef DEBUGUSEREVENTS
    printf( "skey (%u)\n", id );
    #endif
}

/* send window positioning event helper */

void cpw_event_position( pCpw cpw, uint_32 id, uint_32 x, uint_32 y ) 
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    window->x   = x;
    window->y   = y;

    #ifdef CPW_COMPILEOPT_WINDOWSPECIFICEVENTS

    if ( window->positionCallback ) {
        window->positionCallback( cpw, id, x, y );
    } else
    
    #endif /* CPW_COMPILEOPT_WINDOWSPECIFICEVENTS */

    if ( cpw->positionCallback ) {
        CpwPositionCallback callback = (CpwPositionCallback)cpw->positionCallback;
        callback( cpw, id, x, y );
    } else 
        defaultPositionCallback( cpw, id, x, y );

    #ifdef DEBUGUSEREVENTS
    printf( "w position (%u:%dx%d)\n", id, x, y );
    #endif
}

/* send visibility changes event helper */

void cpw_event_visibility( pCpw cpw, uint_32 id, uint_32 state ) 
{
    #ifdef CPW_COMPILEOPT_WINDOWSPECIFICEVENTS

    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    if ( window->visibilityCallback ) {
        window->visibilityCallback( cpw, id, state );
    } else
    
    #endif /* CPW_COMPILEOPT_WINDOWSPECIFICEVENTS */

    if ( cpw->visibilityCallback ) {
        CpwVisibilityCallback callback = (CpwVisibilityCallback)cpw->visibilityCallback;
        callback( cpw, id, state );
    } else 
        defaultVisibilityCallback( cpw, id, state );

    #ifdef DEBUGUSEREVENTS
    switch( state ) {
        case CPW_HIDE:
        printf( "w hide (%u) \n", id ); break;
        case CPW_SHOW:
        printf( "w show (%u) \n", id ); break;
        case CPW_GAINEDFOCUS:
        printf( "w gain (%u) \n", id ); break;
        case CPW_LOSTFOCUS:
        printf( "w lost (%u) \n", id ); break;
        case CPW_ICONIC:
        printf( "w iconic (%u) \n", id ); break;
        case CPW_RESTORED:
        printf( "w restore (%u) \n", id ); break;
    }
    #endif
}

/* send mouse entry event helper */

void cpw_event_mouseentry( pCpw cpw, uint_32 id, uint_32 state ) 
{
    #ifdef CPW_COMPILEOPT_WINDOWSPECIFICEVENTS

    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    if ( window->mouseentryCallback ) {
        window->mouseentryCallback( cpw, id, state );
    } else
    
    #endif /* CPW_COMPILEOPT_WINDOWSPECIFICEVENTS */

    if ( cpw->mouseentryCallback ) {
        CpwMouseEntryCallback callback = (CpwMouseEntryCallback)cpw->mouseentryCallback;
        callback( cpw, id, state );
    } else 
        defaultMouseEntryCallback( cpw, id, state );

    #ifdef DEBUGUSEREVENTS
    if ( state == CPW_MOUSE_ENTER ) printf( "m enter (%u)\n", id ); 
    else printf( "m leave (%u)\n", id );
    #endif
}

/* menu selected event helper */

void cpw_event_menuselect( pCpw cpw, uint_32 id, uint_32 menuid, uint_32 entryid )
{
    cpw_menus_popupcallback( cpw, id, menuid, entryid );
}

/*************************************************************************/
/*                                                                       */
/*   processing events on the stack                                      */
/*                                                                       */
/*************************************************************************/

/* process the top event on the stack */

bool cpw_event_sendevent( pCpw cpw )
{
    CpwEventStack * es = (CpwEventStack*)cpw->stack;

    eventpurged:

    /* if next event is valid event */

    if ( es->stack[es->nxtindex].used != 0 ) 
    {
        
        /* if next event has been manually purged */

        if ( es->stack[es->nxtindex].filt == 1 ) {

            es->stack[es->nxtindex].used  = false;
            es->stack[es->nxtindex].filt  = false;
            es->stack[es->nxtindex].event = CPW_NO_EVENT;

            #ifdef DEBUGEVENTRAW
            printf( "p%1d:%d/%d\n", es->stack[es->nxtindex].event, es->insindex, es->nxtindex );
            #endif

            es->nxtindex++;

            goto eventpurged;
        }

        switch( es->stack[es->nxtindex].event ) 
        {
            case CPW_NO_EVENT:
            break;

            case CPW_MOUSECLICK_EVENT:
                cpw_event_mouseclick( cpw, es->stack[es->nxtindex].winid, 
                                           es->stack[es->nxtindex].p1, 
                                           es->stack[es->nxtindex].p2, 
                                           es->stack[es->nxtindex].p3, 
                                           es->stack[es->nxtindex].p4 );
            break;

            case CPW_MOUSEMOTION_EVENT:
                cpw_event_mousemove( cpw, es->stack[es->nxtindex].winid, 
                                          es->stack[es->nxtindex].p1, 
                                          es->stack[es->nxtindex].p2 );
            break;

            case CPW_MOUSEDRAG_EVENT:
                cpw_event_mousedrag( cpw, es->stack[es->nxtindex].winid, 
                                          es->stack[es->nxtindex].p1, 
                                          es->stack[es->nxtindex].p2, 
                                          es->stack[es->nxtindex].p3 );
            break;

            case CPW_MOUSEENTRY_EVENT:
                cpw_event_mouseentry( cpw, es->stack[es->nxtindex].winid, 
                                           es->stack[es->nxtindex].p1 );
            break;

            case CPW_KEYBOARD_EVENT:
                cpw_event_keyboard( cpw, es->stack[es->nxtindex].winid, 
                                         es->stack[es->nxtindex].p1, 
                                         es->stack[es->nxtindex].p2, 
                                         es->stack[es->nxtindex].p3, 
                                         es->stack[es->nxtindex].p4,
                                         es->stack[es->nxtindex].p5 );
            break;

            case CPW_SYSKEYBOARD_EVENT:
                cpw_event_systemkeyboard( cpw, es->stack[es->nxtindex].winid, 
                                               es->stack[es->nxtindex].p1, 
                                               es->stack[es->nxtindex].p2, 
                                               es->stack[es->nxtindex].p3, 
                                               es->stack[es->nxtindex].p4 );
            break;

            case CPW_CREATE_EVENT:
                cpw_event_create( cpw, es->stack[es->nxtindex].winid, true );
            break;

            case CPW_DESTROY_EVENT:
                cpw_event_create( cpw, es->stack[es->nxtindex].winid, false );
            break;

            case CPW_POSITION_EVENT:
                if ( es->stack[es->nxtindex+1].event == CPW_POSITION_EVENT ) break;
                cpw_event_position( cpw, es->stack[es->nxtindex].winid, 
                                         es->stack[es->nxtindex].p1, 
                                         es->stack[es->nxtindex].p2 );
            break;

            case CPW_VISIBILITY_EVENT:
                cpw_event_visibility( cpw, es->stack[es->nxtindex].winid, 
                                           es->stack[es->nxtindex].p1 );
            break;

            case CPW_DISPLAY_EVENT:
                cpw_event_display( cpw, es->stack[es->nxtindex].winid );
            break;

            case CPW_RESHAPE_EVENT:
                if ( es->stack[es->nxtindex+1].event == CPW_RESHAPE_EVENT ) break;
                cpw_event_reshape( cpw, es->stack[es->nxtindex].winid, 
                                        es->stack[es->nxtindex].p1, 
                                        es->stack[es->nxtindex].p2 );

            case CPW_MENUSELECT_EVENT:
                cpw_event_menuselect( cpw, es->stack[es->nxtindex].winid,
                                           es->stack[es->nxtindex].p1, 
                                           es->stack[es->nxtindex].p2 );
            break;
        }
        es->stack[es->nxtindex].used  = false;
        es->stack[es->nxtindex].filt  = false;
        es->stack[es->nxtindex].event = CPW_NO_EVENT;

        #ifdef DEBUGEVENTRAW
        printf( "s%1d:%d/%d\n", es->stack[es->nxtindex].event, es->insindex, es->nxtindex );
        #endif

        es->nxtindex++;
    }

    /* pending events? */

    if ( es->stack[es->nxtindex].used != 0 ) return true;
    return false;
}

/* process the top num events on the stack, return false if there are no pending events */

bool cpw_event_sendevents( pCpw cpw, uint_32 count )
{
    while ( count > 0 ) {
        if ( !cpw_event_sendevent( cpw ) ) return false;
        count--;
    }
    return true;
}

/*************************************************************************/
/*                                                                       */
/*   event stack.                                                        */
/*                                                                       */
/*************************************************************************/

/*
    stack[0] = event <-------
    stack[1] = event         | 
    stack[2] = event         |
    stack[3] = event         |
    stack[4] = event         |
    ..                       |
    stack[255] = event   ----
*/

/* quick filter event check for any event insert code */

bool cpw_event_filtered( pCpw cpw, CpwWindowEvent event ) 
{
    CpwEventStack * es = (CpwEventStack*)cpw->stack;
    return es->filter.flag[event];
}

/* insert standard event - returns false if event is filtered */

bool cpw_event_insert( pCpw cpw, uint_32 id, CpwWindowEvent event, 
                       uint_32 p1, uint_32 p2, uint_32 p3, uint_32 p4, uint_32 p5 )
{
    CpwEventStack * es = (CpwEventStack*)cpw->stack;

    if ( es->filter.flag[event] ) return false;

    if ( es->stack[es->insindex].used == 0 ) {

        es->stack[es->insindex].used   = true;
        es->stack[es->insindex].winid  = id;
        es->stack[es->insindex].event  = event;
        es->stack[es->insindex].p1     = p1;
        es->stack[es->insindex].p2     = p2;
        es->stack[es->insindex].p3     = p3;
        es->stack[es->insindex].p4     = p4;
        es->stack[es->insindex].p5     = p5;
        es->insindex++;

        #ifdef DEBUGEVENTRAW
        printf( "r%1d:%d/%d\n", es->stack[es->nxtindex].event, es->insindex, es->nxtindex );
        #endif
    }

    return true;
}

/* insert high priority event */

bool cpw_event_insertnext( pCpw cpw, uint_32 id, CpwWindowEvent event,
                       uint_32 p1, uint_32 p2, uint_32 p3, uint_32 p4, uint_32 p5 )
{
    switch( event ) 
    {
        case CPW_NO_EVENT:
        break;

        case CPW_MOUSECLICK_EVENT:
            cpw_event_mouseclick( cpw, id, p1, p2, p3, p4 );
        break;

        case CPW_MOUSEMOTION_EVENT:
            cpw_event_mousemove( cpw, id, p1, p2 );
        break;

        case CPW_MOUSEDRAG_EVENT:
            cpw_event_mousedrag( cpw, id, p1, p2, p3 );
        break;

        case CPW_MOUSEENTRY_EVENT:
            cpw_event_mouseentry( cpw, id, p1 ); 
        break;

        case CPW_KEYBOARD_EVENT:
            cpw_event_keyboard( cpw, id, p1, p2, p3, p4, p5 );
        break;

        case CPW_SYSKEYBOARD_EVENT:
            cpw_event_systemkeyboard( cpw, id, p1, p2, p3, p4 );
        break;

        case CPW_CREATE_EVENT:
            cpw_event_create( cpw, id, true );
        break;

        case CPW_DESTROY_EVENT:
            cpw_event_create( cpw, id, false );
        break;

        case CPW_POSITION_EVENT:
            cpw_event_position( cpw, id, p1, p2 );
        break;

        case CPW_VISIBILITY_EVENT:
            cpw_event_visibility( cpw, id, p1 ); 
        break;

        case CPW_DISPLAY_EVENT:
            cpw_event_display( cpw, id );
        break;

        case CPW_RESHAPE_EVENT:
            cpw_event_reshape( cpw, id, p1, p2 ); 
        break;
    }
    return true;
}

/*************************************************************************/
/*                                                                       */
/*   localhost window events interface                                   */
/*                                                                       */
/*************************************************************************/

/* the localhost adapter calls this api to inject events into the event stack */

void cpw_hostevent_setfocus( pCpw cpw, uint_32 id ) /* win32 */
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;

    cpw->win_foregroundid = id;

    cpw->win_currentid = 0;
    cpw_localhost_makecurrent( cpw, window->plhwc );

    cpw_event_insert( cpw, id, CPW_VISIBILITY_EVENT, CPW_GAINEDFOCUS, 0, 0, 0, 0 );
    return;
}

void cpw_hostevent_lostfocus( pCpw cpw, uint_32 id ) 
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;
    if ( cpw->win_foregroundid == id ) 
        cpw->win_foregroundid = 0;

    if ( window->fullscreen == true )
        cpw_localhost_inconifywindow( window );         

    cpw_event_insert( cpw, id, CPW_VISIBILITY_EVENT, CPW_LOSTFOCUS, 0, 0, 0, 0 );
    return;
}

void cpw_hostevent_cursorenter( pCpw cpw, uint_32 id ) 
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;
    if ( window->cursorpresent == true ) return;
    window->cursorpresent = true;
    cpw_localhost_setcursor( cpw, window );
    cpw_event_insert( cpw, id, CPW_MOUSEENTRY_EVENT, CPW_MOUSE_ENTER, 0, 0, 0, 0 );
    return;
}

void cpw_hostevent_cursorleft( pCpw cpw, uint_32 id ) 
{
    pCpwWin window = cpw_windowlist_lookup( cpw, id );
    if ( window == 0 ) return;
    if ( window->cursorpresent == false ) return;
    window->cursorpresent = false;
    cpw_localhost_restorecursor( cpw );
    cpw_event_insert( cpw, id, CPW_MOUSEENTRY_EVENT, CPW_MOUSE_LEAVE, 0, 0, 0, 0 );
    return;
}

void cpw_hostevent_draw( pCpw cpw, uint_32 id ) 
{
    /* post draw events directly to the window */

    cpw_event_insertnext( cpw, id, CPW_DISPLAY_EVENT, 0, 0, 0, 0, 0 );
}

void cpw_hostevent_reshape( pCpw cpw, uint_32 id, uint_32 width, uint_32 height ) 
{
    /* note: if we use the indirect events (event stack inserts) instead
       of the direct reshape/redraw usage we get the 'GLUT reshape issue'
       on win32. I need to do some work on the win32 adapter to fix this...
       and then i'll make the default method indirect. */

    cpw_event_insertnext( cpw, id, CPW_RESHAPE_EVENT, width, height, 0, 0, 0 );
    cpw_event_insertnext( cpw, id, CPW_DISPLAY_EVENT, 0, 0, 0, 0, 0 );
}

void cpw_hostevent_destroy( pCpw cpw, uint_32 id )
{
    cpw_event_insert( cpw, id, CPW_DESTROY_EVENT, 0, 0, 0, 0, 0 );
}

void cpw_hostevent_position( pCpw cpw, uint_32 id, uint_32 x, uint_32 y )
{
    cpw_event_insert( cpw, id, CPW_POSITION_EVENT, x, y, 0, 0, 0 );
}

void cpw_hostevent_visibility( pCpw cpw, uint_32 id, uint_32 state )
{
    cpw_event_insert( cpw, id, CPW_VISIBILITY_EVENT, state, 0, 0, 0, 0 );
}

void cpw_hostevent_mouseclick( pCpw cpw, uint_32 id, uint_32 state, uint_32 button, uint_32 x, uint_32 y )
{
    cpw_event_insert( cpw, id, CPW_MOUSECLICK_EVENT, state, button, x, y, 0 );
}

void cpw_hostevent_mousedrag( pCpw cpw, uint_32 id, uint_32 button, int_32 x, int_32 y )
{
    cpw_event_insert( cpw, id, CPW_MOUSEDRAG_EVENT, button, x, y, 0, 0 );
}

void cpw_hostevent_mousemove( pCpw cpw, uint_32 id, uint_32 x, uint_32 y )
{
    cpw_event_insert( cpw, id, CPW_MOUSEMOTION_EVENT, x, y, 0, 0, 0 );
}

void cpw_hostevent_keyboard( pCpw cpw, uint_32 id, uint_32 key, bool shift, uint_32 state, uint_32 x, uint_32 y )
{
    cpw_event_insert( cpw, id, CPW_KEYBOARD_EVENT, key, shift, state, x, y );
}

void cpw_hostevent_systemkeyboard( pCpw cpw, uint_32 id, uint_32 key, uint_32 state, uint_32 x, uint_32 y )
{
    cpw_event_insert( cpw, id, CPW_SYSKEYBOARD_EVENT, key, state, x, y, 0 );
}

void cpw_hostevent_menuselect( pCpw cpw, uint_32 id, uint_32 menuid, uint_32 entryid )
{
    cpw_event_insert( cpw, id, CPW_MENUSELECT_EVENT, menuid, entryid, 0, 0, 0 );
}