root/trunk/orca/gl/gx.c

Revision 144, 45.4 kB (checked in by krobillard, 3 years ago)

Native arguments are now kept on the stack until after the call and the
result is now always put into a1.

Line 
1/*============================================================================
2    ORCA OpenGL Module
3    Copyright (C) 2005-2006  Karl Robillard
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18===========================================================================*/
19
20
21#include <assert.h>
22#include <stdio.h>
23#include <string.h>
24#include <glv_keys.h>
25#include "gx.h"
26#include "math3d.h"
27#include "os.h"
28#include "res_font.h"
29#include "dlist.h"
30#include "audio.h"
31#include <time.h>
32
33#ifdef __APPLE__
34#include <OpenGL/glu.h>
35#else
36#include <GL/glu.h>
37#endif
38
39
40//#define PERF_CLOCK  1
41
42#define RESTYPE    argc
43
44#define MOUSE_UNSET     -9999
45
46
47extern OPortDevice joystickPortDev;
48extern int wav_loader( const char*, uint8_t*, int );
49extern int png_loader( const char*, uint8_t*, int );
50extern int png_save( FILE*, OImage* );
51extern void makeTexfontNative( OValue* );
52
53
54struct GxEnv gEnv;
55GLView* gView = 0;
56
57
58enum WidgetValue    // widget
59{
60    WV_PARENT = 1,
61    WV_CHILDREN,
62    WV_X,
63    WV_Y,
64    WV_W,
65    WV_H,
66    WV_MOUSE_MOVE,
67    WV_MOUSE_DOWN,
68    WV_MOUSE_UP,
69    WV_MOUSE_WHEEL,
70    WV_KEY_DOWN,
71    WV_KEY_UP,
72    WV_RENDER,
73    WV_COUNT
74};
75
76
77enum InputValue    // input_values
78{
79    INPUT_CODE = 0,
80    INPUT_MX,
81    INPUT_MY,
82    INPUT_DX,
83    INPUT_DY
84};
85
86
87//----------------------------------------------------------------------------
88
89
90/*
91   Returns non-zero if error thrown.
92*/
93int gxHandleError()
94{
95    if( orErrorThrown )
96    {
97        switch( orErrorType(orErrorThrown) )
98        {
99            case OR_ERROR_QUIT:
100                gEnv.running = 0;
101                glv_setEventHandler( gView, 0 );
102                return 1;
103
104            //case OR_ERROR_THROW:
105            default:
106                return 1;
107                //orPrintNative( orErrorThrown );
108                //break;
109        }
110
111        //orErrorClear;
112    }
113    return 0;
114}
115
116
117static int mapButton( int code )
118{
119    switch( code )
120    {
121        case GLV_BUTTON_LEFT:   return 1;
122        case GLV_BUTTON_RIGHTreturn 2;
123        case GLV_BUTTON_MIDDLE: return 3;
124    }
125    return 0;
126}
127
128
129/*
130static int mapState( int code )
131{
132    switch( code )
133    {
134        case GLV_MASK_SHIFT:
135        case GLV_MASK_CTRL:
136        case GLV_MASK_ALT:
137        case GLV_MASK_CAPS:
138        case GLV_MASK_LEFT:
139        case GLV_MASK_MIDDLE:
140        case GLV_MASK_RIGHT:
141    }
142}
143*/
144
145
146static OValue* matchKeyBlock( OIndex blkN, int code )
147{
148    OBlock* blk = orBLOCKS + blkN;
149    OValue* it  = blk->values;
150    OValue* end = blk->values + blk->used;
151
152    while( it != end )
153    {
154        if( it->pair[0] == code )
155        {
156            return ++it;
157        }
158        it += 2;
159    }
160    return 0;
161}
162
163
164static OValue* widgetEventHandler( int which )
165{
166    OBlock* ctx;
167    OValue* val;
168
169    if( gEnv.widgetCtxN )
170    {
171        ctx = orBlockPtr( gEnv.widgetCtxN );
172        val = ctx->values + which;
173        if( val->type == OT_BLOCK )
174            return val;
175    }
176    return 0;
177}
178
179
180static void eventHandler( GLView* view, GLViewEvent* event )
181{
182    (void) view;
183
184    switch( event->type )
185    {
186        case GLV_EVENT_FOCUS_IN:
187            printf( "KR focus in\n" );
188            gEnv.prev_mouseX = MOUSE_UNSET;
189            break;
190
191        case GLV_EVENT_FOCUS_OUT:
192            printf( "KR focus out\n" );
193            break;
194
195        case GLV_EVENT_RESIZE:
196            gEnv.view_wd     = (double) view->width;
197            gEnv.view_hd     = (double) view->height;
198            gEnv.view_aspect = gEnv.view_wd / gEnv.view_hd;
199            break;
200
201        case GLV_EVENT_CLOSE:
202            gEnv.running = 0;
203            glv_setEventHandler( gView, 0 );
204            break;
205
206        case GLV_EVENT_BUTTON_DOWN:
207        {
208            OValue* blkV = widgetEventHandler( WV_MOUSE_DOWN );
209            if( blkV )
210            {
211                OValue* iv = gEnv.input_values;
212
213                iv[ INPUT_CODE ].integer = mapButton( event->code );
214                iv[ INPUT_MX ].integer   = event->x;
215                iv[ INPUT_MY ].integer   = gView->height - event->y - 1;
216
217                orEvalBlock( orBLOCKS + blkV->index, blkV->series.it );
218                gxHandleError();
219            }
220        }
221            break;
222
223        case GLV_EVENT_BUTTON_UP:
224        {
225            OValue* blkV = widgetEventHandler( WV_MOUSE_UP );
226            if( blkV )
227            {
228                OValue* iv = gEnv.input_values;
229
230                iv[ INPUT_CODE ].integer = mapButton( event->code );
231                iv[ INPUT_MX ].integer   = event->x;
232                iv[ INPUT_MY ].integer   = gView->height - event->y - 1;
233
234                orEvalBlock( orBLOCKS + blkV->index, blkV->series.it );
235                gxHandleError();
236            }
237        }
238            break;
239
240        case GLV_EVENT_MOTION:
241        {
242            OValue* blkV = widgetEventHandler( WV_MOUSE_MOVE );
243            if( blkV )
244            {
245                OValue* iv = gEnv.input_values;
246
247                //printf( "KR mouse-move %d %d\n", event->x, event->y );
248
249                iv[ INPUT_MX ].integer = event->x;
250                iv[ INPUT_MY ].integer = gView->height - event->y - 1;
251
252                if( gEnv.prev_mouseX == MOUSE_UNSET )
253                {
254                    printf( "KR mouse reset\n" );
255                    iv[ INPUT_DX ].integer = 0;
256                    iv[ INPUT_DY ].integer = 0;
257                }
258                else
259                {
260                    iv[ INPUT_DX ].integer = event->x - gEnv.prev_mouseX;
261                    iv[ INPUT_DY ].integer = gEnv.prev_mouseY - event->y;
262                }
263
264                orEvalBlock( orBLOCKS + blkV->index, blkV->series.it );
265                gxHandleError();
266            }
267
268            gEnv.prev_mouseX = event->x;
269            gEnv.prev_mouseY = event->y;
270        }
271            break;
272
273        case GLV_EVENT_WHEEL:
274        {
275            OValue* blkV = widgetEventHandler( WV_MOUSE_WHEEL );
276            if( blkV )
277            {
278                OValue* iv = gEnv.input_values;
279
280                iv[ INPUT_DY ].integer = event->y;
281
282                orEvalBlock( orBLOCKS + blkV->index, blkV->series.it );
283                gxHandleError();
284            }
285        }
286            break;
287
288        case GLV_EVENT_KEY_DOWN:
289        {
290            OValue* blkV = widgetEventHandler( WV_KEY_DOWN );
291            if( blkV )
292            {
293                OValue* hval = matchKeyBlock( blkV->index, event->code );
294                if( hval )
295                {
296                    orEvalBlock( orBLOCKS + hval->index, hval->series.it );
297                    gxHandleError();
298                }
299            }
300        }
301            break;
302
303        case GLV_EVENT_KEY_UP:
304        {
305            OValue* blkV = widgetEventHandler( WV_KEY_UP );
306            if( blkV )
307            {
308                OValue* hval = matchKeyBlock( blkV->index, event->code );
309                if( hval )
310                {
311                    orEvalBlock( orBLOCKS + hval->index, hval->series.it );
312                    gxHandleError();
313                }
314            }
315        }
316            break;
317    }
318}
319
320
321void getVector( int count, OValue* val, GLdouble* vec )
322{
323    OValue* end = val + count;
324    while( val != end )
325    {
326        if( val->type == OT_DECIMAL )
327            *vec++ = orDecimal(val);
328        else if( val->type == OT_INTEGER )
329            *vec++ = (GLdouble) orInt(val);
330        else
331            *vec++ = 0.0;
332        ++val;
333    }
334}
335
336
337//----------------------------------------------------------------------------
338
339
340void gxDisableTexture()
341{
342    glDisable( GL_TEXTURE_2D );
343}
344
345
346void gxEnableTexture0()
347{
348    glEnable( GL_TEXTURE_2D );
349}
350
351
352void gxStateText()
353{
354    gEnv.state.stateId = GRS_TEXT;
355
356    glDepthMask( GL_FALSE );
357
358    glDisable( GL_ALPHA_TEST );
359    glDisable( GL_DEPTH_TEST );
360    glDisable( GL_FOG );
361    glDisable( GL_LIGHTING );
362    glDisable( GL_COLOR_MATERIAL );
363
364    glEnable( GL_BLEND );
365    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
366
367    gxEnableTexture0();
368
369    //glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
370}
371
372
373void gxStateSolid()
374{
375    gEnv.state.stateId = GRS_SOLID;
376
377    glDepthMask( GL_FALSE );
378    glDisable( GL_LIGHTING );
379    glDisable( GL_BLEND );
380    glDisable( GL_CULL_FACE );
381
382    gxDisableTexture();
383}
384
385
386void gxStateModel()
387{
388    gEnv.state.stateId = GRS_MODEL;
389
390    glDepthMask( GL_TRUE );
391    glShadeModel( GL_SMOOTH );
392
393    glDisable( GL_POLYGON_STIPPLE );
394
395    glColorMaterial( GL_FRONT, GL_DIFFUSE );
396    glEnable( GL_COLOR_MATERIAL );
397
398    glEnable( GL_DEPTH_TEST );
399    glEnable( GL_CULL_FACE );
400    glEnable( GL_LIGHTING );
401    //glEnable( GL_LIGHT0 );
402
403    glDisable( GL_BLEND );
404
405    //gxEnableTexture0();
406}
407
408
409static GLuint buildTexture( OImage* img, int mipmap )
410{
411    GLuint id;
412    GLenum format;
413    GLint  comp;
414
415    switch( img->format )
416    {
417        case OR_IMG_GRAY:
418            format = GL_LUMINANCE;
419            comp   = 1;
420            //format = GL_LUMINANCE_ALPHA;
421            //comp   = GL_LUMINANCE_ALPHA;
422            break;
423
424        case OR_IMG_RGB:
425            format = GL_RGB;
426            comp   = 3;
427            break;
428
429        case OR_IMG_RGBA:
430            format = GL_RGBA;
431            comp   = 4;
432            break;
433
434        default:
435            return 0;
436    }
437
438    glGenTextures( 1, &id );
439    glBindTexture( GL_TEXTURE_2D, id );
440
441    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
442    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
443
444    if( mipmap )
445    {
446        gluBuild2DMipmaps( GL_TEXTURE_2D, comp, img->width, img->height,
447                           format, GL_UNSIGNED_BYTE, img + 1 );
448    }
449    else
450    {
451        glTexImage2D( GL_TEXTURE_2D, 0, comp, img->width, img->height, 0,
452                      format, GL_UNSIGNED_BYTE, img + 1 );
453    }
454
455    glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
456
457    return id;
458}
459
460
461extern void orLoadNative( OValue* a1 );
462extern TextureResource* createTexture();
463
464GLuint gxLoadTexture( OValue* val )
465{
466    TextureResource* tex;
467    uint32_t key;
468    OValue* result;
469
470    assert( orIsString(val->type) );
471
472    key = gxHashString( val );
473    tex = gxTexture( key );
474    if( tex )
475        return tex->glTexId;
476
477    tex = createTexture();
478    if( tex )
479    {
480        resd_add( &gEnv.resd, &tex->res, key );
481
482        result = orTOS;
483        orCopyV( result, *val );
484        orLoadNative( result );
485
486        if( result->type == OT_IMAGE )
487        {
488            OBinary* bin = orSTRING(result);
489            if( bin->buf )
490            {
491                tex->imgHold = orHold( OT_BINARY, orBinaryN(bin) );
492                tex->glTexId = buildTexture( (OImage*) bin->buf, 0 );
493
494                return tex->glTexId;
495            }
496        }
497    }
498    return 0;
499}
500
501
502//----------------------------------------------------------------------------
503
504
505/**
506  Returns TexGlyphInfo of rendered glyph.
507  Advances x.
508*/
509static TexGlyphInfo* renderGlyphXY( const TexFont* txf, int c,
510                                    GLfloat* xp, GLfloat y,
511                                    const TexGlyphInfo* leftGI )
512{
513    GLfloat w, h;
514    GLfloat tw, th;
515    GLfloat gx, gy;
516    GLfloat min_s, max_s;
517    GLfloat min_t, max_t;
518    TexGlyphInfo* tgi;
519    int xkern;
520
521    tgi = txf_glyph( txf, c );
522    if( tgi )
523    {
524        w = (GLfloat) txf->tex_width;
525        h = (GLfloat) txf->tex_height;
526
527        tw = (GLfloat) tgi->width;
528        th = (GLfloat) tgi->height;
529
530        gx = (GLfloat) tgi->x;
531        gy = (GLfloat) tgi->y;
532
533        min_s = gx / w;
534        min_t = 1.0f - (gy / h);
535        max_s = (gx + tw) / w;
536        max_t = 1.0f - ((gy + th) / h);
537
538        xkern = leftGI ? txf_kerning( txf, leftGI, tgi ) : 0;
539
540        gx = *xp + (GLfloat) (tgi->xoffset + xkern);
541        *xp += (GLfloat) (tgi->advance + xkern);
542        gy = y + (GLfloat) tgi->yoffset;
543
544        glTexCoord2f( min_s, min_t );  glVertex2f( gx, gy );
545        glTexCoord2f( max_s, min_t );  glVertex2f( gx + tw, gy );
546        glTexCoord2f( max_s, max_t );  glVertex2f( gx + tw, gy + th );
547        glTexCoord2f( min_s, max_t );  glVertex2f( gx, gy + th );
548    }
549    return tgi;
550}
551
552
553static GLfloat advanceGlyph( TexFont* txf, int c )
554{
555    TexGlyphInfo* tgi = txf_glyph( txf, c );
556    if( tgi )
557        return (GLfloat) tgi->advance;
558    return 0.0f;
559}
560
561
562/*
563   Draws GL_QUADS at current pen position.
564   The pen is moved to the end of text.
565*/
566void gxDrawText( FontResource* fnt, const char* it, const char* end )
567{
568    GLfloat x, y;
569    TexGlyphInfo* prev = 0;
570
571    if( it != end )
572    {
573        x = gEnv.state.penX;
574        y = gEnv.state.penY;
575
576        //glColor3ub( 255, 255, 255 );
577
578        glBegin( GL_QUADS );
579        do
580        {
581            if( *it == '\n' )
582            {
583                x = (GLfloat) fnt->tfont.max_ascent;
584                y -= x * 1.5f;  // Estimate of line spacing.
585                x = gEnv.state.penX;
586                prev = 0;
587            }
588            else if( *it == ' ' )
589            {
590                x += advanceGlyph( &fnt->tfont, ' ' );
591                prev = 0;
592            }
593            else
594            {
595                prev = renderGlyphXY( &fnt->tfont, *it, &x, y, prev );
596            }
597            ++it;
598        }
599        while( it != end );
600        glEnd();
601
602        gEnv.state.penX = x;
603        gEnv.state.penY = y;
604    }
605}
606
607
608void gxDrawTextValue( FontResource* fnt, OValue* val )
609{
610    if( val->type == OT_STRING )
611    {
612        OString* str = orSTRING(val);
613        gxDrawText( fnt, orStrChars(str,val), str->charArray + str->used );
614    }
615    else
616    {
617        OString tmp;
618
619        // NOTE: This will blow up (overflow) if result is longer than
620        // OR_TMP_SIZE.
621        // Need an orForm() which works directly on a memory buffer?
622        tmp.buf   = orTmp;
623        tmp.used  = 0;
624        tmp.avail = OR_TMP_SIZE;
625        orForm( &tmp, val );
626
627        gxDrawText( fnt, orTmp, orTmp + tmp.used );
628    }
629}
630
631
632//----------------------------------------------------------------------------
633
634
635#if 0
636TextureResource* gxTexture( const char* file )
637{
638}
639
640
641static void compileSprite( OValue* val )
642{
643    /*
644    if( val->type == OT_IMAGE )
645                OString* str = orSTRING(it);
646                compileSprite( (OImage*) str->buf );
647    */
648    if( val->type == OT_FILE )
649    {
650        OString* str = orSTRING(val);
651        orTermCStr( str );
652        TextureResource* tr = gxTexture( orC);
653    }
654
655    glColor3ub( 255, 255, 255 );
656
657    glBegin( GL_QUADS );
658    glEnd();
659}
660#endif
661
662
663//----------------------------------------------------------------------------
664
665
666OR_NATIVE( compileNative )
667{
668    OBlock* blk;
669    OValue* error;
670    OArray prog;
671    GLuint list;
672
673
674    blk = orBLOCK(a1);
675
676    // Load images
677
678    list = glGenLists( 1 );
679
680    orArrayInit( &prog, sizeof(RenderWord), blk->used );
681
682    error = compileProg( blk->values + a1->series.it,
683                         blk->values + blk->used, &prog );
684    if( error )
685    {
686        orError( "render-prog invalid" );
687        orSetErrorLoc( a1->index, error - blk->values );
688    }
689    else
690    {
691        glNewList( list, GL_COMPILE );
692        gxRunRenderProgram( (RenderWord*) prog.buf );
693        glEndList();
694    }
695
696    orArrayFree( &prog );
697
698    orResult( OT_INTEGER, list );
699}
700
701
702OR_NATIVE( callNative )
703{
704    if( a1->type == OT_INTEGER )
705    {
706        glCallList( orInt(a1) );
707    }
708    else
709    {
710        DrawListResource* res = gxDList( orInt(a1) );
711        if( res && res->prog )
712            gxRunRenderProgram( res->prog );
713    }
714}
715
716
717OR_NATIVE( displayNative )
718{
719    GLViewMode mode;
720
721    if( gView )
722    {
723        if( orRefineSet(a1+1) )
724        {
725            if( a1->type == OT_PAIR )
726            {
727                mode.id     = GLV_MODEID_WINDOW;
728                mode.width  = a1->pair[0];
729                mode.height = a1->pair[1];
730            }
731            else
732            {
733                orError( "display expected pair!" );
734            }
735        }
736        else
737        {
738            mode.id     = orInt(a1);
739            mode.width  = 0;
740            mode.height = 0;
741        }
742
743        glv_changeMode( gView, &mode );
744    }
745}
746
747
748static void _queryModes( const GLViewMode* mode, void* user )
749{
750    OValue* val;
751    OBlock* blk = (OBlock*) user;
752
753    val = orAppendValue( blk, OT_INTEGER, mode->id );
754    val->flags |= OR_FLAG_EOL;
755
756    val = orAppendValue( blk, OT_PAIR, mode->width );
757    val->pair[1] = mode->height;
758
759    orAppendValue( blk, OT_INTEGER, mode->refreshRate );
760    orAppendValue( blk, OT_INTEGER, mode->depth );
761}
762
763
764OR_NATIVE( displayModesNative )
765{
766    OBlock* blk;
767    (void) a1;
768
769    blk = orMakeBlock( 4 * 32 );
770    glv_queryModes( _queryModes, blk );
771    orResultBLOCK( orBlockN(blk) );
772}
773
774
775/*
776   Make sure we have a series of [word! block!] or [char! block!] pairs.
777*/
778static int validKeyHandlerBlock( OIndex blkN, OIndex blkIt )
779{
780    OBlock* blk = orBLOCKS + blkN;
781    OValue* it  = blk->values + blkIt;
782    OValue* end = blk->values + blk->used;
783
784    if( (end - it) & 1 )
785        return 0;
786
787    while( it != end )
788    {
789        if( (it->type != OT_WORD) &&
790            (it->type != OT_CHAR) )
791            return 0;
792        ++it;
793
794        if( it->type != OT_BLOCK )
795            return 0;
796        ++it;
797    }
798
799    return 1;
800}
801
802
803static int strnequ( const char* sa, const char* sb, int n )
804{
805    const char* end = sa + n;
806    while( sa != end )
807    {
808        if( *sa++ != *sb++ )
809            return 0;
810    }
811    return 1;
812}
813
814
815/*
816   Replaces atoms with event codes for faster key dispatch during
817   eventHandler.
818*/
819static void mapKeyAtoms( OBlock* blk )
820{
821    int code;
822    int ch;
823    int strLen;
824    const char* cp;
825    OValue* it  = blk->values;
826    OValue* end = blk->values + blk->used;
827
828    while( it != end )
829    {
830        code = 0;
831
832        if( it->type == OT_WORD )
833        {
834            cp = orAtomCString( orAtom(it) );
835            strLen = 0;
836            while( cp[ strLen ] )
837                ++strLen;
838            ch = (strLen == 1) ? *cp : 0;
839        }
840        else if( it->type == OT_CHAR )
841        {
842            ch = orInt(it);
843            if( ! ch )
844                ch = -1;
845        }
846
847        if( ch )
848        {
849            switch( ch )
850            {
851                case '0':  code = KEY_0; break;
852                case '1':  code = KEY_1; break;
853                case '2':  code = KEY_2; break;
854                case '3':  code = KEY_3; break;
855                case '4':  code = KEY_4; break;
856                case '5':  code = KEY_5; break;
857                case '6':  code = KEY_6; break;
858                case '7':  code = KEY_7; break;
859                case '8':  code = KEY_8; break;
860                case '9':  code = KEY_9; break;
861
862                case 'a':  code = KEY_a; break;
863                case 'b':  code = KEY_b; break;
864                case 'c':  code = KEY_c; break;
865                case 'd':  code = KEY_d; break;
866                case 'e':  code = KEY_e; break;
867                case 'f':  code = KEY_f; break;
868                case 'g':  code = KEY_g; break;
869                case 'h':  code = KEY_h; break;
870                case 'i':  code = KEY_i; break;
871                case 'j':  code = KEY_j; break;
872                case 'k':  code = KEY_k; break;
873                case 'l':  code = KEY_l; break;
874                case 'm':  code = KEY_m; break;
875                case 'n':  code = KEY_n; break;
876                case 'o':  code = KEY_o; break;
877                case 'p':  code = KEY_p; break;
878                case 'q':  code = KEY_q; break;
879                case 'r':  code = KEY_r; break;
880                case 's':  code = KEY_s; break;
881                case 't':  code = KEY_t; break;
882                case 'u':  code = KEY_u; break;
883                case 'v':  code = KEY_v; break;
884                case 'w':  code = KEY_w; break;
885                case 'x':  code = KEY_x; break;
886                case 'y':  code = KEY_y; break;
887                case 'z':  code = KEY_z; break;
888
889                case ',':  code = KEY_Comma;      break;
890                case '.':  code = KEY_Period;     break;
891                case '/':  code = KEY_Slash;      break;
892                case ';':  code = KEY_Semicolon;  break;
893                case '\'': code = KEY_Apostrophe; break;
894                case '[':  code = KEY_Bracket_L;  break;
895                case ']':  code = KEY_Bracket_R;  break;
896                case '-':  code = KEY_Minus;      break;
897                case '=':  code = KEY_Equal;      break;
898                case '\\': code = KEY_Backslash;  break;
899                case '`':  code = KEY_Grave;      break;
900                case '~':  code = KEY_Grave;      break;
901            }
902        }
903        else if( strLen == 2 )
904        {
905                 if( strnequ( cp, "up", 2 ) ) code = KEY_Up;
906            else if( strnequ( cp, "f1", 2 ) ) code = KEY_F1;
907            else if( strnequ( cp, "f2", 2 ) ) code = KEY_F2;
908            else if( strnequ( cp, "f3", 2 ) ) code = KEY_F3;
909            else if( strnequ( cp, "f4", 2 ) ) code = KEY_F4;
910            else if( strnequ( cp, "f5", 2 ) ) code = KEY_F5;
911            else if( strnequ( cp, "f6", 2 ) ) code = KEY_F6;
912            else if( strnequ( cp, "f7", 2 ) ) code = KEY_F7;
913            else if( strnequ( cp, "f8", 2 ) ) code = KEY_F8;
914            else if( strnequ( cp, "f9", 2 ) ) code = KEY_F9;
915        }
916        else if( strLen == 3 )
917        {
918                 if( strnequ( cp, "spc", 3 ) ) code = KEY_Space;
919            else if( strnequ( cp, "esc", 3 ) ) code = KEY_Escape;
920            else if( strnequ( cp, "tab", 3 ) ) code = KEY_Tab;
921            else if( strnequ( cp, "end", 3 ) ) code = KEY_End;
922            else if( strnequ( cp, "del", 3 ) ) code = KEY_Delete;
923            else if( strnequ( cp, "f10", 3 ) ) code = KEY_F10;
924            else if( strnequ( cp, "f11", 3 ) ) code = KEY_F11;
925            else if( strnequ( cp, "f12", 3 ) ) code = KEY_F12;
926        }
927        else
928        {
929                 if( strnequ( cp, "down",     4 ) ) code = KEY_Down;
930            else if( strnequ( cp, "back",     4 ) ) code = KEY_Back_Space;
931            else if( strnequ( cp, "left",     4 ) ) code = KEY_Left;
932            else if( strnequ( cp, "right",    5 ) ) code = KEY_Right;
933            else if( strnequ( cp, "home",     4 ) ) code = KEY_Home;
934            else if( strnequ( cp, "return",   6 ) ) code = KEY_Return;
935            else if( strnequ( cp, "insert",   6 ) ) code = KEY_Insert;
936            else if( strnequ( cp, "pg-up",    5 ) ) code = KEY_Page_Up;
937            else if( strnequ( cp, "pg-down",  7 ) ) code = KEY_Page_Down;
938            else if( strnequ( cp, "num-lock", 8 ) ) code = KEY_Num_Lock;
939            else if( strnequ( cp, "print",    5 ) ) code = KEY_Print;
940            else if( strnequ( cp, "pause",    5 ) ) code = KEY_Pause;
941
942            else if( strnequ( cp, "kp-8",     4 ) ) code = KEY_KP_Up;
943            else if( strnequ( cp, "kp-5",     4 ) ) code = KEY_KP_Begin;
944            else if( strnequ( cp, "kp-4",     4 ) ) code = KEY_KP_Left;
945            else if( strnequ( cp, "kp-6",     4 ) ) code = KEY_KP_Right;
946            else if( strnequ( cp, "kp-7",     4 ) ) code = KEY_KP_Home;
947            else if( strnequ( cp, "kp-2",     4 ) ) code = KEY_KP_Down;
948            else if( strnequ( cp, "kp-9",     4 ) ) code = KEY_KP_Page_Up;
949            else if( strnequ( cp, "kp-3",     4 ) ) code = KEY_KP_Page_Down;
950            else if( strnequ( cp, "kp-1",     4 ) ) code = KEY_KP_End;
951            else if( strnequ( cp, "kp-0",     4 ) ) code = KEY_KP_Insert;
952            else if( strnequ( cp, "kp-ins",   6 ) ) code = KEY_KP_Insert;
953            else if( strnequ( cp, "kp-del",   6 ) ) code = KEY_KP_Delete;
954            else if( strnequ( cp, "kp-enter", 8 ) ) code = KEY_KP_Enter;
955            else if( strnequ( cp, "kp-div",   6 ) ) code = KEY_KP_Divide;
956            else if( strnequ( cp, "kp-mul",   6 ) ) code = KEY_KP_Multiply;
957            else if( strnequ( cp, "kp-add",   6 ) ) code = KEY_KP_Add;
958            else if( strnequ( cp, "kp-sub",   6 ) ) code = KEY_KP_Subtract;
959            /*
960            else if( strnequ( cp, "kp-sub",   6 ) ) code = KEY_KP_Separator;
961            else if( strnequ( cp, "kp-enter", 6 ) ) code = KEY_KP_Decimal;
962            else if( strnequ( cp, "kp-enter", 6 ) ) code = KEY_KP_Equal;
963            */
964        }
965
966        orSetTF( it, OT_PAIR );
967        it->pair[0] = code;
968        it->pair[1] = 0;
969
970        it += 2;
971    }
972}
973
974
975struct ExecState
976{
977    OIndex renderBlkN;
978    OIndex widgetHold;
979};
980
981
982/*
983   Setup ExecState & gEnv to use widget as event handler.
984   Returns zero if error thrown.
985*/
986static int prepWidgetHandler( OContext* ctx, struct ExecState* es )
987{
988    OBlock* vblk;
989    OValue* val;
990
991    vblk = orBlockPtr( ctx->vblkN );
992
993    if( vblk->used >= WV_COUNT )
994    {
995        gEnv.widgetCtxN = ctx->vblkN;
996
997        es->widgetHold = orHold( OT_BLOCK, gEnv.widgetCtxN );
998
999        val = vblk->values + WV_RENDER;
1000        if( val->type == OT_BLOCK )
1001            es->renderBlkN = val->index;
1002    }
1003    return 1;
1004}
1005
1006
1007#define colorU8ToF(n)    (((GLfloat) n) / 255.0f)
1008
1009OR_NATIVE( clearColorNative )
1010{
1011    switch( a1->type )
1012    {
1013        case OT_TUPLE:
1014        {
1015            GLclampf col[4];
1016
1017            col[0] = colorU8ToF( a1->tuple[0] );
1018            col[1] = colorU8ToF( a1->tuple[1] );
1019            col[2] = colorU8ToF( a1->tuple[2] );
1020            col[3] = (a1->argc > 3) ? colorU8ToF( a1->tuple[3] ) : 0.0f;
1021
1022            glClearColor( col[0], col[1], col[2], col[3] );
1023
1024            gEnv.state.clear = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
1025        }
1026            break;
1027
1028        default:
1029            gEnv.state.clear = GL_DEPTH_BUFFER_BIT;
1030            break;
1031    }
1032}
1033
1034
1035#define RELEASE(h)  if( orHoldIsValid(h) ) orRelease(h)
1036
1037OR_NATIVE( execNative )
1038{
1039#ifdef PERF_CLOCK
1040    clock_t t1, t1e;
1041#endif
1042    struct ExecState es;
1043
1044    es.renderBlkN = 0;
1045    es.widgetHold = OR_INVALID_HOLD;
1046
1047    gEnv.widgetCtxN = 0;
1048
1049    if( a1->type == OT_OBJECT )
1050    {
1051        if( ! prepWidgetHandler( &a1->ctx, &es ) )
1052            return;
1053    }
1054
1055    gEnv.running = 1;
1056    while( 1 )
1057    {
1058        glDepthMask( GL_TRUE );
1059        //glClear( GL_DEPTH_BUFFER_BIT );
1060        glClear( gEnv.state.clear );
1061
1062#ifdef PERF_CLOCK
1063        t1 = clock();
1064#endif
1065        glv_handleEvents( gView );
1066        if( ! gEnv.running )
1067            break;
1068
1069        if( es.renderBlkN )
1070        {
1071            orEvalBlock( orBLOCKS + es.renderBlkN, 0 );
1072            if( gxHandleError() )
1073                break;     // Don't change display if error occurs.
1074        }
1075
1076#ifdef PERF_CLOCK
1077        t1e = clock() - t1;
1078        printf( "clock %g\n",
1079                ((double) t1e) / CLOCKS_PER_SEC );
1080        //printf( "clock %ld\n", t1e );
1081#endif
1082
1083        glv_swapBuffers( gView );
1084    }
1085
1086    RELEASE(