From 971901e453c1678c1201f8c3e890c88c46c8b377 Mon Sep 17 00:00:00 2001 From: Christian Kroll Date: Wed, 17 Oct 2012 08:29:41 +0000 Subject: [PATCH] Rewrote API simulator from scratch for Win32 since Cygwin ceased support for Win32's native OpenGL libraries. --- defaults.mk | 12 +- simulator/Makefile | 7 +- simulator/i386pe.x | 53 +++-- simulator/joystick.c | 9 - simulator/joystick.h | 15 -- simulator/main.c | 403 ++++++++++++++++++++-------------- simulator/trackball.c | 371 ++++++++++++++++++------------- simulator/trackball.h | 156 +++++++------- simulator/util.c | 26 --- simulator/winmain.c | 492 ++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 1080 insertions(+), 464 deletions(-) delete mode 100644 simulator/joystick.c delete mode 100644 simulator/joystick.h delete mode 100644 simulator/util.c create mode 100644 simulator/winmain.c diff --git a/defaults.mk b/defaults.mk index 7b47988..3f9940d 100644 --- a/defaults.mk +++ b/defaults.mk @@ -21,7 +21,7 @@ LIBS = -lm # flags for the compiler CFLAGS ?= -Wall -W -Wno-unused-parameter -Wno-sign-compare -CFLAGS += -g -Os -std=gnu99 -fgnu89-inline -D_XOPEN_SOURCE=600 -DNDEBUG +CFLAGS += -g -Os -std=gnu99 -mstrict-X -fgnu89-inline -D_XOPEN_SOURCE=600 -DNDEBUG # flags for the linker LDFLAGS += -T ./avr5.x -Wl,-Map,image.map -mmcu=$(MCU) @@ -35,13 +35,13 @@ MACHINE = $(shell uname -m) #$(info $(OSTYPE)) ifeq ($(OSTYPE),cygwin) - CFLAGS_SIM = -g -Wall -pedantic -std=c99 -O0 -D_WIN32 -mno-cygwin -D_XOPEN_SOURCE=600 - LDFLAGS_SIM = -Wl -mno-cygwin -T simulator/i386pe.x - LIBS_SIM = -lglut32 -lglu32 -lopengl32 -lm + CFLAGS_SIM = -g -Wall -pedantic -std=c99 -O0 -D_WIN32 -D_XOPEN_SOURCE=600 + LDFLAGS_SIM = -Wl -T simulator/i386pe.x + LIBS_SIM = -lgdi32 -lwinmm -lm else - CFLAGS_SIM = -g -Wall -pedantic -std=c99 -O0 -D_XOPEN_SOURCE=600 + CFLAGS_SIM = -g -g -Wall -pedantic -std=c99 -O0 -D_XOPEN_SOURCE=600 ifeq ($(MACHINE),x86_64) - LDFLAGS_SIM = -Wl -T simulator/elf_x86_64.x + LDFLAGS_SIM = -g -Wl -T simulator/elf_x86_64.x else LDFLAGS_SIM = -Wl -T simulator/elf_i386.x endif diff --git a/simulator/Makefile b/simulator/Makefile index b9f40f4..5a59bf7 100644 --- a/simulator/Makefile +++ b/simulator/Makefile @@ -3,7 +3,10 @@ TOPDIR = .. include $(TOPDIR)/defaults.mk -SRC_SIM = main.c trackball.c util.c eeprom.c - +ifeq ($(OSTYPE),cygwin) + SRC_SIM = winmain.c trackball.c eeprom.c +else + SRC_SIM = main.c trackball.c eeprom.c +endif include $(TOPDIR)/rules.mk diff --git a/simulator/i386pe.x b/simulator/i386pe.x index bd4ed97..6ea1c29 100644 --- a/simulator/i386pe.x +++ b/simulator/i386pe.x @@ -1,6 +1,6 @@ /* Default linker script, for normal executables */ OUTPUT_FORMAT(pei-i386) -SEARCH_DIR("/usr/i686-pc-cygwin/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api"); +SEARCH_DIR("/usr/i686-cygwin/lib"); SEARCH_DIR("/usr/lib"); SEARCH_DIR("/usr/lib/w32api"); SECTIONS { /* Make the virtual address and file offset synced if the alignment is @@ -12,6 +12,8 @@ SECTIONS *(.init) *(.text) *(SORT(.text$*)) + *(.text.*) + *(.gnu.linkonce.t.*) *(.glue_7t) *(.glue_7) ___CTOR_LIST__ = .; __CTOR_LIST__ = . ; @@ -28,7 +30,7 @@ SECTIONS on fork. This used to be named ".data". The linker used to include this between __data_start__ and __data_end__, but that breaks building the cygwin32 dll. Instead, we name the section - ".data_cygwin_nocopy" and explictly include it after __data_end__. */ + ".data_cygwin_nocopy" and explicitly include it after __data_end__. */ .data BLOCK(__section_alignment__) : { __data_start__ = . ; @@ -36,24 +38,30 @@ SECTIONS *(.data2) *(SORT(.data$*)) *(.jcr) - __eeprom_start__ = . ; + __eeprom_start__ = . ; *(.eeprom) - __game_descriptors_start__ = . ; - *(.game_descriptors) - __game_descriptors_end__ = . ; - __data_end__ = . ; + __game_descriptors_start__ = . ; + *(.game_descriptors) + __game_descriptors_end__ = . ; + __data_end__ = . ; *(.data_cygwin_nocopy) } .rdata BLOCK(__section_alignment__) : { *(.rdata) *(SORT(.rdata$*)) - *(.eh_frame) - ___RUNTIME_PSEUDO_RELOC_LIST__ = .; - __RUNTIME_PSEUDO_RELOC_LIST__ = .; + __rt_psrelocs_start = .; *(.rdata_runtime_pseudo_reloc) - ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .; - __RUNTIME_PSEUDO_RELOC_LIST_END__ = .; + __rt_psrelocs_end = .; + } + __rt_psrelocs_size = __rt_psrelocs_end - __rt_psrelocs_start; + ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .; + __RUNTIME_PSEUDO_RELOC_LIST_END__ = .; + ___RUNTIME_PSEUDO_RELOC_LIST__ = . - __rt_psrelocs_size; + __RUNTIME_PSEUDO_RELOC_LIST__ = . - __rt_psrelocs_size; + .eh_frame BLOCK(__section_alignment__) : + { + *(.eh_frame*) } .pdata BLOCK(__section_alignment__) : { @@ -76,6 +84,8 @@ SECTIONS *(.debug$T) *(.debug$F) *(.drectve) + *(.note.GNU-stack) + *(.gnu.lto_*) } .idata BLOCK(__section_alignment__) : { @@ -86,7 +96,9 @@ SECTIONS /* These zeroes mark the end of the import list. */ LONG (0); LONG (0); LONG (0); LONG (0); LONG (0); SORT(*)(.idata$4) + __IAT_start__ = .; SORT(*)(.idata$5) + __IAT_end__ = .; SORT(*)(.idata$6) SORT(*)(.idata$7) } @@ -153,10 +165,14 @@ SECTIONS { *(.debug_pubnames) } + .debug_pubtypes BLOCK(__section_alignment__) (NOLOAD) : + { + *(.debug_pubtypes) + } /* DWARF 2. */ .debug_info BLOCK(__section_alignment__) (NOLOAD) : { - *(.debug_info) *(.gnu.linkonce.wi.*) + *(.debug_info .gnu.linkonce.wi.*) } .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) : { @@ -168,7 +184,7 @@ SECTIONS } .debug_frame BLOCK(__section_alignment__) (NOLOAD) : { - *(.debug_frame) + *(.debug_frame*) } .debug_str BLOCK(__section_alignment__) (NOLOAD) : { @@ -199,9 +215,18 @@ SECTIONS { *(.debug_varnames) } + .debug_macro BLOCK(__section_alignment__) (NOLOAD) : + { + *(.debug_macro) + } /* DWARF 3. */ .debug_ranges BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_ranges) } + /* DWARF 4. */ + .debug_types BLOCK(__section_alignment__) (NOLOAD) : + { + *(.debug_types .gnu.linkonce.wt.*) + } } diff --git a/simulator/joystick.c b/simulator/joystick.c deleted file mode 100644 index 8826f64..0000000 --- a/simulator/joystick.c +++ /dev/null @@ -1,9 +0,0 @@ -#include "joystick.h" - -unsigned char fakeport; - - -// fake function since our keybord doesn't need any initialisation -void joy_init() -{ -} diff --git a/simulator/joystick.h b/simulator/joystick.h deleted file mode 100644 index 6bbb525..0000000 --- a/simulator/joystick.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef JOYSTICK_H_ -#define JOYSTICK_H_ - -extern unsigned char fakeport; - -#define JOYISFIRE (0x01 & fakeport) -#define JOYISLEFT (0x02 & fakeport) -#define JOYISRIGHT (0x04 & fakeport) -#define JOYISDOWN (0x08 & fakeport) -#define JOYISUP (0x10 & fakeport) - -unsigned char waitForFire; - - -#endif /*JOYSTICK_H_*/ diff --git a/simulator/main.c b/simulator/main.c index 6f8f87d..12f63d8 100644 --- a/simulator/main.c +++ b/simulator/main.c @@ -1,46 +1,82 @@ -#ifdef _WIN32 - #include - #include - #include -#define pthread_t int +/** + * \defgroup unixsimulator Simulation of the Borg API for UNIX like platforms. + */ +/*@{*/ + +/** + * @file main.c + * @brief Simulator for Unix like platforms. + * @author Martin Ongsiek, Peter Fuhrmann, Christian Kroll + */ + +#ifdef OSX_ + #include #else - #ifdef OSX_ - #include - #else - #include - #endif - #include // for threads in linux - #include - #include - #include - #include + #include #endif +#include +#include +#include +#include +#include #include #include #include "../config.h" #include "../display_loop.h" -#include "../pixel.h" #include "trackball.h" -unsigned char fakeport; -volatile unsigned char oldMode, oldOldmode, mode; -extern unsigned char waitForFire; +/** Number of bytes per row. */ +#define LINEBYTES ((NUM_COLS + 1) / 8) +/** Fake port for simulating joystick input. */ +volatile unsigned char fakeport; +/** Flag which indicates if wait should jump to the menu if fire is pressed. */ +volatile unsigned char waitForFire; +/** The simulated frame buffer of the borg. */ +volatile unsigned char pixmap[NUMPLANE][NUM_ROWS][LINEBYTES]; +/** Jump buffer which leads directly the menu. */ +extern jmp_buf newmode_jmpbuf; -int WindWidth, WindHeight; +/** Width of the window. */ +int WindWidth; +/** Height of the window. */ +int WindHeight; -unsigned char pixmap[NUMPLANE][NUM_ROWS][LINEBYTES]; -unsigned char joystick; +/** Rotation of the x-axis of the scene. */ +float view_rotx = 0; +/** Rotation of the y-axis of the scene. */ +float view_roty = 0; +/** Rotation of the z-axis of the scene. */ +float view_rotz = 0; -float view_rotx = 0, view_roty = 0, view_rotz = 0; +/** GLUT window handle. */ int win; -pthread_t simthread; -GLUquadric* quad; +/** + * Simple wait function. + * @param ms The requested delay in milliseconds. + */ +void wait(unsigned int ms) { + if (waitForFire) { + if (fakeport & 0x01) { + longjmp(newmode_jmpbuf, 43); + } + } + usleep(ms * 1000); +} + + +/** + * Draw a LED in the given color (which is a list). + * @param color List which contains a sphere. + * @param pos_x x-coordinate + * @param pos_y y-coordinate + * @param pos_z z-coordinate + */ void drawLED(int color, float pos_x, float pos_y, float pos_z) { glPushMatrix(); glTranslatef(pos_x, pos_y, pos_z); @@ -48,175 +84,216 @@ void drawLED(int color, float pos_x, float pos_y, float pos_z) { glPopMatrix(); } -void display(void){ - int x, y, z, level, color; - tbReshape(WindWidth, WindHeight); - glClear(GL_COLOR_BUFFER_BIT); - glPushMatrix(); - glTranslatef(NUM_COLS*2., 0., NUM_ROWS*2.); + +/** + * Draws the LED matrix. + */ +void display(void) { + int x, y, z, level, color; + tbReshape(WindWidth, WindHeight); + glClear(GL_COLOR_BUFFER_BIT); + glPushMatrix(); + glTranslatef(NUM_COLS * 2., 0., NUM_ROWS * 2.); tbMatrix(); - glRotatef(view_rotx, 1.0, 0.0, 0.0); - glRotatef(view_roty, 0.0, 1.0, 0.0); + glRotatef(view_rotx, 1.0, 0.0, 0.0); + glRotatef(view_roty, 0.0, 1.0, 0.0); glRotatef(view_rotz, 0.0, 0.0, 1.0); - glTranslatef(-NUM_COLS*2, 0., -NUM_ROWS*2.); - for (x = 0; x < 1; x++) { + glTranslatef(-NUM_COLS * 2, 0., -NUM_ROWS * 2.); + for (x = 0; x < 1; x++) { for (y = 0; y < NUM_COLS; y++) { for (z = 0; z < NUM_ROWS; z++) { color = 0; for (level = 0; level < NUMPLANE; level++) { - if (pixmap[level][z%NUM_ROWS][y/8] & (1 << y % 8)) { - color = level+1; + if (pixmap[level][z % NUM_ROWS][y / 8] & (1 << y % 8)) { + color = level + 1; } } - drawLED(color, (float)y*4.0, - (float)x*4.0, - (float)(NUM_ROWS-1-z)*4.0); + drawLED(color, (float) y * 4.0, (float) x * 4.0, + (float) (NUM_ROWS - 1 - z) * 4.0); } } - } + } glPopMatrix(); glutSwapBuffers(); -#ifdef _WIN32 - Sleep(10); -#else + usleep(20000); -#endif - joystick = 255; } -void keyboard(unsigned char key, int x, int y){ + +/** + * Handler for processing key presses. + * @param key The pressed key encoded in ASCII. + * @param x X-position of the mouse pointer. + * @param y Y-position of the mouse pointer. + */ +void keyboard(unsigned char key, int x, int y) { switch (key) { - case 'q': printf("Quit\n"); - glutDestroyWindow(win); - exit(0); - break; - case ' ': - fakeport |= 0x01; - break; - case 'a': - fakeport |= 0x02; - break; - case 'd': - fakeport |= 0x04; - break; - case 's': - fakeport |= 0x08; - break; - case 'w': - fakeport |= 0x10; - break; + case 'q': + printf("Quit\n"); + glutDestroyWindow(win); + exit(0); + break; + case ' ': + fakeport |= 0x01; + break; + case 'a': + fakeport |= 0x02; + break; + case 'd': + fakeport |= 0x04; + break; + case 's': + fakeport |= 0x08; + break; + case 'w': + fakeport |= 0x10; + break; } } -void keyboardup(unsigned char key, int x, int y){ + +/** + * Handler for processing key releases. + * @param key The released key encoded in ASCII. + * @param x X-position of the mouse pointer. + * @param y Y-position of the mouse pointer. + */ +void keyboardup(unsigned char key, int x, int y) { switch (key) { - case ' ': - fakeport &= ~0x01; - break; - case 'a': - fakeport &= ~0x02; - break; - case 'd': - fakeport &= ~0x04; - break; - case 's': - fakeport &= ~0x08; - break; - case 'w': - fakeport &= ~0x10; - break; + case ' ': + fakeport &= ~0x01; + break; + case 'a': + fakeport &= ~0x02; + break; + case 'd': + fakeport &= ~0x04; + break; + case 's': + fakeport &= ~0x08; + break; + case 'w': + fakeport &= ~0x10; + break; } } -void mouse(int button, int state, int x, int y) -{ - tbMouse(button, state, x, y); + +/** + * Relays mouse position and button state to the trackball implementation. + * @param button Currently monitored button. + * @param state State of that button. + * @param x X-position of the mouse pointer. + * @param y Y-position of the mouse pointer. + */ +void mouse(int button, int state, int x, int y) { + tbMouse(button, state, x, y); } -void motion(int x, int y) -{ - tbMotion(x, y); + +/** + * Relays motion request to the trackball implementation. + * @param x X-position for the motion direction. + * @param y Y-position for the motion direction. + */ +void motion(int x, int y) { + tbMotion(x, y); } -void reshape(int width, int height) -{ +/** + * Updating the window size. + * @param width Width of the window. + * @param height Height of the window. + */ +void reshape(int width, int height) { - tbReshape(width, height); + tbReshape(width, height); - glViewport(0, 0, width, height); + glViewport(0, 0, width, height); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(60.0, (float)WindWidth/(float)WindWidth, 5., 1000.); - gluLookAt(NUM_ROWS*2., NUM_ROWS*2.+50., NUM_COLS*2., - NUM_ROWS*2., NUM_ROWS*2., NUM_COLS*2., - 0.0, 0.0, 1.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60.0, (float) WindWidth / (float) WindWidth, 5., 1000.); + gluLookAt(NUM_ROWS * 2., NUM_ROWS * 2. + 50., NUM_COLS * 2., NUM_ROWS * 2., + NUM_ROWS * 2., NUM_COLS * 2., 0.0, 0.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); - WindWidth = width; - WindHeight = height; + WindWidth = width; + WindHeight = height; } -/* change view angle */ + +/** + * Handler for special keys (the arrow keys in particular) for adjusting the + * view angle of the scene. + * @param k The pressed special key using GLUT's nomenclature. + * @param x X-position of the mouse pointer. + * @param y Y-position of the mouse pointer. + */ static void special(int k, int x, int y) { - switch (k) { - case GLUT_KEY_UP: - view_rotx += 5.0; - break; - case GLUT_KEY_DOWN: - view_rotx -= 5.0; - break; - case GLUT_KEY_LEFT: - view_rotz += 5.0; - break; - case GLUT_KEY_RIGHT: - view_rotz -= 5.0; - break; - default: - return; - } - glutPostRedisplay(); + switch (k) { + case GLUT_KEY_UP: + view_rotx += 5.0; + break; + case GLUT_KEY_DOWN: + view_rotx -= 5.0; + break; + case GLUT_KEY_LEFT: + view_rotz += 5.0; + break; + case GLUT_KEY_RIGHT: + view_rotz -= 5.0; + break; + default: + return; + } + glutPostRedisplay(); } -/* -void timf(int value) { - glutPostRedisplay(); - glutTimerFunc(1, timf, 0); -}*/ + +/** + * Entry point for starting the display loop thread. + * @param unused Not used. Only here to satisfy signature constraints. + */ void *display_loop_run(void * unused) { display_loop(); return 0; } -int main(int argc, char **argv){ - WindHeight = 700; - WindWidth = 700; - glutInit(&argc,argv); - glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); - glutInitWindowSize(WindHeight, WindWidth); - win = glutCreateWindow("16x16 Borg Simulator"); - // callback - //glutReshapeFunc(reshape); - glutDisplayFunc(display); - glutIdleFunc(display); - glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF); - glutKeyboardFunc(keyboard); - glutKeyboardUpFunc(keyboardup); - glutSpecialFunc(special); - glutMouseFunc(mouse); - glutMotionFunc(motion); +/** + * Main function for the simulator. + * @param argc The argument count. + * @param argv Command line arguments. + * @return Exit codem, always zero. + */ +int main(int argc, char **argv) { + WindHeight = 700; + WindWidth = 700; + glutInit(&argc, argv); + glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); + glutInitWindowSize(WindHeight, WindWidth); + win = glutCreateWindow("16x16 Borg Simulator"); - // clearcolor & main loop - glClearColor(0,0,0,1.0); - gluPerspective(60.0, (float)WindWidth/(float)WindWidth, 5., 1000.); - gluLookAt(NUM_COLS*2., NUM_COLS*2.+50., NUM_ROWS*2., - NUM_COLS*2., NUM_COLS*2., NUM_ROWS*2., - 0.0, 0.0, 1.0); + // callback + glutDisplayFunc(display); + glutIdleFunc(display); + glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF); + glutKeyboardFunc(keyboard); + glutKeyboardUpFunc(keyboardup); + glutSpecialFunc(special); + glutMouseFunc(mouse); + glutMotionFunc(motion); + + // clearcolor & main loop + glClearColor(0, 0, 0, 1.0); + gluPerspective(60.0, (float) WindWidth / (float) WindWidth, 5., 1000.); + gluLookAt(NUM_COLS * 2., NUM_COLS * 2. + 50., NUM_ROWS * 2., NUM_COLS * 2., + NUM_COLS * 2., NUM_ROWS * 2., 0.0, 0.0, 1.0); // init Call List for LED - quad = gluNewQuadric(); + GLUquadric* quad = gluNewQuadric(); glNewList(0, GL_COMPILE); glColor4f(0.8, 0.0, 0.0, 1.); gluSphere(quad, 1.0, 12, 12); @@ -224,26 +301,24 @@ int main(int argc, char **argv){ glNewList(1, GL_COMPILE); glColor4f(0.5, 0.0, 0.0, 1.); gluSphere(quad, 1.4, 12, 12); - glEndList(); + glEndList(); glNewList(2, GL_COMPILE); glColor4f(0.7, 0.0, 0.0, 1.); gluSphere(quad, 1.55, 12, 12); - glEndList(); + glEndList(); glNewList(3, GL_COMPILE); glColor4f(1.00, 0.0, 0.0, 1.); - gluSphere(quad, 1.7, 12, 12); - glEndList(); + gluSphere(quad, 1.7, 12, 12); + glEndList(); - tbInit(GLUT_LEFT_BUTTON); - tbAnimate(GL_FALSE); + tbInit(GLUT_LEFT_BUTTON); + tbAnimate(GL_FALSE); - // start display_loop thread -#ifdef _WIN32 - _beginthread((void (*)(void*))display_loop_run, 0, NULL); -#else - pthread_create(&simthread, NULL, display_loop_run, NULL); -#endif - //glutTimerFunc(40, timf, 0); // Set up timer for 40ms, about 25 fps - glutMainLoop(); - return 0; + pthread_t simthread; + pthread_create(&simthread, NULL, display_loop_run, NULL); + + glutMainLoop(); + return 0; } + +/*@}*/ diff --git a/simulator/trackball.c b/simulator/trackball.c index 500b2d5..edbad02 100644 --- a/simulator/trackball.c +++ b/simulator/trackball.c @@ -1,4 +1,9 @@ -/* +/** + * \addtogroup unixsimulator + */ +/*@{*/ + +/** * Simple trackball-like motion adapted (ripped off) from projtex.c * (written by David Yu and David Blythe). See the SIGGRAPH '96 * Advanced OpenGL course notes. @@ -20,67 +25,68 @@ * Typical setup: * * - void - init(void) - { - tbInit(GLUT_MIDDLE_BUTTON); - tbAnimate(GL_TRUE); - . . . - } - - void - reshape(int width, int height) - { - tbReshape(width, height); - . . . - } - - void - display(void) - { - glPushMatrix(); - - tbMatrix(); - . . . draw the scene . . . - - glPopMatrix(); - } - - void - mouse(int button, int state, int x, int y) - { - tbMouse(button, state, x, y); - . . . - } - - void - motion(int x, int y) - { - tbMotion(x, y); - . . . - } - - int - main(int argc, char** argv) - { - . . . - init(); - glutReshapeFunc(reshape); - glutDisplayFunc(display); - glutMouseFunc(mouse); - glutMotionFunc(motion); - . . . - } + * void + * init(void) + * { + * tbInit(GLUT_MIDDLE_BUTTON); + * tbAnimate(GL_TRUE); + * . . . + * } * - * */ + * void + * reshape(int width, int height) + * { + * tbReshape(width, height); + * . . . + * } + * + * void + * display(void) + * { + * glPushMatrix(); + * + * tbMatrix(); + * . . . draw the scene . . . + * + * glPopMatrix(); + * } + * + * void + * mouse(int button, int state, int x, int y) + * { + * tbMouse(button, state, x, y); + * . . . + * } + * + * void + * motion(int x, int y) + * { + * tbMotion(x, y); + * . . . + * } + * + * int + * main(int argc, char** argv) + * { + * . . . + * init(); + * glutReshapeFunc(reshape); + * glutDisplayFunc(display); + * glutMouseFunc(mouse); + * glutMotionFunc(motion); + * . . . + * } + * + * @file trackball.c + * @brief Helper functions for the UNIX platform Borg simulator. + * @author Martin Ongsiek, David Yu, David Blythe + */ /* includes */ #include #include #ifdef OSX_ # include -#elif _WIN32 -# include #else # include #endif @@ -104,131 +110,186 @@ static GLboolean tb_animate = GL_TRUE; /* functions */ -static void _tbPointToVector(int x, int y, int width, int height, float v[3]) -{ - float d, a; - /* project x, y onto a hemi-sphere centered within width, height. */ - v[0] = (2.0 * x - width) / width; - v[1] = (height - 2.0 * y) / height; - d = sqrt(v[0] * v[0] + v[1] * v[1]); - v[2] = cos((3.14159265 / 2.0) * ((d < 1.0) ? d : 1.0)); - a = 1.0 / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - v[0] *= a; - v[1] *= a; - v[2] *= a; +/** + * Project x and y onto a hemisphere centered within given width and height. + * @param x X-coordinate + * @param y Y-coordinate + * @param width Width of the hemisphere. + * @param height Width of the hemisphere. + * @param v Vector where the projection is performed on. + */ +static void _tbPointToVector(int x, int y, int width, int height, float v[3]) { + float d, a; + + /* project x, y onto a hemisphere centered within width, height. */ + v[0] = (2.0 * x - width) / width; + v[1] = (height - 2.0 * y) / height; + d = sqrt(v[0] * v[0] + v[1] * v[1]); + v[2] = cos((3.14159265 / 2.0) * ((d < 1.0) ? d : 1.0)); + a = 1.0 / sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + v[0] *= a; + v[1] *= a; + v[2] *= a; } -static void _tbAnimate(void) -{ - glutPostRedisplay(); + +/** + * Redisplay current window contents. + */ +static void _tbAnimate(void) { + glutPostRedisplay(); } -void _tbStartMotion(int x, int y, int button, int time) -{ - assert(tb_button != -1); - tb_tracking = GL_TRUE; - tb_lasttime = time; - _tbPointToVector(x, y, tb_width, tb_height, tb_lastposition); +/** + * Starts trackball movement depending on the mouse position. + * @param x X-position of the mouse pointer. + * @param y Y-position of the mouse pointer. + * @param button Not used. + * @param time Elapsed time. + */ +void _tbStartMotion(int x, int y, int button, int time) { + assert(tb_button != -1); + + tb_tracking = GL_TRUE; + tb_lasttime = time; + _tbPointToVector(x, y, tb_width, tb_height, tb_lastposition); } -void _tbStopMotion(int button, unsigned time) -{ - assert(tb_button != -1); - tb_tracking = GL_FALSE; +/** + * Stops trackball movement. + * @param button Not used + * @param time Not used. + */ +void _tbStopMotion(int button, unsigned time) { + assert(tb_button != -1); - if (time == tb_lasttime && tb_animate) { - glutIdleFunc(_tbAnimate); - } else { - tb_angle = 0.0; - if (tb_animate) - glutIdleFunc(0); - } + tb_tracking = GL_FALSE; + + if (time == tb_lasttime && tb_animate) { + glutIdleFunc(_tbAnimate); + } else { + tb_angle = 0.0; + if (tb_animate) { + glutIdleFunc(0); + } + } } -void tbAnimate(GLboolean animate) -{ - tb_animate = animate; + + +/** + * Starts or stops the spinning movement of the trackball. + * @param animate GL_TRUE for starting and GL_FALSE for stopping the animation. + */ +void tbAnimate(GLboolean animate) { + tb_animate = animate; } -void tbInit(GLuint button) -{ - tb_button = button; - tb_angle = 0.0; - /* put the identity in the trackball transform */ - glPushMatrix(); - glLoadIdentity(); - glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)tb_transform); - glPopMatrix(); +/** + * Has to be called before any other tb call. + * @param button Mouse button state. + */ +void tbInit(GLuint button) { + tb_button = button; + tb_angle = 0.0; + + /* put the identity in the trackball transform */ + glPushMatrix(); + glLoadIdentity(); + glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *) tb_transform); + glPopMatrix(); } -void tbMatrix() -{ - assert(tb_button != -1); - glPushMatrix(); - glLoadIdentity(); - glRotatef(tb_angle, -tb_axis[0], tb_axis[2], tb_axis[1]); - glMultMatrixf((GLfloat *)tb_transform); - glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *)tb_transform); - glPopMatrix(); +/** + * Gets the tb matrix rotation. + */ +void tbMatrix() { + assert(tb_button != -1); - glMultMatrixf((GLfloat *)tb_transform); - } + glPushMatrix(); + glLoadIdentity(); + glRotatef(tb_angle, -tb_axis[0], tb_axis[2], tb_axis[1]); + glMultMatrixf((GLfloat *) tb_transform); + glGetFloatv(GL_MODELVIEW_MATRIX, (GLfloat *) tb_transform); + glPopMatrix(); -void tbReshape(int width, int height) -{ - assert(tb_button != -1); - - tb_width = width; - tb_height = height; + glMultMatrixf((GLfloat *) tb_transform); } -void tbMouse(int button, int state, int x, int y) -{ - assert(tb_button != -1); - if (state == GLUT_DOWN && button == tb_button) - _tbStartMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME)); - else if (state == GLUT_UP && button == tb_button) - _tbStopMotion(button, glutGet(GLUT_ELAPSED_TIME)); +/** + * Reshape callback function for determining the window size. + * @param width Width of the trackball. + * @param height Height of the trackball. + */ +void tbReshape(int width, int height) { + assert(tb_button != -1); + + tb_width = width; + tb_height = height; } -void tbMotion(int x, int y) -{ - GLfloat current_position[3], dx, dy, dz; - assert(tb_button != -1); +/** + * Starts motion depending on mouse position and button state. + * @param button The button whose state has changed. + * @param state The state of that button. + * @param x X-position of the mouse pointer. + * @param y Y-position of the mouse pointer. + */ +void tbMouse(int button, int state, int x, int y) { + assert(tb_button != -1); - if (tb_tracking == GL_FALSE) - return; - - _tbPointToVector(x, y, tb_width, tb_height, current_position); - - /* calculate the angle to rotate by (directly proportional to the - length of the mouse movement */ - dx = current_position[0] - tb_lastposition[0]; - dy = current_position[1] - tb_lastposition[1]; - dz = current_position[2] - tb_lastposition[2]; - tb_angle = 90.0 * sqrt(dx * dx + dy * dy + dz * dz); - - /* calculate the axis of rotation (cross product) */ - tb_axis[0] = tb_lastposition[1] * current_position[2] - - tb_lastposition[2] * current_position[1]; - tb_axis[1] = tb_lastposition[2] * current_position[0] - - tb_lastposition[0] * current_position[2]; - tb_axis[2] = tb_lastposition[0] * current_position[1] - - tb_lastposition[1] * current_position[0]; - - /* reset for next time */ - tb_lasttime = glutGet(GLUT_ELAPSED_TIME); - tb_lastposition[0] = current_position[0]; - tb_lastposition[1] = current_position[1]; - tb_lastposition[2] = current_position[2]; - - /* remember to draw new position */ - glutPostRedisplay(); + if (state == GLUT_DOWN && button == tb_button) + _tbStartMotion(x, y, button, glutGet(GLUT_ELAPSED_TIME)); + else if (state == GLUT_UP && button == tb_button) + _tbStopMotion(button, glutGet(GLUT_ELAPSED_TIME)); } + + +/** + * Starts a rotating scene motion to the given coordinates. + * @param x The x-coordinate. + * @param y The y-coordinate. + */ +void tbMotion(int x, int y) { + GLfloat current_position[3], dx, dy, dz; + + assert(tb_button != -1); + + if (tb_tracking == GL_FALSE) + return; + + _tbPointToVector(x, y, tb_width, tb_height, current_position); + + /* calculate the angle to rotate by (directly proportional to the + length of the mouse movement */ + dx = current_position[0] - tb_lastposition[0]; + dy = current_position[1] - tb_lastposition[1]; + dz = current_position[2] - tb_lastposition[2]; + tb_angle = 90.0 * sqrt(dx * dx + dy * dy + dz * dz); + + /* calculate the axis of rotation (cross product) */ + tb_axis[0] = tb_lastposition[1] * current_position[2] + - tb_lastposition[2] * current_position[1]; + tb_axis[1] = tb_lastposition[2] * current_position[0] + - tb_lastposition[0] * current_position[2]; + tb_axis[2] = tb_lastposition[0] * current_position[1] + - tb_lastposition[1] * current_position[0]; + + /* reset for next time */ + tb_lasttime = glutGet(GLUT_ELAPSED_TIME); + tb_lastposition[0] = current_position[0]; + tb_lastposition[1] = current_position[1]; + tb_lastposition[2] = current_position[2]; + + /* remember to draw new position */ + glutPostRedisplay(); +} + +/*@}*/ diff --git a/simulator/trackball.h b/simulator/trackball.h index 6c93ec0..412068c 100644 --- a/simulator/trackball.h +++ b/simulator/trackball.h @@ -1,77 +1,86 @@ -/* - * Simple trackball-like motion adapted (ripped off) from projtex.c - * (written by David Yu and David Blythe). See the SIGGRAPH '96 - * Advanced OpenGL course notes. - * - * - * Usage: - * - * o call gltbInit() in before any other gltb call - * o call gltbReshape() from the reshape callback - * o call gltbMatrix() to get the trackball matrix rotation - * o call gltbStartMotion() to begin trackball movememt - * o call gltbStopMotion() to stop trackball movememt - * o call gltbMotion() from the motion callback - * o call gltbAnimate(GL_TRUE) if you want the trackball to continue - * spinning after the mouse button has been released - * o call gltbAnimate(GL_FALSE) if you want the trackball to stop - * spinning after the mouse button has been released - * - * Typical setup: - * - * - void - init(void) - { - gltbInit(GLUT_MIDDLE_BUTTON); - gltbAnimate(GL_TRUE); - . . . - } +/** + * \addtogroup unixsimulator + */ +/*@{*/ - void - reshape(int width, int height) - { - gltbReshape(width, height); - . . . - } - - void - display(void) - { - glPushMatrix(); - - gltbMatrix(); - . . . draw the scene . . . - - glPopMatrix(); - } - - void - mouse(int button, int state, int x, int y) - { - gltbMouse(button, state, x, y); - . . . - } - - void - motion(int x, int y) - { - gltbMotion(x, y); - . . . - } - - int - main(int argc, char** argv) - { - . . . - init(); - glutReshapeFunc(reshape); - glutDisplayFunc(display); - glutMouseFunc(mouse); - glutMotionFunc(motion); - . . . - } - ***/ +/** + * Simple trackball-like motion adapted (ripped off) from projtex.c + * (written by David Yu and David Blythe). See the SIGGRAPH '96 + * Advanced OpenGL course notes. + * + * + * Usage: + * + * o call tbInit() in before any other tb call + * o call tbReshape() from the reshape callback + * o call tbMatrix() to get the trackball matrix rotation + * o call tbStartMotion() to begin trackball movememt + * o call tbStopMotion() to stop trackball movememt + * o call tbMotion() from the motion callback + * o call tbAnimate(GL_TRUE) if you want the trackball to continue + * spinning after the mouse button has been released + * o call tbAnimate(GL_FALSE) if you want the trackball to stop + * spinning after the mouse button has been released + * + * Typical setup: + * + * + * void + * init(void) + * { + * tbInit(GLUT_MIDDLE_BUTTON); + * tbAnimate(GL_TRUE); + * . . . + * } + * + * void + * reshape(int width, int height) + * { + * tbReshape(width, height); + * . . . + * } + * + * void + * display(void) + * { + * glPushMatrix(); + * + * tbMatrix(); + * . . . draw the scene . . . + * + * glPopMatrix(); + * } + * + * void + * mouse(int button, int state, int x, int y) + * { + * tbMouse(button, state, x, y); + * . . . + * } + * + * void + * motion(int x, int y) + * { + * tbMotion(x, y); + * . . . + * } + * + * int + * main(int argc, char** argv) + * { + * . . . + * init(); + * glutReshapeFunc(reshape); + * glutDisplayFunc(display); + * glutMouseFunc(mouse); + * glutMotionFunc(motion); + * . . . + * } + * + * @file trackball.h + * @brief Header file for helper functions for the UNIX platform Borg simulator. + * @author Martin Ongsiek, David Yu, David Blythe + */ /* functions */ @@ -87,3 +96,4 @@ void tbAnimate(GLboolean animate); + /*@}*/ diff --git a/simulator/util.c b/simulator/util.c deleted file mode 100644 index cc0d394..0000000 --- a/simulator/util.c +++ /dev/null @@ -1,26 +0,0 @@ -#ifdef _WIN32 - #include -#endif - -#include -#include -#include -#include -#include -#include "joystick.h" - -extern jmp_buf newmode_jmpbuf; - -void wait(unsigned int ms) { - if (waitForFire) { - if (JOYISFIRE) { - longjmp(newmode_jmpbuf, 43); - } - } - -#ifdef _WIN32 - Sleep(ms); -#else - usleep(ms*1000); -#endif -} diff --git a/simulator/winmain.c b/simulator/winmain.c new file mode 100644 index 0000000..7a74c1e --- /dev/null +++ b/simulator/winmain.c @@ -0,0 +1,492 @@ +/** + * \defgroup winsimulator Simulation of the Borg API for the Win32 platform. + */ +/*@{*/ + +/** + * This is a native Win32 port of the Borgware-2D API simulator. Although the + * OpenGL based simulator is in fact platform independent, there are some + * obstacles regarding Cygwin's OpenGL support. + * + * Earlier versions of Cygwin used to ship bindings to Win32's native OpenGL + * libraries. Unfortunately some of those native components (GLUT in particular) + * weren't maintained for years so it was decided to cease support for them. + * + * The reasons are explained in more detail at + * http://cygwin.com/ml/cygwin/2012-05/msg00276.html + * + * The OpenGL bindings which are now shipped with Cygwin require a running + * X-Server which I consider clumsy to use on a Windows platform (especially for + * a small application like this simulator). So I decided to write a native + * Win32 application to free Windows developers from the hassles of rolling out + * a complete X11 setup. + * + * The native simulator feels like the OpenGL based one, with the exception that + * you can't rotate the matrix (I'm using the plain GDI32 API for the graphics). + * + * @file winmain.c + * @brief Simulator for the Win32 platform. + * @author Christian Kroll + */ + +#include +#include +#include "../config.h" +#include "../display_loop.h" + +/** Number of bytes per row. */ +#define LINEBYTES ((NUM_COLS + 1) / 8) + +/** The width (in pixels) of the margin around a LED. */ +#define LED_MARGIN 1 +/** The diameter (in pixels) of a LED. */ +#define LED_DIAMETER 14 +/** The extend of the whole LED including its margin. */ +#define LED_EXTENT (2 * LED_MARGIN + LED_DIAMETER) + +/** Width of the canvas. */ +#define WND_X_EXTENTS (NUM_COLS * LED_EXTENT) +/** Height of the canvas. */ +#define WND_Y_EXTENTS (NUM_ROWS * LED_EXTENT) + + +/* string constants */ +LPCSTR g_strWindowClass = "BorgSimulatorWindowClass"; +LPCSTR g_strWindowTitle = "Borg Simulator"; + +LPCSTR g_strError = "Error"; +LPCSTR g_strErrorRegisterWindow = "Error: Could not register window class."; +LPCSTR g_strErrorCreateWindow = "Error: Could not create window."; + + +/** Event object for the multimedia timer (wait() function). */ +HANDLE g_hWaitEvent; + + +/** Fake port for simulating joystick input. */ +volatile unsigned char fakeport; +/** Flag which indicates if wait should jump to the menu if fire is pressed. */ +volatile unsigned char waitForFire; +/** The simulated frame buffer of the borg. */ +volatile unsigned char pixmap[NUMPLANE][NUM_ROWS][LINEBYTES]; +/** Jump buffer which leads directly the menu. */ +extern jmp_buf newmode_jmpbuf; + +/* forward declarations */ +LRESULT CALLBACK simWndProc(HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam); + + +/** + * Registers a window class (necessary for creating a window). + * @param lpwc Pointer to WNDCLASS struct. + * @param hInstance Handle of the instance where this window class belongs to. + * @return TRUE if successful, otherwise FALSE. + */ +BOOL simRegisterWindowClass(WNDCLASSA *const lpwc, + HINSTANCE hInstance) +{ + lpwc->style = 0; + lpwc->lpfnWndProc = simWndProc; + lpwc->cbClsExtra = 0; + lpwc->cbWndExtra = 0; + lpwc->hInstance = hInstance; + lpwc->hIcon = LoadIcon(NULL, IDI_WINLOGO); + lpwc->hCursor = LoadCursor(NULL, IDC_ARROW); + lpwc->hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + lpwc->lpszMenuName = NULL; + lpwc->lpszClassName = g_strWindowClass; + + return (RegisterClassA(lpwc) != 0); +} + + +/** + * Creates a new window and makes it visible. + * @param lphWnd Pointer to window handle. + * @param hInstance Handle of the instance where this window belongs to. + * @param nCmdShow Flag for showing the window minimized, maximized etc. + * @return TRUE if successful, otherwise FALSE. + */ +BOOL simCreateWindow(HWND *lphWnd, + HINSTANCE hInstance, + int nCmdShow) +{ + /* create window and retrieve its handle */ + *lphWnd = CreateWindow(g_strWindowClass, g_strWindowTitle, + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, + WND_X_EXTENTS * 2, WND_Y_EXTENTS * 2, HWND_DESKTOP, + NULL, hInstance, NULL); + + /* mske it visible */ + if (*lphWnd != NULL) + { + ShowWindow(*lphWnd, nCmdShow); + UpdateWindow(*lphWnd); + return TRUE; + } + + return FALSE; +} + + +/** + * Draws the LED matrix on the given device context. + * @param hdc The device context where the LED matrix should be drawn on. + */ +void simDrawMatrix(HDC hdc) +{ + COLORREF colorLed; + HBRUSH hBrushLed; + HGDIOBJ hGdiOld; + unsigned int c, p, x, y, absX; + int left, right, top, bottom; + static unsigned char const shl_map[8] = + {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; + + /* clear background */ + FloodFill(hdc, 0, 0, RGB(0, 0, 0)); + + /* go through every plane */ + for (p = 0; p < NUMPLANE; ++p) + { + /* create and select red brush into device context */ + colorLed = RGB((255.0 / NUMPLANE) * (p + 1), 0, 0); + hBrushLed = CreateSolidBrush(colorLed); + hGdiOld = SelectObject(hdc, hBrushLed); + + /* translate pixmap into LEDs */ + for (y = 0; y < NUM_ROWS; ++y) + { + for (c = 0; c < LINEBYTES; ++c) + { + for (x = 0; x < 8; ++x) + { + if (pixmap[p][y][c] & shl_map[x]) + { + // eventually draw a LED, mirroring its coordinates + absX = (c * 8 + x) * LED_EXTENT + LED_MARGIN; + left = WND_X_EXTENTS - absX; + right = WND_X_EXTENTS - absX - LED_DIAMETER + 1; + top = y * LED_EXTENT + LED_MARGIN; + bottom = top + LED_DIAMETER - 1; + Ellipse(hdc, left, top, right, bottom); + } + } + } + } + + /* dispose that brush */ + DeleteObject(SelectObject(hdc, hGdiOld)); + } +} + + +/** + * Retrieves device context from given window, creates a compatible memory + * device context for double buffering and hands that thing over to + * simDrawMatrix(). + * @param hWnd The window where the LED-Matrix should be displayed. + */ +void simDisplay(HWND hWnd) +{ + RECT rect; + HDC hdc; + HDC hMemDc; + HBITMAP hBmp; + HBITMAP hOldBmp; + + /* retrieve window dimensions */ + if (GetClientRect(hWnd, &rect)) + { + int const cx = rect.right - rect.left; + int const cy = rect.bottom - rect.top; + + /* retrieve device context */ + if ((hdc = GetDC(hWnd)) != NULL) + { + /* make window contents scalable */ + SetMapMode(hdc, MM_ANISOTROPIC); + SetWindowExtEx(hdc, WND_X_EXTENTS, WND_Y_EXTENTS, NULL); + SetViewportExtEx(hdc, cx, cy, NULL); + + /* create memory device context for double buffering */ + hMemDc = CreateCompatibleDC(hdc); + if (hMemDc != NULL) + { + /* contents of the memory DC should be scaled as well */ + SetMapMode(hMemDc, MM_ANISOTROPIC); + SetWindowExtEx(hMemDc, WND_X_EXTENTS, WND_Y_EXTENTS, NULL); + SetViewportExtEx(hMemDc, cx, cy, NULL); + + /* create a bitmap to be associated with the memory DC... */ + hBmp = CreateCompatibleBitmap(hdc, cx, cy); + if (hBmp != NULL) + { + /* ...and selct that into that DC */ + hOldBmp = (HBITMAP)SelectObject(hMemDc, hBmp); + + /* finally *sigh* draw the LED matrix */ + simDrawMatrix(hMemDc); + + /* and blit that into the window DC */ + BitBlt(hdc, 0, 0, cx, cy, hMemDc, 0, 0, SRCCOPY); + + /* clean up */ + DeleteObject(SelectObject(hMemDc, hOldBmp)); + } + DeleteDC(hMemDc); + } + ReleaseDC(hWnd, hdc); + } + InvalidateRect(hWnd, &rect, FALSE); + } +} + +/** + * Message handler for the main window. + * @param hWnd The window whose messages should be processed. + * @param msg The message fired from the operating system. + * @param wParam First message parameter. + * @param lParam Second message parameter. + */ +LRESULT CALLBACK simWndProc(HWND hWnd, + UINT msg, + WPARAM wParam, + LPARAM lParam) +{ + PAINTSTRUCT ps; + HDC hdc; + LPMINMAXINFO lpminmax; + + switch (msg) + { + /* enforce minimum window size */ + case WM_GETMINMAXINFO: + lpminmax = (LPMINMAXINFO)lParam; + lpminmax->ptMinTrackSize.x = WND_X_EXTENTS * 2; + lpminmax->ptMinTrackSize.y = WND_Y_EXTENTS * 2; + break; + + /* paint window contents */ + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + if (hdc != NULL) + { + simDisplay(hWnd); + EndPaint(hWnd, &ps); + } + break; + + /* map key presses to fake joystick movements */ + case WM_KEYDOWN: + switch (wParam) + { + case VK_ESCAPE: + case 'Q': + PostQuitMessage(0); + break; + + case VK_SPACE: + fakeport |= 0x01; + break; + + case 'A': + fakeport |= 0x02; + break; + + case 'D': + fakeport |= 0x04; + break; + + case 'S': + fakeport |= 0x08; + break; + + case 'W': + fakeport |= 0x10; + break; + + default: + return DefWindowProcA(hWnd, msg, wParam, lParam); + break; + } + break; + + /* map key releases to fake joystick movements */ + case WM_KEYUP: + switch(wParam) + { + case VK_SPACE: + fakeport &= ~0x01; + break; + + case 'A': + fakeport &= ~0x02; + break; + + case 'D': + fakeport &= ~0x04; + break; + + case 'S': + fakeport &= ~0x08; + break; + + case 'W': + fakeport &= ~0x10; + break; + + default: + return DefWindowProcA(hWnd, msg, wParam, lParam); + break; + } + break; + + /* refresh the LED matrix every 40 ms */ + case WM_TIMER: + simDisplay(hWnd); + break; + + /* quit application */ + case WM_DESTROY: + PostQuitMessage(0); + break; + + /* Windows' default handler */ + default: + return DefWindowProcA(hWnd, msg, wParam, lParam); + break; + } + + return 0; +} + + +/** + * Entry point for starting the the display loop in a thread. + * @param lpParam Free style arguments for the thread function (not used here). + * @return Always zero. + */ +DWORD WINAPI simLoop(LPVOID lpParam) +{ + display_loop(); + return 0; +} + + +/** + * Wait function which utilizes multimedia timers and thread synchronization + * objects. Although this is much more complicated than calling the Sleep() + * function, it is also much more precise. + * @param ms The requested delay in milliseconds. + */ +void wait(int ms) +{ + TIMECAPS tc; + MMRESULT mmresult; + MMRESULT mmTimerEventId; + UINT uResolution; + + /* check if fire button is pressed (and if it is, jump to the menu) */ + if (waitForFire) { + if (fakeport & 0x01) { + longjmp(newmode_jmpbuf, 43); + } + } + + /* retrieve timer resolution capabilities of the current system */ + mmresult = timeGetDevCaps(&tc, sizeof(tc)); + if (mmresult == TIMERR_NOERROR) + { + /* retrieve best resolution and configure timer services accordingly */ + uResolution = min(max(tc.wPeriodMin, 0), tc.wPeriodMax); + mmresult = timeBeginPeriod(uResolution); + if (mmresult == TIMERR_NOERROR) + { + /* actually retrieve a multimedia timer */ + mmTimerEventId = timeSetEvent(ms, uResolution, g_hWaitEvent, NULL, + TIME_ONESHOT | TIME_CALLBACK_EVENT_PULSE); + if (mmTimerEventId != NULL) + { + /* now halt until that timer pulses our wait event object */ + WaitForSingleObject(g_hWaitEvent, INFINITE); + + /* relieve the timer from its duties */ + timeKillEvent(mmTimerEventId); + } + + /* relax timer service constraints */ + timeEndPeriod (uResolution); + } + } +} + + +/** + * Main function of the windows simulator. + * @param hInstance Instance handle given by the operating system. + * @param hPrevInstance This parameter has no meaning in Win32. + * @param lpCmdLine Pointer to a null terminated command line string. + * @param nCmdShow Flags for showing the window minimized, maximized and so on. + * @return Exit code, always 0 here. + */ +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow) +{ + WNDCLASS wc; + HWND hWnd; + MSG msg; + HANDLE hThread; + + /* regster window class (with nice black background!) */ + if (simRegisterWindowClass(&wc, hInstance)) + { + /* actually create the window and make it visible */ + if (simCreateWindow(&hWnd, hInstance, nCmdShow)) + { + /* event handle for multimedia timer (for the wait() function) */ + g_hWaitEvent = CreateEventA(NULL, FALSE, FALSE, "Local\\WaitEvent"); + if (g_hWaitEvent != NULL) + { + /* start the display loop thread */ + hThread = CreateThread(NULL, 0, simLoop, NULL, 0, NULL); + if (hThread != NULL) + { + /* ensure that the display loop stays responsive */ + SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST); + + /* issue a timer message every 40 ms (roughly 25 fps) */ + /* NOTE: this has nothing to do with the multimedia timer */ + SetTimer(hWnd, 23, 40, NULL); + + /* standard Windows(R) message loop */ + /* (runs as long as the window hasn't been closed) */ + while (GetMessageA(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessageA(&msg); + } + + /* stop the display loop */ + TerminateThread(hThread, 0); + } + + /* relieve wait event object from its duties */ + CloseHandle(g_hWaitEvent); + } + + return msg.wParam; + } + MessageBoxA(HWND_DESKTOP, g_strErrorCreateWindow, g_strError, MB_OK); + } + MessageBoxA(HWND_DESKTOP, g_strErrorRegisterWindow, g_strError, MB_OK); + + return 0; +} + +/*@}*/