From 99d53f15a4216240088132f6af9cb194b519b1cc Mon Sep 17 00:00:00 2001 From: Diederick Niehorster Date: Thu, 4 Apr 2013 01:56:39 +0000 Subject: [PATCH] - moving to a new way of handling window changes (position, size, visibility) - we now use a work list and all actions queued up in that list get executed in the main loop, just before redraws are called - simplified redrawing on windows, now no longer doing so in response to WM_PAINT message. This allows us to full control over when we draw - now simplified use of state variables in X11 and andriod - resize, reposition etc callback now processed thru notify checkers that call callback only if needed - worklist also has an init work type which is executed once for every window, just before the window receives its first draw. This gets rid of hacks to call the reshape function once (that only worked for windows that are created before mainloop is entered) - in init work processor we also check for presence of draw callback as not having one is an error following GLUT. - some cleanup in general git-svn-id: svn+ssh://svn.code.sf.net/p/freeglut/code/trunk/freeglut/freeglut@1577 7f0cb862-5218-0410-a997-914c9d46530a --- progs/demos/Resizer/Resizer.cpp | 107 +++- src/android/fg_internal_android.h | 3 +- src/android/fg_main_android.c | 1044 +++++++++++++++++++----------------- src/android/fg_structure_android.c | 2 - src/android/fg_window_android.c | 64 +-- src/fg_gamemode.c | 9 - src/fg_internal.h | 82 ++- src/fg_main.c | 140 +++-- src/fg_main.h | 34 -- src/fg_window.c | 54 +- src/mswin/fg_internal_mswin.h | 1 - src/mswin/fg_main_mswin.c | 382 ++++++++++--- src/mswin/fg_menu_mswin.c | 4 +- src/mswin/fg_state_mswin.c | 31 +- src/mswin/fg_window_mswin.c | 340 +----------- src/x11/fg_internal_x11.h | 1 + src/x11/fg_main_x11.c | 115 +++- src/x11/fg_window_x11.c | 86 +-- 18 files changed, 1335 insertions(+), 1164 deletions(-) delete mode 100644 src/fg_main.h diff --git a/progs/demos/Resizer/Resizer.cpp b/progs/demos/Resizer/Resizer.cpp index 57fce0b..163123f 100644 --- a/progs/demos/Resizer/Resizer.cpp +++ b/progs/demos/Resizer/Resizer.cpp @@ -4,10 +4,6 @@ int nWindow, nChildWindow = -1; int nLoopMain = 0; - -int nPosX, nPosY; -int nWidth, nHeight; - GLboolean bChildPosDone = GL_FALSE, bChildSizeDone = GL_FALSE; void SampleKeyboard( unsigned char cChar, int nMouseX, int nMouseY ); @@ -21,14 +17,14 @@ void WindowStatus(int state); void DrawQuad() { - nWidth = glutGet(GLUT_WINDOW_WIDTH); - nHeight = glutGet(GLUT_WINDOW_HEIGHT); + int width = glutGet(GLUT_WINDOW_WIDTH); + int height = glutGet(GLUT_WINDOW_HEIGHT); glBegin(GL_QUADS); - glVertex2d(nWidth*.25, nHeight*.75); - glVertex2d(nWidth*.75, nHeight*.75); - glVertex2d(nWidth*.75, nHeight*.25); - glVertex2d(nWidth*.25, nHeight*.25); + glVertex2d(width*.25, height*.75); + glVertex2d(width*.75, height*.75); + glVertex2d(width*.75, height*.25); + glVertex2d(width*.25, height*.25); glEnd(); } @@ -38,6 +34,12 @@ void UnhideTimer(int window) glutShowWindow(); } +void ChangeTitleTimer(int unused) +{ + glutSetIconTitle("new icon title"); + glutSetWindowTitle("new test title"); +} + void SampleKeyboard( unsigned char cChar, int nMouseX, int nMouseY ) { switch (cChar) @@ -72,7 +74,7 @@ void SampleKeyboard( unsigned char cChar, int nMouseX, int nMouseY ) { glutSetWindow(nWindow); printf("main window resize\n"); - if (nWidth<400) + if (glutGet(GLUT_WINDOW_WIDTH)<400) glutReshapeWindow(600,300); else glutReshapeWindow(300,300); @@ -103,7 +105,7 @@ void SampleKeyboard( unsigned char cChar, int nMouseX, int nMouseY ) * the client area is at a different position if the window has borders * and/or a title bar. */ - if (nPosX<400) + if (glutGet(GLUT_WINDOW_X)<400) glutPositionWindow(600,300); else glutPositionWindow(300,300); @@ -112,16 +114,47 @@ void SampleKeyboard( unsigned char cChar, int nMouseX, int nMouseY ) break; + case 'd': + case 'D': + if (nChildWindow!=-1 && cChar=='d') /* Capital D always moves+resizes the main window*/ + { + glutSetWindow(nChildWindow); + if (!bChildPosDone) + glutPositionWindow(glutGet(GLUT_WINDOW_X)+50,glutGet(GLUT_WINDOW_Y)+50); + else + glutPositionWindow(glutGet(GLUT_WINDOW_X)-50,glutGet(GLUT_WINDOW_Y)-50); + bChildPosDone = !bChildPosDone; + if (!bChildSizeDone) + glutReshapeWindow(glutGet(GLUT_WINDOW_WIDTH)+50,glutGet(GLUT_WINDOW_HEIGHT)+50); + else + glutReshapeWindow(glutGet(GLUT_WINDOW_WIDTH)-50,glutGet(GLUT_WINDOW_HEIGHT)-50); + bChildSizeDone = !bChildSizeDone; + } + else + { + if (glutGet(GLUT_WINDOW_X)<400) + glutPositionWindow(600,300); + else + glutPositionWindow(300,300); + if (glutGet(GLUT_WINDOW_WIDTH)<400) + glutReshapeWindow(600,300); + else + glutReshapeWindow(300,300); + } + break; + + case 'c': case 'C': if (nChildWindow==-1) { + int width = glutGet(GLUT_WINDOW_WIDTH); + int height = glutGet(GLUT_WINDOW_HEIGHT); + /* open child window */ printf("open child window\n"); - nWidth = glutGet(GLUT_WINDOW_WIDTH); - nHeight = glutGet(GLUT_WINDOW_HEIGHT); - nChildWindow = glutCreateSubWindow(nWindow,(int)(nWidth*.35),(int)(nHeight*.35),(int)(nWidth*.3),(int)(nHeight*.3)); + nChildWindow = glutCreateSubWindow(nWindow,(int)(width*.35),(int)(height*.35),(int)(width*.3),(int)(height*.3)); glutKeyboardFunc( SampleKeyboard ); glutDisplayFunc( Redisplay ); glutReshapeFunc( Reshape ); @@ -144,6 +177,7 @@ void SampleKeyboard( unsigned char cChar, int nMouseX, int nMouseY ) case 'i': case 'I': glutIconifyWindow(); + glutTimerFunc(1500, ChangeTitleTimer, 0); break; @@ -175,7 +209,7 @@ void Reshape(int width, int height) { int win = glutGetWindow(); - printf("reshape %s, %dx%d\n",win==nWindow?"main":"child", + printf("reshape %s, client area: %dx%d\n",win==nWindow?"main":"child", width, height); glViewport(0,0,width,height); @@ -185,9 +219,21 @@ void Reshape(int width, int height) if (win==nWindow && nChildWindow!=-1) { + /* Put child window in right place */ + int x = (int)(width*.35), y=(int)(height*.35), w=(int)(width*.3), h = (int)(height*.3); + if (bChildPosDone) + { + x += 50; + y += 50; + } + if (bChildSizeDone) + { + w += 50; + h += 50; + } glutSetWindow(nChildWindow); - glutPositionWindow((int)(width*.35),(int)(height*.35)); - glutReshapeWindow((int)(width*.3),(int)(height*.3)); + glutPositionWindow(x,y); + glutReshapeWindow(w,h); glutSetWindow(nWindow); } } @@ -196,7 +242,7 @@ void Position(int x, int y) { int win = glutGetWindow(); - printf("position, %s: (%d,%d)\n",win==nWindow?"top-left of main":"top-left of child relative to parent", + printf("position, %s: (%d,%d)\n",win==nWindow?"top-left (non-client) of main":"top-left of child relative to parent", x, y); } @@ -232,12 +278,14 @@ void Redisplay(void) void Timer(int unused) { int win = glutGetWindow(); + int x, y; + int width, height; int border, caption; - nPosX = glutGet(GLUT_WINDOW_X); - nPosY = glutGet(GLUT_WINDOW_Y); - nWidth = glutGet(GLUT_WINDOW_WIDTH); - nHeight = glutGet(GLUT_WINDOW_HEIGHT); + x = glutGet(GLUT_WINDOW_X); + y = glutGet(GLUT_WINDOW_Y); + width = glutGet(GLUT_WINDOW_WIDTH); + height = glutGet(GLUT_WINDOW_HEIGHT); border = glutGet(GLUT_WINDOW_BORDER_WIDTH); caption = glutGet(GLUT_WINDOW_HEADER_HEIGHT); /* returned position is top-left of client area, to get top-left of @@ -253,14 +301,14 @@ void Timer(int unused) /* printf("window border: %dpx, caption: %dpx\n",border,caption); */ if (win==nWindow) printf("main window %dx%d, top-left of client at: (%d,%d), of window at: (%d,%d)\n", - nWidth, nHeight, - nPosX ,nPosY, - nPosX-border, - nPosY-border-caption); + width, height, + x ,y, + x-border, + y-border-caption); else printf("child window %dx%d, top-left of client at: (%d,%d), relative to parent\n", - nWidth, nHeight, - nPosX ,nPosY); + width, height, + x ,y); /* (re)set the timer callback and ask glut to call it in 500 ms */ glutTimerFunc(500, Timer, 0); @@ -287,6 +335,7 @@ int main(int argc, char* argv[]) glutInitWindowSize(200,200); nWindow = glutCreateWindow("test"); + glutSetIconTitle("test icon title"); printf("main window id: %d\n", nWindow); glutKeyboardFunc( SampleKeyboard ); diff --git a/src/android/fg_internal_android.h b/src/android/fg_internal_android.h index 0feaad6..445251f 100644 --- a/src/android/fg_internal_android.h +++ b/src/android/fg_internal_android.h @@ -106,8 +106,7 @@ struct tagSFG_PlatformJoystick typedef struct tagSFG_PlatformWindowState SFG_PlatformWindowState; struct tagSFG_PlatformWindowState { - int32_t LastHeight; - int32_t LastWidth; + char unused; }; #endif /* FREEGLUT_INTERNAL_ANDROID_H */ diff --git a/src/android/fg_main_android.c b/src/android/fg_main_android.c index 15d31e6..ce3f585 100644 --- a/src/android/fg_main_android.c +++ b/src/android/fg_main_android.c @@ -1,478 +1,566 @@ -/* - * fg_main_android.c - * - * The Android-specific windows message processing methods. - * - * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. - * Written by Pawel W. Olszta, - * Copied for Platform code by Evan Felix - * Copyright (C) 2012 Sylvain Beucler - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include "fg_internal.h" -#include "egl/fg_window_egl.h" - -#include -#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FreeGLUT", __VA_ARGS__)) -#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "FreeGLUT", __VA_ARGS__)) -#include -#include - -static struct touchscreen touchscreen; -static unsigned char key_a2fg[256]; - -/* Cf. http://developer.android.com/reference/android/view/KeyEvent.html */ -/* These codes are missing in */ -/* Don't convert to enum, since it may conflict with future version of - that */ -#define AKEYCODE_FORWARD_DEL 112 -#define AKEYCODE_CTRL_LEFT 113 -#define AKEYCODE_CTRL_RIGHT 114 -#define AKEYCODE_MOVE_HOME 122 -#define AKEYCODE_MOVE_END 123 -#define AKEYCODE_INSERT 124 -#define AKEYCODE_ESCAPE 127 -#define AKEYCODE_F1 131 -#define AKEYCODE_F2 132 -#define AKEYCODE_F3 133 -#define AKEYCODE_F4 134 -#define AKEYCODE_F5 135 -#define AKEYCODE_F6 136 -#define AKEYCODE_F7 137 -#define AKEYCODE_F8 138 -#define AKEYCODE_F9 139 -#define AKEYCODE_F10 140 -#define AKEYCODE_F11 141 -#define AKEYCODE_F12 142 - -#define EVENT_HANDLED 1 -#define EVENT_NOT_HANDLED 0 - -/** - * Initialize Android keycode to GLUT keycode mapping - */ -static void key_init() { - memset(key_a2fg, 0, sizeof(key_a2fg)); - - key_a2fg[AKEYCODE_F1] = GLUT_KEY_F1; - key_a2fg[AKEYCODE_F2] = GLUT_KEY_F2; - key_a2fg[AKEYCODE_F3] = GLUT_KEY_F3; - key_a2fg[AKEYCODE_F4] = GLUT_KEY_F4; - key_a2fg[AKEYCODE_F5] = GLUT_KEY_F5; - key_a2fg[AKEYCODE_F6] = GLUT_KEY_F6; - key_a2fg[AKEYCODE_F7] = GLUT_KEY_F7; - key_a2fg[AKEYCODE_F8] = GLUT_KEY_F8; - key_a2fg[AKEYCODE_F9] = GLUT_KEY_F9; - key_a2fg[AKEYCODE_F10] = GLUT_KEY_F10; - key_a2fg[AKEYCODE_F11] = GLUT_KEY_F11; - key_a2fg[AKEYCODE_F12] = GLUT_KEY_F12; - - key_a2fg[AKEYCODE_PAGE_UP] = GLUT_KEY_PAGE_UP; - key_a2fg[AKEYCODE_PAGE_DOWN] = GLUT_KEY_PAGE_DOWN; - key_a2fg[AKEYCODE_MOVE_HOME] = GLUT_KEY_HOME; - key_a2fg[AKEYCODE_MOVE_END] = GLUT_KEY_END; - key_a2fg[AKEYCODE_INSERT] = GLUT_KEY_INSERT; - - key_a2fg[AKEYCODE_DPAD_UP] = GLUT_KEY_UP; - key_a2fg[AKEYCODE_DPAD_DOWN] = GLUT_KEY_DOWN; - key_a2fg[AKEYCODE_DPAD_LEFT] = GLUT_KEY_LEFT; - key_a2fg[AKEYCODE_DPAD_RIGHT] = GLUT_KEY_RIGHT; - - key_a2fg[AKEYCODE_ALT_LEFT] = GLUT_KEY_ALT_L; - key_a2fg[AKEYCODE_ALT_RIGHT] = GLUT_KEY_ALT_R; - key_a2fg[AKEYCODE_SHIFT_LEFT] = GLUT_KEY_SHIFT_L; - key_a2fg[AKEYCODE_SHIFT_RIGHT] = GLUT_KEY_SHIFT_R; - key_a2fg[AKEYCODE_CTRL_LEFT] = GLUT_KEY_CTRL_L; - key_a2fg[AKEYCODE_CTRL_RIGHT] = GLUT_KEY_CTRL_R; -} - -/** - * Convert an Android key event to ASCII. - */ -static unsigned char key_ascii(struct android_app* app, AInputEvent* event) { - int32_t code = AKeyEvent_getKeyCode(event); - - /* Handle a few special cases: */ - switch (code) { - case AKEYCODE_DEL: - return 8; - case AKEYCODE_FORWARD_DEL: - return 127; - case AKEYCODE_ESCAPE: - return 27; - } - - /* Get usable JNI context */ - JNIEnv* env = app->activity->env; - JavaVM* vm = app->activity->vm; - (*vm)->AttachCurrentThread(vm, &env, NULL); - - jclass KeyEventClass = (*env)->FindClass(env, "android/view/KeyEvent"); - jmethodID KeyEventConstructor = (*env)->GetMethodID(env, KeyEventClass, "", "(II)V"); - jobject keyEvent = (*env)->NewObject(env, KeyEventClass, KeyEventConstructor, - AKeyEvent_getAction(event), AKeyEvent_getKeyCode(event)); - jmethodID KeyEvent_getUnicodeChar = (*env)->GetMethodID(env, KeyEventClass, "getUnicodeChar", "(I)I"); - int ascii = (*env)->CallIntMethod(env, keyEvent, KeyEvent_getUnicodeChar, AKeyEvent_getMetaState(event)); - - /* LOGI("getUnicodeChar(%d) = %d ('%c')", AKeyEvent_getKeyCode(event), ascii, ascii); */ - (*vm)->DetachCurrentThread(vm); - - return ascii; -} - -unsigned long fgPlatformSystemTime ( void ) -{ - struct timeval now; - gettimeofday( &now, NULL ); - return now.tv_usec/1000 + now.tv_sec*1000; -} - -/* - * Does the magic required to relinquish the CPU until something interesting - * happens. - */ -void fgPlatformSleepForEvents( long msec ) -{ - /* Android's NativeActivity relies on a Looper/ALooper object to - notify about events. The Looper object is plugged on two - internal pipe(2)s to detect system and input events. Sadly you - can only ask the Looper for an event, not just ask whether - there is a pending event (and process it later). Consequently, - short of redesigning NativeActivity, we cannot - SleepForEvents. */ -} - -/** - * Process the next input event. - */ -int32_t handle_input(struct android_app* app, AInputEvent* event) { - SFG_Window* window = fgWindowByHandle(app->window); - if (window == NULL) - return EVENT_NOT_HANDLED; - - /* FIXME: in Android, when a key is repeated, down - and up events happen most often at the exact same time. This - makes it impossible to animate based on key press time. */ - /* e.g. down/up/wait/down/up rather than down/wait/down/wait/up */ - /* This looks like a bug in the Android virtual keyboard system :/ - Real buttons such as the Back button appear to work correctly - (series of down events with proper getRepeatCount value). */ - - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { - /* LOGI("action: %d", AKeyEvent_getAction(event)); */ - /* LOGI("keycode: %d", code); */ - int32_t code = AKeyEvent_getKeyCode(event); - - if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) { - int32_t keypress = 0; - unsigned char ascii = 0; - if ((keypress = key_a2fg[code]) && FETCH_WCB(*window, Special)) { - INVOKE_WCB(*window, Special, (keypress, window->State.MouseX, window->State.MouseY)); - return EVENT_HANDLED; - } else if ((ascii = key_ascii(app, event)) && FETCH_WCB(*window, Keyboard)) { - INVOKE_WCB(*window, Keyboard, (ascii, window->State.MouseX, window->State.MouseY)); - return EVENT_HANDLED; - } - } - else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP) { - int32_t keypress = 0; - unsigned char ascii = 0; - if ((keypress = key_a2fg[code]) && FETCH_WCB(*window, Special)) { - INVOKE_WCB(*window, SpecialUp, (keypress, window->State.MouseX, window->State.MouseY)); - return EVENT_HANDLED; - } else if ((ascii = key_ascii(app, event)) && FETCH_WCB(*window, Keyboard)) { - INVOKE_WCB(*window, KeyboardUp, (ascii, window->State.MouseX, window->State.MouseY)); - return EVENT_HANDLED; - } - } - } - - int32_t source = AInputEvent_getSource(event); - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION - && source == AINPUT_SOURCE_TOUCHSCREEN) { - int32_t action = AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK; - /* Pointer ID for clicks */ - int32_t pidx = AMotionEvent_getAction(event) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - /* TODO: Handle multi-touch; also handle multiple sources/devices */ - /* cf. http://sourceforge.net/mailarchive/forum.php?thread_name=20120518071314.GA28061%40perso.beuc.net&forum_name=freeglut-developer */ - if (0) { - LOGI("motion action=%d index=%d source=%d", action, pidx, source); - int count = AMotionEvent_getPointerCount(event); - int i; - for (i = 0; i < count; i++) { - LOGI("multi(%d): %.01f,%.01f", - AMotionEvent_getPointerId(event, i), - AMotionEvent_getX(event, i), AMotionEvent_getY(event, i)); - } - } - float x = AMotionEvent_getX(event, 0); - float y = AMotionEvent_getY(event, 0); - - /* Virtual arrows PAD */ - /* Don't interfere with existing mouse move event */ - if (!touchscreen.in_mmotion) { - struct vpad_state prev_vpad = touchscreen.vpad; - touchscreen.vpad.left = touchscreen.vpad.right - = touchscreen.vpad.up = touchscreen.vpad.down = false; - - /* int32_t width = ANativeWindow_getWidth(window->Window.Handle); */ - int32_t height = ANativeWindow_getHeight(window->Window.Handle); - if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_MOVE) { - if ((x > 0 && x < 100) && (y > (height - 100) && y < height)) - touchscreen.vpad.left = true; - if ((x > 200 && x < 300) && (y > (height - 100) && y < height)) - touchscreen.vpad.right = true; - if ((x > 100 && x < 200) && (y > (height - 100) && y < height)) - touchscreen.vpad.down = true; - if ((x > 100 && x < 200) && (y > (height - 200) && y < (height - 100))) - touchscreen.vpad.up = true; - } - if (action == AMOTION_EVENT_ACTION_DOWN && - (touchscreen.vpad.left || touchscreen.vpad.right || touchscreen.vpad.down || touchscreen.vpad.up)) - touchscreen.vpad.on = true; - if (action == AMOTION_EVENT_ACTION_UP) - touchscreen.vpad.on = false; - if (prev_vpad.left != touchscreen.vpad.left - || prev_vpad.right != touchscreen.vpad.right - || prev_vpad.up != touchscreen.vpad.up - || prev_vpad.down != touchscreen.vpad.down - || prev_vpad.on != touchscreen.vpad.on) { - if (FETCH_WCB(*window, Special)) { - if (prev_vpad.left == false && touchscreen.vpad.left == true) - INVOKE_WCB(*window, Special, (GLUT_KEY_LEFT, x, y)); - else if (prev_vpad.right == false && touchscreen.vpad.right == true) - INVOKE_WCB(*window, Special, (GLUT_KEY_RIGHT, x, y)); - else if (prev_vpad.up == false && touchscreen.vpad.up == true) - INVOKE_WCB(*window, Special, (GLUT_KEY_UP, x, y)); - else if (prev_vpad.down == false && touchscreen.vpad.down == true) - INVOKE_WCB(*window, Special, (GLUT_KEY_DOWN, x, y)); - } - if (FETCH_WCB(*window, SpecialUp)) { - if (prev_vpad.left == true && touchscreen.vpad.left == false) - INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_LEFT, x, y)); - if (prev_vpad.right == true && touchscreen.vpad.right == false) - INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_RIGHT, x, y)); - if (prev_vpad.up == true && touchscreen.vpad.up == false) - INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_UP, x, y)); - if (prev_vpad.down == true && touchscreen.vpad.down == false) - INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_DOWN, x, y)); - } - return EVENT_HANDLED; - } - } - - /* Normal mouse events */ - if (!touchscreen.vpad.on) { - window->State.MouseX = x; - window->State.MouseY = y; - if (action == AMOTION_EVENT_ACTION_DOWN && FETCH_WCB(*window, Mouse)) { - touchscreen.in_mmotion = true; - INVOKE_WCB(*window, Mouse, (GLUT_LEFT_BUTTON, GLUT_DOWN, x, y)); - } else if (action == AMOTION_EVENT_ACTION_UP && FETCH_WCB(*window, Mouse)) { - touchscreen.in_mmotion = false; - INVOKE_WCB(*window, Mouse, (GLUT_LEFT_BUTTON, GLUT_UP, x, y)); - } else if (action == AMOTION_EVENT_ACTION_MOVE && FETCH_WCB(*window, Motion)) { - INVOKE_WCB(*window, Motion, (x, y)); - } - } - - return EVENT_HANDLED; - } - - /* Let Android handle other events (e.g. Back and Menu buttons) */ - return EVENT_NOT_HANDLED; -} - -/** - * Process the next main command. - */ -void handle_cmd(struct android_app* app, int32_t cmd) { - SFG_Window* window = fgWindowByHandle(app->window); /* may be NULL */ - switch (cmd) { - /* App life cycle, in that order: */ - case APP_CMD_START: - LOGI("handle_cmd: APP_CMD_START"); - break; - case APP_CMD_RESUME: - LOGI("handle_cmd: APP_CMD_RESUME"); - /* Cf. fgPlatformProcessSingleEvent */ - break; - case APP_CMD_INIT_WINDOW: /* surfaceCreated */ - /* The window is being shown, get it ready. */ - LOGI("handle_cmd: APP_CMD_INIT_WINDOW %p", app->window); - fgDisplay.pDisplay.single_native_window = app->window; - /* start|resume: glPlatformOpenWindow was waiting for Handle to be - defined and will now continue processing */ - break; - case APP_CMD_GAINED_FOCUS: - LOGI("handle_cmd: APP_CMD_GAINED_FOCUS"); - break; - case APP_CMD_WINDOW_RESIZED: - LOGI("handle_cmd: APP_CMD_WINDOW_RESIZED"); - if (window->Window.pContext.egl.Surface != EGL_NO_SURFACE) - /* Make ProcessSingleEvent detect the new size, only available - after the next SwapBuffer */ - glutPostRedisplay(); - break; - - case APP_CMD_SAVE_STATE: /* onSaveInstanceState */ - /* The system has asked us to save our current state, when it - pauses the application without destroying it right after. */ - app->savedState = strdup("Detect me as non-NULL on next android_main"); - app->savedStateSize = strlen(app->savedState) + 1; - LOGI("handle_cmd: APP_CMD_SAVE_STATE"); - break; - case APP_CMD_PAUSE: - LOGI("handle_cmd: APP_CMD_PAUSE"); - /* Cf. fgPlatformProcessSingleEvent */ - break; - case APP_CMD_LOST_FOCUS: - LOGI("handle_cmd: APP_CMD_LOST_FOCUS"); - break; - case APP_CMD_TERM_WINDOW: /* surfaceDestroyed */ - /* The application is being hidden, but may be restored */ - LOGI("handle_cmd: APP_CMD_TERM_WINDOW"); - fghPlatformCloseWindowEGL(window); - window->State.NeedToInitContext = GL_TRUE; - fgDisplay.pDisplay.single_native_window = NULL; - break; - case APP_CMD_STOP: - LOGI("handle_cmd: APP_CMD_STOP"); - break; - case APP_CMD_DESTROY: /* Activity.onDestroy */ - LOGI("handle_cmd: APP_CMD_DESTROY"); - /* User closed the application for good, let's kill the window */ - { - /* Can't use fgWindowByHandle as app->window is NULL */ - SFG_Window* window = fgStructure.CurrentWindow; - if (window != NULL) { - fgDestroyWindow(window); - } else { - LOGI("APP_CMD_DESTROY: No current window"); - } - } - /* glue has already set android_app->destroyRequested=1 */ - break; - - case APP_CMD_CONFIG_CHANGED: - /* Handle rotation / orientation change */ - LOGI("handle_cmd: APP_CMD_CONFIG_CHANGED"); - break; - case APP_CMD_LOW_MEMORY: - LOGI("handle_cmd: APP_CMD_LOW_MEMORY"); - break; - default: - LOGI("handle_cmd: unhandled cmd=%d", cmd); - } -} - -void fgPlatformOpenWindow( SFG_Window* window, const char* title, - GLboolean positionUse, int x, int y, - GLboolean sizeUse, int w, int h, - GLboolean gameMode, GLboolean isSubWindow ); - -void fgPlatformProcessSingleEvent ( void ) -{ - /* When the screen is resized, the window handle still points to the - old window until the next SwapBuffer, while it's crucial to set - the size (onShape) correctly before the next onDisplay callback. - Plus we don't know if the next SwapBuffer already occurred at the - time we process the event (e.g. during onDisplay). */ - /* So we do the check each time rather than on event. */ - /* Interestingly, on a Samsung Galaxy S/PowerVR SGX540 GPU/Android - 2.3, that next SwapBuffer is fake (but still necessary to get the - new size). */ - SFG_Window* window = fgStructure.CurrentWindow; - if (window != NULL && window->Window.Handle != NULL) { - int32_t width = ANativeWindow_getWidth(window->Window.Handle); - int32_t height = ANativeWindow_getHeight(window->Window.Handle); - if (width != window->State.pWState.LastWidth || height != window->State.pWState.LastHeight) { - window->State.pWState.LastWidth = width; - window->State.pWState.LastHeight = height; - LOGI("width=%d, height=%d", width, height); - if( FETCH_WCB( *window, Reshape ) ) - INVOKE_WCB( *window, Reshape, ( width, height ) ); - else - glViewport( 0, 0, width, height ); - glutPostRedisplay(); - } - } - - /* Read pending event. */ - int ident; - int events; - struct android_poll_source* source; - /* This is called "ProcessSingleEvent" but this means we'd only - process ~60 (screen Hz) mouse events per second, plus other ports - are processing all events already. So let's process all pending - events. */ - /* if ((ident=ALooper_pollOnce(0, NULL, &events, (void**)&source)) >= 0) { */ - while ((ident=ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) { - /* Process this event. */ - if (source != NULL) { - source->process(source->app, source); - } - } - - /* If we're not in RESUME state, Android paused us, so wait */ - struct android_app* app = fgDisplay.pDisplay.app; - if (app->destroyRequested != 1 && app->activityState != APP_CMD_RESUME) { - INVOKE_WCB(*window, Pause, ()); - - int FOREVER = -1; - while (app->destroyRequested != 1 && (app->activityState != APP_CMD_RESUME)) { - if ((ident=ALooper_pollOnce(FOREVER, NULL, &events, (void**)&source)) >= 0) { - /* Process this event. */ - if (source != NULL) { - source->process(source->app, source); - } - } - } - /* Coming back from a pause: */ - /* - Recreate window context and surface */ - /* - Call user-defined hook to restore resources (textures...) */ - /* - Exit pause looop */ - if (app->destroyRequested != 1) { - /* Android is full-screen only, simplified call.. */ - /* Ideally we'd have a fgPlatformReopenWindow() */ - /* If we're hidden by a non-fullscreen or translucent activity, - we'll be paused but not stopped, and keep the current - surface; in which case fgPlatformOpenWindow will no-op. */ - fgPlatformOpenWindow(window, "", GL_FALSE, 0, 0, GL_FALSE, 0, 0, GL_FALSE, GL_FALSE); - - if (!FETCH_WCB(*window, InitContext)) - fgWarning("Resuming application, but no callback to reload context resources (glutInitContextFunc)"); - } - - INVOKE_WCB(*window, Resume, ()); - } -} - -void fgPlatformMainLoopPreliminaryWork ( void ) -{ - LOGI("fgPlatformMainLoopPreliminaryWork\n"); - - key_init(); - - /* Make sure glue isn't stripped. */ - /* JNI entry points need to be bundled even when linking statically */ - app_dummy(); -} +/* + * fg_main_android.c + * + * The Android-specific windows message processing methods. + * + * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved. + * Written by Pawel W. Olszta, + * Copied for Platform code by Evan Felix + * Copyright (C) 2012 Sylvain Beucler + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include "fg_internal.h" +#include "egl/fg_window_egl.h" + +#include +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FreeGLUT", __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "FreeGLUT", __VA_ARGS__)) +#include +#include + +extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify); +extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify); +extern void fgPlatformFullScreenToggle( SFG_Window *win ); +extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y ); +extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height ); +extern void fgPlatformPushWindow( SFG_Window *window ); +extern void fgPlatformPopWindow( SFG_Window *window ); +extern void fgPlatformHideWindow( SFG_Window *window ); +extern void fgPlatformIconifyWindow( SFG_Window *window ); +extern void fgPlatformShowWindow( SFG_Window *window ); + +static struct touchscreen touchscreen; +static unsigned char key_a2fg[256]; + +/* Cf. http://developer.android.com/reference/android/view/KeyEvent.html */ +/* These codes are missing in */ +/* Don't convert to enum, since it may conflict with future version of + that */ +#define AKEYCODE_FORWARD_DEL 112 +#define AKEYCODE_CTRL_LEFT 113 +#define AKEYCODE_CTRL_RIGHT 114 +#define AKEYCODE_MOVE_HOME 122 +#define AKEYCODE_MOVE_END 123 +#define AKEYCODE_INSERT 124 +#define AKEYCODE_ESCAPE 127 +#define AKEYCODE_F1 131 +#define AKEYCODE_F2 132 +#define AKEYCODE_F3 133 +#define AKEYCODE_F4 134 +#define AKEYCODE_F5 135 +#define AKEYCODE_F6 136 +#define AKEYCODE_F7 137 +#define AKEYCODE_F8 138 +#define AKEYCODE_F9 139 +#define AKEYCODE_F10 140 +#define AKEYCODE_F11 141 +#define AKEYCODE_F12 142 + +#define EVENT_HANDLED 1 +#define EVENT_NOT_HANDLED 0 + +/** + * Initialize Android keycode to GLUT keycode mapping + */ +static void key_init() { + memset(key_a2fg, 0, sizeof(key_a2fg)); + + key_a2fg[AKEYCODE_F1] = GLUT_KEY_F1; + key_a2fg[AKEYCODE_F2] = GLUT_KEY_F2; + key_a2fg[AKEYCODE_F3] = GLUT_KEY_F3; + key_a2fg[AKEYCODE_F4] = GLUT_KEY_F4; + key_a2fg[AKEYCODE_F5] = GLUT_KEY_F5; + key_a2fg[AKEYCODE_F6] = GLUT_KEY_F6; + key_a2fg[AKEYCODE_F7] = GLUT_KEY_F7; + key_a2fg[AKEYCODE_F8] = GLUT_KEY_F8; + key_a2fg[AKEYCODE_F9] = GLUT_KEY_F9; + key_a2fg[AKEYCODE_F10] = GLUT_KEY_F10; + key_a2fg[AKEYCODE_F11] = GLUT_KEY_F11; + key_a2fg[AKEYCODE_F12] = GLUT_KEY_F12; + + key_a2fg[AKEYCODE_PAGE_UP] = GLUT_KEY_PAGE_UP; + key_a2fg[AKEYCODE_PAGE_DOWN] = GLUT_KEY_PAGE_DOWN; + key_a2fg[AKEYCODE_MOVE_HOME] = GLUT_KEY_HOME; + key_a2fg[AKEYCODE_MOVE_END] = GLUT_KEY_END; + key_a2fg[AKEYCODE_INSERT] = GLUT_KEY_INSERT; + + key_a2fg[AKEYCODE_DPAD_UP] = GLUT_KEY_UP; + key_a2fg[AKEYCODE_DPAD_DOWN] = GLUT_KEY_DOWN; + key_a2fg[AKEYCODE_DPAD_LEFT] = GLUT_KEY_LEFT; + key_a2fg[AKEYCODE_DPAD_RIGHT] = GLUT_KEY_RIGHT; + + key_a2fg[AKEYCODE_ALT_LEFT] = GLUT_KEY_ALT_L; + key_a2fg[AKEYCODE_ALT_RIGHT] = GLUT_KEY_ALT_R; + key_a2fg[AKEYCODE_SHIFT_LEFT] = GLUT_KEY_SHIFT_L; + key_a2fg[AKEYCODE_SHIFT_RIGHT] = GLUT_KEY_SHIFT_R; + key_a2fg[AKEYCODE_CTRL_LEFT] = GLUT_KEY_CTRL_L; + key_a2fg[AKEYCODE_CTRL_RIGHT] = GLUT_KEY_CTRL_R; +} + +/** + * Convert an Android key event to ASCII. + */ +static unsigned char key_ascii(struct android_app* app, AInputEvent* event) { + int32_t code = AKeyEvent_getKeyCode(event); + + /* Handle a few special cases: */ + switch (code) { + case AKEYCODE_DEL: + return 8; + case AKEYCODE_FORWARD_DEL: + return 127; + case AKEYCODE_ESCAPE: + return 27; + } + + /* Get usable JNI context */ + JNIEnv* env = app->activity->env; + JavaVM* vm = app->activity->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + + jclass KeyEventClass = (*env)->FindClass(env, "android/view/KeyEvent"); + jmethodID KeyEventConstructor = (*env)->GetMethodID(env, KeyEventClass, "", "(II)V"); + jobject keyEvent = (*env)->NewObject(env, KeyEventClass, KeyEventConstructor, + AKeyEvent_getAction(event), AKeyEvent_getKeyCode(event)); + jmethodID KeyEvent_getUnicodeChar = (*env)->GetMethodID(env, KeyEventClass, "getUnicodeChar", "(I)I"); + int ascii = (*env)->CallIntMethod(env, keyEvent, KeyEvent_getUnicodeChar, AKeyEvent_getMetaState(event)); + + /* LOGI("getUnicodeChar(%d) = %d ('%c')", AKeyEvent_getKeyCode(event), ascii, ascii); */ + (*vm)->DetachCurrentThread(vm); + + return ascii; +} + +unsigned long fgPlatformSystemTime ( void ) +{ + struct timeval now; + gettimeofday( &now, NULL ); + return now.tv_usec/1000 + now.tv_sec*1000; +} + +/* + * Does the magic required to relinquish the CPU until something interesting + * happens. + */ +void fgPlatformSleepForEvents( long msec ) +{ + /* Android's NativeActivity relies on a Looper/ALooper object to + notify about events. The Looper object is plugged on two + internal pipe(2)s to detect system and input events. Sadly you + can only ask the Looper for an event, not just ask whether + there is a pending event (and process it later). Consequently, + short of redesigning NativeActivity, we cannot + SleepForEvents. */ +} + +/** + * Process the next input event. + */ +int32_t handle_input(struct android_app* app, AInputEvent* event) { + SFG_Window* window = fgWindowByHandle(app->window); + if (window == NULL) + return EVENT_NOT_HANDLED; + + /* FIXME: in Android, when a key is repeated, down + and up events happen most often at the exact same time. This + makes it impossible to animate based on key press time. */ + /* e.g. down/up/wait/down/up rather than down/wait/down/wait/up */ + /* This looks like a bug in the Android virtual keyboard system :/ + Real buttons such as the Back button appear to work correctly + (series of down events with proper getRepeatCount value). */ + + if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { + /* LOGI("action: %d", AKeyEvent_getAction(event)); */ + /* LOGI("keycode: %d", code); */ + int32_t code = AKeyEvent_getKeyCode(event); + + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) { + int32_t keypress = 0; + unsigned char ascii = 0; + if ((keypress = key_a2fg[code]) && FETCH_WCB(*window, Special)) { + INVOKE_WCB(*window, Special, (keypress, window->State.MouseX, window->State.MouseY)); + return EVENT_HANDLED; + } else if ((ascii = key_ascii(app, event)) && FETCH_WCB(*window, Keyboard)) { + INVOKE_WCB(*window, Keyboard, (ascii, window->State.MouseX, window->State.MouseY)); + return EVENT_HANDLED; + } + } + else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP) { + int32_t keypress = 0; + unsigned char ascii = 0; + if ((keypress = key_a2fg[code]) && FETCH_WCB(*window, Special)) { + INVOKE_WCB(*window, SpecialUp, (keypress, window->State.MouseX, window->State.MouseY)); + return EVENT_HANDLED; + } else if ((ascii = key_ascii(app, event)) && FETCH_WCB(*window, Keyboard)) { + INVOKE_WCB(*window, KeyboardUp, (ascii, window->State.MouseX, window->State.MouseY)); + return EVENT_HANDLED; + } + } + } + + int32_t source = AInputEvent_getSource(event); + if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION + && source == AINPUT_SOURCE_TOUCHSCREEN) { + int32_t action = AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK; + /* Pointer ID for clicks */ + int32_t pidx = AMotionEvent_getAction(event) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + /* TODO: Handle multi-touch; also handle multiple sources/devices */ + /* cf. http://sourceforge.net/mailarchive/forum.php?thread_name=20120518071314.GA28061%40perso.beuc.net&forum_name=freeglut-developer */ + if (0) { + LOGI("motion action=%d index=%d source=%d", action, pidx, source); + int count = AMotionEvent_getPointerCount(event); + int i; + for (i = 0; i < count; i++) { + LOGI("multi(%d): %.01f,%.01f", + AMotionEvent_getPointerId(event, i), + AMotionEvent_getX(event, i), AMotionEvent_getY(event, i)); + } + } + float x = AMotionEvent_getX(event, 0); + float y = AMotionEvent_getY(event, 0); + + /* Virtual arrows PAD */ + /* Don't interfere with existing mouse move event */ + if (!touchscreen.in_mmotion) { + struct vpad_state prev_vpad = touchscreen.vpad; + touchscreen.vpad.left = touchscreen.vpad.right + = touchscreen.vpad.up = touchscreen.vpad.down = false; + + /* int32_t width = ANativeWindow_getWidth(window->Window.Handle); */ + int32_t height = ANativeWindow_getHeight(window->Window.Handle); + if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_MOVE) { + if ((x > 0 && x < 100) && (y > (height - 100) && y < height)) + touchscreen.vpad.left = true; + if ((x > 200 && x < 300) && (y > (height - 100) && y < height)) + touchscreen.vpad.right = true; + if ((x > 100 && x < 200) && (y > (height - 100) && y < height)) + touchscreen.vpad.down = true; + if ((x > 100 && x < 200) && (y > (height - 200) && y < (height - 100))) + touchscreen.vpad.up = true; + } + if (action == AMOTION_EVENT_ACTION_DOWN && + (touchscreen.vpad.left || touchscreen.vpad.right || touchscreen.vpad.down || touchscreen.vpad.up)) + touchscreen.vpad.on = true; + if (action == AMOTION_EVENT_ACTION_UP) + touchscreen.vpad.on = false; + if (prev_vpad.left != touchscreen.vpad.left + || prev_vpad.right != touchscreen.vpad.right + || prev_vpad.up != touchscreen.vpad.up + || prev_vpad.down != touchscreen.vpad.down + || prev_vpad.on != touchscreen.vpad.on) { + if (FETCH_WCB(*window, Special)) { + if (prev_vpad.left == false && touchscreen.vpad.left == true) + INVOKE_WCB(*window, Special, (GLUT_KEY_LEFT, x, y)); + else if (prev_vpad.right == false && touchscreen.vpad.right == true) + INVOKE_WCB(*window, Special, (GLUT_KEY_RIGHT, x, y)); + else if (prev_vpad.up == false && touchscreen.vpad.up == true) + INVOKE_WCB(*window, Special, (GLUT_KEY_UP, x, y)); + else if (prev_vpad.down == false && touchscreen.vpad.down == true) + INVOKE_WCB(*window, Special, (GLUT_KEY_DOWN, x, y)); + } + if (FETCH_WCB(*window, SpecialUp)) { + if (prev_vpad.left == true && touchscreen.vpad.left == false) + INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_LEFT, x, y)); + if (prev_vpad.right == true && touchscreen.vpad.right == false) + INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_RIGHT, x, y)); + if (prev_vpad.up == true && touchscreen.vpad.up == false) + INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_UP, x, y)); + if (prev_vpad.down == true && touchscreen.vpad.down == false) + INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_DOWN, x, y)); + } + return EVENT_HANDLED; + } + } + + /* Normal mouse events */ + if (!touchscreen.vpad.on) { + window->State.MouseX = x; + window->State.MouseY = y; + if (action == AMOTION_EVENT_ACTION_DOWN && FETCH_WCB(*window, Mouse)) { + touchscreen.in_mmotion = true; + INVOKE_WCB(*window, Mouse, (GLUT_LEFT_BUTTON, GLUT_DOWN, x, y)); + } else if (action == AMOTION_EVENT_ACTION_UP && FETCH_WCB(*window, Mouse)) { + touchscreen.in_mmotion = false; + INVOKE_WCB(*window, Mouse, (GLUT_LEFT_BUTTON, GLUT_UP, x, y)); + } else if (action == AMOTION_EVENT_ACTION_MOVE && FETCH_WCB(*window, Motion)) { + INVOKE_WCB(*window, Motion, (x, y)); + } + } + + return EVENT_HANDLED; + } + + /* Let Android handle other events (e.g. Back and Menu buttons) */ + return EVENT_NOT_HANDLED; +} + +/** + * Process the next main command. + */ +void handle_cmd(struct android_app* app, int32_t cmd) { + SFG_Window* window = fgWindowByHandle(app->window); /* may be NULL */ + switch (cmd) { + /* App life cycle, in that order: */ + case APP_CMD_START: + LOGI("handle_cmd: APP_CMD_START"); + break; + case APP_CMD_RESUME: + LOGI("handle_cmd: APP_CMD_RESUME"); + /* Cf. fgPlatformProcessSingleEvent */ + break; + case APP_CMD_INIT_WINDOW: /* surfaceCreated */ + /* The window is being shown, get it ready. */ + LOGI("handle_cmd: APP_CMD_INIT_WINDOW %p", app->window); + fgDisplay.pDisplay.single_native_window = app->window; + window->State.WorkMask |= GLUT_INIT_WORK; + /* start|resume: glPlatformOpenWindow was waiting for Handle to be + defined and will now continue processing */ + break; + case APP_CMD_GAINED_FOCUS: + LOGI("handle_cmd: APP_CMD_GAINED_FOCUS"); + break; + case APP_CMD_WINDOW_RESIZED: + LOGI("handle_cmd: APP_CMD_WINDOW_RESIZED"); + if (window->Window.pContext.egl.Surface != EGL_NO_SURFACE) + /* Make ProcessSingleEvent detect the new size, only available + after the next SwapBuffer */ + glutPostRedisplay(); + break; + + case APP_CMD_SAVE_STATE: /* onSaveInstanceState */ + /* The system has asked us to save our current state, when it + pauses the application without destroying it right after. */ + app->savedState = strdup("Detect me as non-NULL on next android_main"); + app->savedStateSize = strlen(app->savedState) + 1; + LOGI("handle_cmd: APP_CMD_SAVE_STATE"); + break; + case APP_CMD_PAUSE: + LOGI("handle_cmd: APP_CMD_PAUSE"); + /* Cf. fgPlatformProcessSingleEvent */ + break; + case APP_CMD_LOST_FOCUS: + LOGI("handle_cmd: APP_CMD_LOST_FOCUS"); + break; + case APP_CMD_TERM_WINDOW: /* surfaceDestroyed */ + /* The application is being hidden, but may be restored */ + LOGI("handle_cmd: APP_CMD_TERM_WINDOW"); + fghPlatformCloseWindowEGL(window); + fgDisplay.pDisplay.single_native_window = NULL; + break; + case APP_CMD_STOP: + LOGI("handle_cmd: APP_CMD_STOP"); + break; + case APP_CMD_DESTROY: /* Activity.onDestroy */ + LOGI("handle_cmd: APP_CMD_DESTROY"); + /* User closed the application for good, let's kill the window */ + { + /* Can't use fgWindowByHandle as app->window is NULL */ + SFG_Window* window = fgStructure.CurrentWindow; + if (window != NULL) { + fgDestroyWindow(window); + } else { + LOGI("APP_CMD_DESTROY: No current window"); + } + } + /* glue has already set android_app->destroyRequested=1 */ + break; + + case APP_CMD_CONFIG_CHANGED: + /* Handle rotation / orientation change */ + LOGI("handle_cmd: APP_CMD_CONFIG_CHANGED"); + break; + case APP_CMD_LOW_MEMORY: + LOGI("handle_cmd: APP_CMD_LOW_MEMORY"); + break; + default: + LOGI("handle_cmd: unhandled cmd=%d", cmd); + } +} + +void fgPlatformOpenWindow( SFG_Window* window, const char* title, + GLboolean positionUse, int x, int y, + GLboolean sizeUse, int w, int h, + GLboolean gameMode, GLboolean isSubWindow ); + +void fgPlatformProcessSingleEvent ( void ) +{ + /* When the screen is resized, the window handle still points to the + old window until the next SwapBuffer, while it's crucial to set + the size (onShape) correctly before the next onDisplay callback. + Plus we don't know if the next SwapBuffer already occurred at the + time we process the event (e.g. during onDisplay). */ + /* So we do the check each time rather than on event. */ + /* Interestingly, on a Samsung Galaxy S/PowerVR SGX540 GPU/Android + 2.3, that next SwapBuffer is fake (but still necessary to get the + new size). */ + SFG_Window* window = fgStructure.CurrentWindow; + if (window != NULL && window->Window.Handle != NULL) { + int32_t width = ANativeWindow_getWidth(window->Window.Handle); + int32_t height = ANativeWindow_getHeight(window->Window.Handle); + fghOnReshapeNotify(width,height); + } + + /* Read pending event. */ + int ident; + int events; + struct android_poll_source* source; + /* This is called "ProcessSingleEvent" but this means we'd only + process ~60 (screen Hz) mouse events per second, plus other ports + are processing all events already. So let's process all pending + events. */ + /* if ((ident=ALooper_pollOnce(0, NULL, &events, (void**)&source)) >= 0) { */ + while ((ident=ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) { + /* Process this event. */ + if (source != NULL) { + source->process(source->app, source); + } + } + + /* If we're not in RESUME state, Android paused us, so wait */ + struct android_app* app = fgDisplay.pDisplay.app; + if (app->destroyRequested != 1 && app->activityState != APP_CMD_RESUME) { + INVOKE_WCB(*window, Pause, ()); + + int FOREVER = -1; + while (app->destroyRequested != 1 && (app->activityState != APP_CMD_RESUME)) { + if ((ident=ALooper_pollOnce(FOREVER, NULL, &events, (void**)&source)) >= 0) { + /* Process this event. */ + if (source != NULL) { + source->process(source->app, source); + } + } + } + /* Coming back from a pause: */ + /* - Recreate window context and surface */ + /* - Call user-defined hook to restore resources (textures...) */ + /* - Exit pause loop */ + if (app->destroyRequested != 1) { + /* Android is full-screen only, simplified call.. */ + /* Ideally we'd have a fgPlatformReopenWindow() */ + /* If we're hidden by a non-fullscreen or translucent activity, + we'll be paused but not stopped, and keep the current + surface; in which case fgPlatformOpenWindow will no-op. */ + fgPlatformOpenWindow(window, "", GL_FALSE, 0, 0, GL_FALSE, 0, 0, GL_FALSE, GL_FALSE); + + /* TODO: should there be a whole GLUT_INIT_WORK forced? probably... + * Could queue that up in APP_CMD_TERM_WINDOW handler, but it'll + * be not possible to ensure InitContext CB gets called before + * Resume CB like that.. so maybe just force calling initContext CB + * here is best. Or we could force work on the window in question.. + * 1) save old work mask, 2) set mask to init only, 3) call fgPlatformProcessWork directly + * 4) set work mask back to the one saved in step 1. + */ + if (!FETCH_WCB(*window, InitContext)) + fgWarning("Resuming application, but no callback to reload context resources (glutInitContextFunc)"); + } + + INVOKE_WCB(*window, Resume, ()); + } +} + +void fgPlatformMainLoopPreliminaryWork ( void ) +{ + LOGI("fgPlatformMainLoopPreliminaryWork\n"); + + key_init(); + + /* Make sure glue isn't stripped. */ + /* JNI entry points need to be bundled even when linking statically */ + app_dummy(); +} + + +/* Step through the work list */ +void fgPlatformProcessWork(SFG_Window *window) +{ + unsigned int workMask = window->State.WorkMask; + /* Now clear it so that any callback generated by the actions below can set work again */ + window->State.WorkMask = 0; + + /* This is before the first display callback: call a few callbacks to inform user of window size, position, etc + * we know this is before the first display callback of a window as for all windows GLUT_INIT_WORK is set when + * they are opened, and work is done before displaying in the mainloop. + */ + if (workMask & GLUT_INIT_WORK) + { + /* notify windowStatus/visibility */ + INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) ); + + /* Position callback, always at 0,0 */ + fghOnPositionNotify(window, 0, 0, GL_TRUE); + + /* Size gets notified on window creation with size detection in mainloop above + * XXX CHECK: does this messages happen too early like on windows, + * so client code cannot have registered a callback yet and the message + * is thus never received by client? + */ + + /* Call init context callback */ + INVOKE_WCB( *window, InitContext, ()); + + /* Lastly, check if we have a display callback, error out if not + * This is the right place to do it, as the redisplay will be + * next right after we exit this function, so there is no more + * opportunity for the user to register a callback for this window. + */ + if (!FETCH_WCB(*window, Display)) + fgError ( "ERROR: No display callback registered for window %d\n", window->ID ); + } + + if (workMask & GLUT_FULL_SCREEN_WORK) + fgPlatformFullScreenToggle( window ); + if (workMask & GLUT_POSITION_WORK) + fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos ); + if (workMask & GLUT_SIZE_WORK) + fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight ); + if (workMask & GLUT_ZORDER_WORK) + { + if (window->State.DesiredZOrder < 0) + fgPlatformPushWindow( window ); + else + fgPlatformPopWindow( window ); + } + + if (workMask & GLUT_VISIBILITY_WORK) + { + /* Visibility status of window should get updated in the window message handlers + * For now, none of these functions called below do anything, so don't worry + * about it + */ + SFG_Window *win = window; + switch (window->State.DesiredVisibility) + { + case DesireHiddenState: + fgPlatformHideWindow( window ); + break; + case DesireIconicState: + /* Call on top-level window */ + while (win->Parent) + win = win->Parent; + fgPlatformIconifyWindow( win ); + break; + case DesireNormalState: + fgPlatformShowWindow( window ); + break; + } + } +} + diff --git a/src/android/fg_structure_android.c b/src/android/fg_structure_android.c index e0a6eb8..b7acd20 100644 --- a/src/android/fg_structure_android.c +++ b/src/android/fg_structure_android.c @@ -33,6 +33,4 @@ void fgPlatformCreateWindow ( SFG_Window *window ) { fghPlatformCreateWindowEGL(window); - window->State.pWState.LastWidth = -1; - window->State.pWState.LastHeight = -1; } diff --git a/src/android/fg_window_android.c b/src/android/fg_window_android.c index 65f4d4d..370f1dc 100644 --- a/src/android/fg_window_android.c +++ b/src/android/fg_window_android.c @@ -32,8 +32,6 @@ #include "egl/fg_window_egl.h" #include -extern void fghRedrawWindow(SFG_Window *window); - /* * Opens a window. Requires a SFG_Window object created and attached * to the freeglut structure. OpenGL context is created here. @@ -96,14 +94,6 @@ void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height ) } /* - * A static helper function to execute display callback for a window - */ -void fgPlatformDisplayWindow ( SFG_Window *window ) -{ - fghRedrawWindow ( window ) ; -} - -/* * Closes a window, destroying the frame and OpenGL context */ void fgPlatformCloseWindow( SFG_Window* window ) @@ -113,25 +103,25 @@ void fgPlatformCloseWindow( SFG_Window* window ) } /* - * This function makes the current window visible + * This function makes the specified window visible */ -void fgPlatformGlutShowWindow( void ) +void fgPlatformShowWindow( void ) { - fprintf(stderr, "fgPlatformGlutShowWindow: STUB\n"); + fprintf(stderr, "fgPlatformShowWindow: STUB\n"); } /* - * This function hides the current window + * This function hides the specified window */ -void fgPlatformGlutHideWindow( void ) +void fgPlatformHideWindow( SFG_Window *window ) { - fprintf(stderr, "fgPlatformGlutHideWindow: STUB\n"); + fprintf(stderr, "fgPlatformHideWindow: STUB\n"); } /* - * Iconify the current window (top-level windows only) + * Iconify the specified window (top-level windows only) */ -void fgPlatformGlutIconifyWindow( void ) +void fgPlatformIconifyWindow( SFG_Window *window ) { fprintf(stderr, "fgPlatformGlutIconifyWindow: STUB\n"); } @@ -152,49 +142,33 @@ void fgPlatformGlutSetIconTitle( const char* title ) fprintf(stderr, "fgPlatformGlutSetIconTitle: STUB\n");} /* - * Change the current window's position - */ -void fgPlatformGlutPositionWindow( int x, int y ) -{ - fprintf(stderr, "fgPlatformGlutPositionWindow: STUB\n"); -} - -/* - * Lowers the current window (by Z order change) - */ -void fgPlatformGlutPushWindow( void ) -{ - fprintf(stderr, "fgPlatformGlutPushWindow: STUB\n"); -} - -/* - * Raises the current window (by Z order change) + * Change the specified window's position */ -void fgPlatformGlutPopWindow( void ) +void fgPlatformPositionWindow( SFG_Window *window, int x, int y ) { - fprintf(stderr, "fgPlatformGlutPopWindow: STUB\n"); + fprintf(stderr, "fgPlatformPositionWindow: STUB\n"); } /* - * Resize the current window so that it fits the whole screen + * Lowers the specified window (by Z order change) */ -void fgPlatformGlutFullScreen( SFG_Window *win ) +void fgPlatformPushWindow( SFG_Window *window ) { - fprintf(stderr, "fgPlatformGlutFullScreen: STUB\n"); + fprintf(stderr, "fgPlatformPushWindow: STUB\n"); } /* - * If we are fullscreen, resize the current window back to its original size + * Raises the specified window (by Z order change) */ -void fgPlatformGlutLeaveFullScreen( SFG_Window *win ) +void fgPlatformPopWindow( SFG_Window *window ) { - fprintf(stderr, "fgPlatformGlutLeaveFullScreen: STUB\n"); + fprintf(stderr, "fgPlatformPopWindow: STUB\n"); } /* * Toggle the window's full screen state. */ -void fgPlatformGlutFullScreenToggle( SFG_Window *win ) +void fgPlatformFullScreenToggle( SFG_Window *win ) { - fprintf(stderr, "fgPlatformGlutFullScreenToggle: STUB\n"); + fprintf(stderr, "fgPlatformFullScreenToggle: STUB\n"); } diff --git a/src/fg_gamemode.c b/src/fg_gamemode.c index d210a88..4b49dcc 100644 --- a/src/fg_gamemode.c +++ b/src/fg_gamemode.c @@ -28,15 +28,6 @@ #include #include "fg_internal.h" -/* - * TODO BEFORE THE STABLE RELEASE: - * - * glutGameModeString() -- missing - * glutEnterGameMode() -- X11 version - * glutLeaveGameMode() -- is that correct? - * glutGameModeGet() -- is that correct? - */ - /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */ extern void fgPlatformRememberState( void ); diff --git a/src/fg_internal.h b/src/fg_internal.h index 2de99b1..2d2ab0b 100644 --- a/src/fg_internal.h +++ b/src/fg_internal.h @@ -382,19 +382,86 @@ struct tagSFG_Context }; +/* + * Bitmasks indicating the different kinds of + * actions that can be scheduled for a window. + */ +#define GLUT_INIT_WORK (1<<0) +#define GLUT_VISIBILITY_WORK (1<<1) +#define GLUT_POSITION_WORK (1<<2) +#define GLUT_SIZE_WORK (1<<3) +#define GLUT_ZORDER_WORK (1<<4) +#define GLUT_FULL_SCREEN_WORK (1<<5) + +/* + * An enumeration containing the state of the GLUT execution: + * initializing, running, or stopping + */ +typedef enum +{ + DesireHiddenState, + DesireIconicState, + DesireNormalState +} fgDesiredVisibility ; + +/* + * There is considerable confusion about the "right thing to + * do" concerning window size and position. GLUT itself is + * not consistent between Windows and UNIX/X11; since + * platform independence is a virtue for "freeglut", we + * decided to break with GLUT's behaviour. + * + * Under UNIX/X11, it is apparently not possible to get the + * window border sizes in order to subtract them off the + * window's initial position until some time after the window + * has been created. Therefore we decided on the following + * behaviour, both under Windows and under UNIX/X11: + * - When you create a window with position (x,y) and size + * (w,h), the upper left hand corner of the outside of the + * window is at (x,y) and the size of the drawable area is + * (w,h). + * - When you query the size and position of the window--as + * is happening here for Windows--"freeglut" will return + * the size of the drawable area--the (w,h) that you + * specified when you created the window--and the coordinates + * of the upper left hand corner of the drawable area, i.e. + * of the client rect--which is NOT the (x,y) you specified. + */ typedef struct tagSFG_WindowState SFG_WindowState; -struct tagSFG_WindowState +struct tagSFG_WindowState /* as per notes above, sizes always refer to the client area (thus without the window decorations) */ { - /* Note that on Windows, sizes always refer to the client area, thus without the window decorations */ + /* window state - size, position, look */ + int Xpos; /* Window's top-left of client area, X-coordinate */ + int Ypos; /* Window's top-left of client area, Y-coordinate */ int Width; /* Window's width in pixels */ int Height; /* The same about the height */ + GLboolean Visible; /* Is the window visible now? Not using fgVisibilityState as we only care if visible or not */ + int Cursor; /* The currently selected cursor style */ + GLboolean IsFullscreen; /* is the window fullscreen? */ + + /* FreeGLUT operations are deferred, that is, window moving, resizing, + * Z-order changing, making full screen or not do not happen immediately + * upon the user's request, but only in the next iteration of the main + * loop, before the display callback is called. This allows multiple + * reshape, position, etc requests to be combined into one and is + * compatible with the way GLUT does things. Callbacks get triggered + * based on the feedback/messages/notifications from the window manager. + * Below here we define what work should be done, as well as the relevant + * parameters for this work. + */ + unsigned int WorkMask; /* work (resize, etc) to be done on the window */ + int DesiredXpos; /* desired X location */ + int DesiredYpos; /* desired Y location */ + int DesiredWidth; /* desired window width */ + int DesiredHeight; /* desired window height */ + int DesiredZOrder; /* desired window Z Order position */ + fgDesiredVisibility DesiredVisibility;/* desired visibility (hidden, iconic, shown/normal) */ + SFG_PlatformWindowState pWState; /* Window width/height (X11) or rectangle/style (Windows) from before a resize, and other stuff only needed on specific platforms */ GLboolean Redisplay; /* Do we have to redisplay? */ - GLboolean Visible; /* Is the window visible now */ - int Cursor; /* The currently selected cursor */ long JoystickPollRate; /* The joystick polling rate */ fg_time_t JoystickLastPoll; /* When the last poll happened */ @@ -402,13 +469,6 @@ struct tagSFG_WindowState int MouseX, MouseY; /* The most recent mouse position */ GLboolean IgnoreKeyRepeat; /* Whether to ignore key repeat. */ - GLboolean KeyRepeating; /* Currently in repeat mode */ - - GLboolean NeedToResize; /* Do we need to resize the window? */ - - GLboolean IsFullscreen; /* is the window fullscreen? */ - - GLboolean NeedToInitContext; /* are OpenGL context/resources loaded? */ GLboolean VisualizeNormals; /* When drawing objects, draw vectors representing the normals as well? */ }; diff --git a/src/fg_main.c b/src/fg_main.c index 2bc8435..82f708c 100644 --- a/src/fg_main.c +++ b/src/fg_main.c @@ -54,8 +54,7 @@ # define MIN(a,b) (((a)<(b)) ? (a) : (b)) #endif -extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height ); -extern void fgPlatformDisplayWindow ( SFG_Window *window ); +extern void fgPlatformProcessWork ( SFG_Window *window ); extern fg_time_t fgPlatformSystemTime ( void ); extern void fgPlatformSleepForEvents( fg_time_t msec ); extern void fgPlatformProcessSingleEvent ( void ); @@ -66,69 +65,118 @@ extern void fgPlatformMainLoopPreliminaryWork ( void ); /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */ -static void fghReshapeWindow ( SFG_Window *window, int width, int height ) +void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify) { - SFG_Window *current_window = fgStructure.CurrentWindow; + GLboolean notify = GL_FALSE; - freeglut_return_if_fail( window != NULL ); + if( width != window->State.Width || + height != window->State.Height ) + { + window->State.Width = width; + window->State.Height = height; - fgPlatformReshapeWindow ( window, width, height ); + notify = GL_TRUE; + } - /* - * Force a window redraw. In Windows at least this is only a partial - * solution: if the window is increasing in size in either dimension, - * the already-drawn part does not get drawn again and things look funny. - * But without this we get this bad behaviour whenever we resize the - * window. - * DN: Hmm.. the above sounds like a concern only in single buffered mode... - */ - window->State.Redisplay = GL_TRUE; + if (notify || forceNotify) + { + SFG_Window *saved_window = fgStructure.CurrentWindow; - if( window->IsMenu ) - fgSetWindow( current_window ); + INVOKE_WCB( *window, Reshape, ( width, height ) ); + + /* + * Force a window redraw. In Windows at least this is only a partial + * solution: if the window is increasing in size in either dimension, + * the already-drawn part does not get drawn again and things look funny. + * But without this we get this bad behaviour whenever we resize the + * window. + * DN: Hmm.. the above sounds like a concern only in single buffered mode... + */ + glutPostRedisplay( ); + if( window->IsMenu ) + fgSetWindow( saved_window ); + } +} + +void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify) +{ + GLboolean notify = GL_FALSE; + + if( x != window->State.Xpos || + y != window->State.Ypos ) + { + window->State.Xpos = x; + window->State.Ypos = y; + + notify = GL_TRUE; + } + + if (notify || forceNotify) + { + SFG_Window *saved_window = fgStructure.CurrentWindow; + INVOKE_WCB( *window, Position, ( x, y ) ); + fgSetWindow( saved_window ); + } } /* * Calls a window's redraw method. This is used when - * a redraw is forced by the incoming window messages. + * a redraw is forced by the incoming window messages, + * or if a redisplay is otherwise pending. + * this is lean and mean without checks as it is + * currently only called from fghcbDisplayWindow which + * only calls this if the window is visible and needs + * a redisplay. + * Note that the fgSetWindow call on Windows makes the + * right device context current on windows, allowing + * direct drawing without BeginPaint/EndPaint in the + * WM_PAINT handler. */ void fghRedrawWindow ( SFG_Window *window ) { SFG_Window *current_window = fgStructure.CurrentWindow; - freeglut_return_if_fail( window ); + fgSetWindow( window ); + INVOKE_WCB( *window, Display, ( ) ); - if( window->State.NeedToInitContext ) { - INVOKE_WCB( *window, InitContext, ()); - window->State.NeedToInitContext = GL_FALSE; - } + fgSetWindow( current_window ); +} - freeglut_return_if_fail( FETCH_WCB ( *window, Display ) ); +void fghRedrawWindowAndChildren ( SFG_Window *window ) +{ + SFG_Window* child; - window->State.Redisplay = GL_FALSE; + fghRedrawWindow(window); - freeglut_return_if_fail( window->State.Visible ); + for( child = ( SFG_Window * )window->Children.First; + child; + child = ( SFG_Window * )child->Node.Next ) + { + fghRedrawWindowAndChildren(child); + } +} - fgSetWindow( window ); - if( window->State.NeedToResize ) - { - /* Set need to resize to false before calling fghReshapeWindow, otherwise - in the case the user's reshape callback calls glutReshapeWindow, - his request would get canceled after fghReshapeWindow gets called. - */ - window->State.NeedToResize = GL_FALSE; +static void fghcbProcessWork( SFG_Window *window, + SFG_Enumerator *enumerator ) +{ + if( window->State.WorkMask ) + fgPlatformProcessWork ( window ); - fghReshapeWindow( - window, - window->State.Width, - window->State.Height - ); - } + fgEnumSubWindows( window, fghcbProcessWork, enumerator ); +} - INVOKE_WCB( *window, Display, ( ) ); +/* + * Make all windows process their work list + */ +static void fghProcessWork( void ) +{ + SFG_Enumerator enumerator; - fgSetWindow( current_window ); + enumerator.found = GL_FALSE; + enumerator.data = NULL; + + fgEnumWindows( fghcbProcessWork, &enumerator ); } @@ -139,7 +187,7 @@ static void fghcbDisplayWindow( SFG_Window *window, window->State.Visible ) { window->State.Redisplay = GL_FALSE; - fgPlatformDisplayWindow ( window ); + fghRedrawWindow ( window ); } fgEnumSubWindows( window, fghcbDisplayWindow, enumerator ); @@ -376,12 +424,18 @@ static void fghSleepForEvents( void ) */ void FGAPIENTRY glutMainLoopEvent( void ) { + /* Process input */ fgPlatformProcessSingleEvent (); if( fgState.Timers.First ) fghCheckTimers( ); if (fgState.NumActiveJoysticks>0) /* If zero, don't poll joysticks */ fghCheckJoystickPolls( ); + + /* Perform work on the window (position, reshape, etc) */ + fghProcessWork( ); + + /* Display */ fghDisplayAll( ); fgCloseWindows( ); diff --git a/src/fg_main.h b/src/fg_main.h deleted file mode 100644 index 175032c..0000000 --- a/src/fg_main.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * fg_main.h - * - * The windows message processing methods. - * - * Copyright (C) 2012 Sylvain Beucler - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef __FG_MAIN_H__ -#define __FG_MAIN_H__ - -#include -#include "fg_internal.h" - -extern void fghRedrawWindow(SFG_Window *window); - -#endif diff --git a/src/fg_window.c b/src/fg_window.c index 711defb..1a65c1f 100644 --- a/src/fg_window.c +++ b/src/fg_window.c @@ -56,17 +56,8 @@ extern void fgPlatformOpenWindow( SFG_Window* window, const char* title, GLboolean sizeUse, int w, int h, GLboolean gameMode, GLboolean isSubWindow ); extern void fgPlatformCloseWindow( SFG_Window* window ); -extern void fgPlatformGlutShowWindow( void ); -extern void fgPlatformGlutHideWindow( void ); -extern void fgPlatformGlutIconifyWindow( void ); extern void fgPlatformGlutSetWindowTitle( const char* title ); extern void fgPlatformGlutSetIconTitle( const char* title ); -extern void fgPlatformGlutPositionWindow( int x, int y ); -extern void fgPlatformGlutPushWindow( void ); -extern void fgPlatformGlutPopWindow( void ); -extern void fgPlatformGlutFullScreen( SFG_Window *win ); -extern void fgPlatformGlutLeaveFullScreen( SFG_Window *win ); -extern void fgPlatformGlutFullScreenToggle( SFG_Window *win ); /* -- PRIVATE FUNCTIONS ---------------------------------------------------- */ @@ -152,7 +143,7 @@ void fgOpenWindow( SFG_Window* window, const char* title, fgInitGL2(); - window->State.NeedToInitContext = GL_TRUE; + window->State.WorkMask |= GLUT_INIT_WORK; } /* @@ -301,7 +292,8 @@ void FGAPIENTRY glutShowWindow( void ) FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutShowWindow" ); FREEGLUT_EXIT_IF_NO_WINDOW ( "glutShowWindow" ); - fgPlatformGlutShowWindow (); + fgStructure.CurrentWindow->State.WorkMask |= GLUT_VISIBILITY_WORK; + fgStructure.CurrentWindow->State.DesiredVisibility = DesireNormalState; fgStructure.CurrentWindow->State.Redisplay = GL_TRUE; } @@ -314,7 +306,8 @@ void FGAPIENTRY glutHideWindow( void ) FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutHideWindow" ); FREEGLUT_EXIT_IF_NO_WINDOW ( "glutHideWindow" ); - fgPlatformGlutHideWindow (); + fgStructure.CurrentWindow->State.WorkMask |= GLUT_VISIBILITY_WORK; + fgStructure.CurrentWindow->State.DesiredVisibility = DesireHiddenState; fgStructure.CurrentWindow->State.Redisplay = GL_FALSE; } @@ -327,7 +320,8 @@ void FGAPIENTRY glutIconifyWindow( void ) FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutIconifyWindow" ); FREEGLUT_EXIT_IF_NO_WINDOW ( "glutIconifyWindow" ); - fgPlatformGlutIconifyWindow (); + fgStructure.CurrentWindow->State.WorkMask |= GLUT_VISIBILITY_WORK; + fgStructure.CurrentWindow->State.DesiredVisibility = DesireIconicState; fgStructure.CurrentWindow->State.Redisplay = GL_FALSE; } @@ -373,9 +367,9 @@ void FGAPIENTRY glutReshapeWindow( int width, int height ) glutLeaveFullScreen(); } - fgStructure.CurrentWindow->State.NeedToResize = GL_TRUE; - fgStructure.CurrentWindow->State.Width = width ; - fgStructure.CurrentWindow->State.Height = height; + fgStructure.CurrentWindow->State.WorkMask |= GLUT_SIZE_WORK; + fgStructure.CurrentWindow->State.DesiredWidth = width ; + fgStructure.CurrentWindow->State.DesiredHeight = height; } /* @@ -392,7 +386,9 @@ void FGAPIENTRY glutPositionWindow( int x, int y ) glutLeaveFullScreen(); } - fgPlatformGlutPositionWindow ( x, y ); + fgStructure.CurrentWindow->State.WorkMask |= GLUT_POSITION_WORK; + fgStructure.CurrentWindow->State.DesiredXpos = x; + fgStructure.CurrentWindow->State.DesiredYpos = y; } /* @@ -403,7 +399,8 @@ void FGAPIENTRY glutPushWindow( void ) FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutPushWindow" ); FREEGLUT_EXIT_IF_NO_WINDOW ( "glutPushWindow" ); - fgPlatformGlutPushWindow (); + fgStructure.CurrentWindow->State.WorkMask |= GLUT_ZORDER_WORK; + fgStructure.CurrentWindow->State.DesiredZOrder = -1; } /* @@ -414,7 +411,8 @@ void FGAPIENTRY glutPopWindow( void ) FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutPopWindow" ); FREEGLUT_EXIT_IF_NO_WINDOW ( "glutPopWindow" ); - fgPlatformGlutPopWindow (); + fgStructure.CurrentWindow->State.WorkMask |= GLUT_ZORDER_WORK; + fgStructure.CurrentWindow->State.DesiredZOrder = 1; } /* @@ -441,12 +439,13 @@ void FGAPIENTRY glutFullScreen( void ) else if (fgStructure.GameModeWindow != NULL && fgStructure.GameModeWindow->ID==win->ID && win->State.IsFullscreen) { /* Ignore fullscreen call on GameMode window, those are always fullscreen already - * only exception is during first entering GameMode + * only exception is when first entering GameMode */ return; } - fgPlatformGlutFullScreen ( win ); + if (!win->State.IsFullscreen) + win->State.WorkMask |= GLUT_FULL_SCREEN_WORK; } /* @@ -454,14 +453,11 @@ void FGAPIENTRY glutFullScreen( void ) */ void FGAPIENTRY glutLeaveFullScreen( void ) { - SFG_Window *win; - FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutFullScreen" ); FREEGLUT_EXIT_IF_NO_WINDOW ( "glutFullScreen" ); - win = fgStructure.CurrentWindow; - - fgPlatformGlutLeaveFullScreen ( win ); + if (fgStructure.CurrentWindow->State.IsFullscreen) + fgStructure.CurrentWindow->State.WorkMask |= GLUT_FULL_SCREEN_WORK; } /* @@ -469,14 +465,10 @@ void FGAPIENTRY glutLeaveFullScreen( void ) */ void FGAPIENTRY glutFullScreenToggle( void ) { - SFG_Window *win; - FREEGLUT_EXIT_IF_NOT_INITIALISED ( "glutFullScreenToggle" ); FREEGLUT_EXIT_IF_NO_WINDOW ( "glutFullScreenToggle" ); - win = fgStructure.CurrentWindow; - - fgPlatformGlutFullScreenToggle ( win ); + fgStructure.CurrentWindow->State.WorkMask |= GLUT_FULL_SCREEN_WORK; } /* diff --git a/src/mswin/fg_internal_mswin.h b/src/mswin/fg_internal_mswin.h index 0e97c95..ee52382 100644 --- a/src/mswin/fg_internal_mswin.h +++ b/src/mswin/fg_internal_mswin.h @@ -97,7 +97,6 @@ struct tagSFG_PlatformWindowState BOOL OldMaximized; /* window maximized state - stored before the window is made fullscreen */ GLboolean MouseTracking; /* Needed for generating GLUT_ENTERED and GLUT_LEFT entry func callbacks on windows */ - GLboolean WindowFuncCalled; /* Indicate whether windowStatus/visibility func was notified that this window was created */ /* Need to store window titles to emulate * glutSetIconTitle/glutSetWindowTitle as Windows has only diff --git a/src/mswin/fg_main_mswin.c b/src/mswin/fg_main_mswin.c index 3cedeb3..b3062ef 100644 --- a/src/mswin/fg_main_mswin.c +++ b/src/mswin/fg_main_mswin.c @@ -28,8 +28,12 @@ #include #include "../fg_internal.h" - extern void fghRedrawWindow ( SFG_Window *window ); +extern void fghRedrawWindowAndChildren ( SFG_Window *window ); +extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify); +extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify); +extern void fghComputeWindowRectFromClientArea_QueryWindow( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside ); +extern void fghGetClientArea( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside ); extern void fgNewWGLCreateContext( SFG_Window* window ); extern GLboolean fgSetupPixelFormat( SFG_Window* window, GLboolean checkOnly, @@ -128,17 +132,14 @@ void fgPlatformProcessSingleEvent ( void ) -static void fghUpdateWindowStatus(SFG_Window *window, GLboolean visState) +static void fghPlatformOnWindowStatusNotify(SFG_Window *window, GLboolean visState, GLboolean forceNotify) { + GLboolean notify = GL_FALSE; SFG_Window* child; if (window->State.Visible != visState) { window->State.Visible = visState; - /* On win32 we only have two states, window displayed and window not displayed (iconified) - * We map these to GLUT_FULLY_RETAINED and GLUT_HIDDEN respectively. - */ - INVOKE_WCB( *window, WindowStatus, ( visState ? GLUT_FULLY_RETAINED:GLUT_HIDDEN ) ); /* If top level window (not a subwindow/child), and icon title text available, switch titles based on visibility state */ if (!window->Parent && window->State.pWState.IconTitle) @@ -150,24 +151,30 @@ static void fghUpdateWindowStatus(SFG_Window *window, GLboolean visState) /* not visible, set icon title */ SetWindowText( window->Window.Handle, window->State.pWState.IconTitle ); } + + notify = GL_TRUE; } - /* Also set visibility state for children */ + if (notify || forceNotify) + { + SFG_Window *saved_window = fgStructure.CurrentWindow; + + /* On win32 we only have two states, window displayed and window not displayed (iconified) + * We map these to GLUT_FULLY_RETAINED and GLUT_HIDDEN respectively. + */ + INVOKE_WCB( *window, WindowStatus, ( visState ? GLUT_FULLY_RETAINED:GLUT_HIDDEN ) ); + fgSetWindow( saved_window ); + } + + /* Also set windowStatus/visibility state for children */ for( child = ( SFG_Window * )window->Children.First; child; child = ( SFG_Window * )child->Node.Next ) { - fghUpdateWindowStatus(child, visState); + fghPlatformOnWindowStatusNotify(child, visState, GL_FALSE); /* No need to propagate forceNotify. Childs get this from their own INIT_WORK */ } } -void fghNotifyWindowStatus(SFG_Window *window) -{ - INVOKE_WCB( *window, WindowStatus, ( window->State.Visible?GLUT_FULLY_RETAINED:GLUT_HIDDEN ) ); - - /* Don't notify children, they get their own just before first time they're drawn */ -} - void fgPlatformMainLoopPreliminaryWork ( void ) { /* no-op */ @@ -447,21 +454,6 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR #endif } - window->State.NeedToResize = GL_TRUE; - /* if we used CW_USEDEFAULT (thats a negative value) for the size - * of the window, query the window now for the size at which it - * was created. - */ - if( ( window->State.Width < 0 ) || ( window->State.Height < 0 ) ) - { - SFG_Window *current_window = fgStructure.CurrentWindow; - - fgSetWindow( window ); - window->State.Width = glutGet( GLUT_WINDOW_WIDTH ); - window->State.Height = glutGet( GLUT_WINDOW_HEIGHT ); - fgSetWindow( current_window ); - } - ReleaseDC( window->Window.Handle, window->Window.pContext.Device ); #if defined(_WIN32_WCE) @@ -488,48 +480,71 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR /* Update visibility state of the window */ if (wParam==SIZE_MINIMIZED) - fghUpdateWindowStatus(window,GL_FALSE); + fghPlatformOnWindowStatusNotify(window,GL_FALSE,GL_FALSE); else if (wParam==SIZE_RESTORED && !window->State.Visible) - fghUpdateWindowStatus(window,GL_TRUE); + fghPlatformOnWindowStatusNotify(window,GL_TRUE,GL_FALSE); - /* Check window visible, we don't want to resize when the user or glutIconifyWindow minimized the window */ + /* Check window visible, we don't want do anything when we get a WM_SIZE because the user or glutIconifyWindow minimized the window */ if( window->State.Visible ) { - /* get old values first to compare to below */ - int width = window->State.Width, height=window->State.Height; + int width, height; #if defined(_WIN32_WCE) - window->State.Width = HIWORD(lParam); - window->State.Height = LOWORD(lParam); + width = HIWORD(lParam); + height = LOWORD(lParam); #else - window->State.Width = LOWORD(lParam); - window->State.Height = HIWORD(lParam); + width = LOWORD(lParam); + height = HIWORD(lParam); #endif /* defined(_WIN32_WCE) */ - if (width!=window->State.Width || height!=window->State.Height) - { - SFG_Window* saved_window = fgStructure.CurrentWindow; - - /* size changed, call reshape callback */ - INVOKE_WCB( *window, Reshape, ( width, height ) ); - glutPostRedisplay( ); - if( window->IsMenu ) - fgSetWindow( saved_window ); - } + /* Update state and call callback, if there was a change */ + fghOnReshapeNotify(window, width, height, GL_FALSE); } /* according to docs, should return 0 */ lRet = 0; break; - case WM_MOVE: + case WM_SIZING: { - SFG_Window* saved_window = fgStructure.CurrentWindow; - RECT windowRect; + /* User resize-dragging the window, call reshape callback and + * force redisplay so display keeps running during dragging. + * Screen still wont update when not moving the cursor though... + */ + /* PRECT prect = (PRECT) lParam; */ + RECT rect; + /* printf("WM_SIZING: nc-area: %i,%i\n",prect->right-prect->left,prect->bottom-prect->top); */ + /* Get client area, the rect in lParam is including non-client area. */ + fghGetClientArea(&rect,window,FALSE); + + /* We'll get a WM_SIZE as well, but as state has + * already been updated here, the fghOnReshapeNotify + * in the handler for that message doesn't do anything. + */ + fghOnReshapeNotify(window, rect.right-rect.left, rect.bottom-rect.top, GL_FALSE); + + /* Now directly call the drawing function to update + * window and window's childs. + * This mimics the WM_PAINT messages that are received during + * resizing. Note that we don't have a WM_MOVING handler + * as move-dragging doesn't generate WM_MOVE or WM_PAINT + * messages until the mouse is released. + */ + fghRedrawWindowAndChildren(window); + } + + /* according to docs, should return TRUE */ + lRet = TRUE; + break; + case WM_MOVE: + { /* Check window is minimized, we don't want to call the position callback when the user or glutIconifyWindow minimized the window */ if (!IsIconic(window->Window.Handle)) { - /* Get top-left of non-client area of window, matching coordinates of + RECT windowRect; + + /* lParam contains coordinates of top-left of client area. + * Get top-left of non-client area of window, matching coordinates of * glutInitPosition and glutPositionWindow, but not those of * glutGet(GLUT_WINDOW_X) and glutGet(GLUT_WINDOW_Y), which return * top-left of client area. @@ -548,8 +563,8 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR windowRect.top = topleft.y; } - INVOKE_WCB( *window, Position, ( windowRect.left, windowRect.top ) ); - fgSetWindow(saved_window); + /* Update state and call callback, if there was a change */ + fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_FALSE); } } @@ -642,12 +657,12 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR /* printf("WM_SHOWWINDOW, shown? %i, source: %i\n",wParam,lParam); */ if (wParam) { - fghUpdateWindowStatus(window, GL_TRUE); + fghPlatformOnWindowStatusNotify(window, GL_TRUE, GL_FALSE); window->State.Redisplay = GL_TRUE; } else { - fghUpdateWindowStatus(window, GL_FALSE); + fghPlatformOnWindowStatusNotify(window, GL_FALSE, GL_FALSE); window->State.Redisplay = GL_FALSE; } break; @@ -655,19 +670,21 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR case WM_PAINT: { RECT rect; - + + /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */ if (GetUpdateRect(hWnd,&rect,FALSE)) { - /* As per docs, upon receiving WM_PAINT, first check if the update region is not empty before you call BeginPaint */ + /* Dummy begin/end paint to validate rect that needs + * redrawing, then signal that a redisplay is needed. + * This allows us full control about when we do any + * redrawing, and is the same as what original GLUT + * does. + */ PAINTSTRUCT ps; - - /* Turn on the visibility in case it was turned off somehow */ - window->State.Visible = GL_TRUE; - - InvalidateRect( hWnd, NULL, GL_FALSE ); BeginPaint( hWnd, &ps ); - fghRedrawWindow( window ); EndPaint( hWnd, &ps ); + + window->State.Redisplay = GL_TRUE; } lRet = 0; /* As per docs, should return 0 */ } @@ -1108,3 +1125,238 @@ LRESULT CALLBACK fgPlatformWindowProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPAR return lRet; } + + +/* Step through the work list */ +void fgPlatformProcessWork(SFG_Window *window) +{ + unsigned int workMask = window->State.WorkMask; + /* Now clear it so that any callback generated by the actions below can set work again */ + window->State.WorkMask = 0; + + /* This is before the first display callback: call a few callbacks to inform user of window size, position, etc + * we know this is before the first display callback of a window as for all windows GLUT_INIT_WORK is set when + * they are opened, and work is done before displaying in the mainloop. + */ + if (workMask & GLUT_INIT_WORK) + { + RECT windowRect; + + /* Notify windowStatus/visibility */ + fghPlatformOnWindowStatusNotify(window, window->State.Visible, GL_TRUE); + + /* get and notify window's position */ + GetWindowRect(window->Window.Handle,&windowRect); + fghOnPositionNotify(window, windowRect.left, windowRect.top, GL_TRUE); + + /* get and notify window's size */ + GetClientRect(window->Window.Handle,&windowRect); + fghOnReshapeNotify(window, windowRect.right-windowRect.left, windowRect.bottom-windowRect.top, GL_TRUE); + + /* Call init context callback */ + INVOKE_WCB( *window, InitContext, ()); + + /* Lastly, check if we have a display callback, error out if not + * This is the right place to do it, as the redisplay will be + * next right after we exit this function, so there is no more + * opportunity for the user to register a callback for this window. + */ + if (!FETCH_WCB(*window, Display)) + fgError ( "ERROR: No display callback registered for window %d\n", window->ID ); + } + + /* On windows we can position, resize and change z order at the same time */ + if (workMask & (GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK|GLUT_FULL_SCREEN_WORK)) + { + UINT flags = SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER; + HWND insertAfter = HWND_TOP; + RECT clientRect; + +#if !defined(_WIN32_WCE) /* FIXME: what about WinCE */ + if (workMask & GLUT_FULL_SCREEN_WORK) + { + /* This asks us to toggle fullscreen mode */ + flags |= SWP_FRAMECHANGED; + + if (window->State.IsFullscreen) + { + /* If we are fullscreen, resize the current window back to its original size */ + /* printf("OldRect %i,%i to %i,%i\n",window->State.pWState.OldRect.left,window->State.pWState.OldRect.top,window->State.pWState.OldRect.right,window->State.pWState.OldRect.bottom); */ + + /* restore style of window before making it fullscreen */ + SetWindowLong(window->Window.Handle, GWL_STYLE, window->State.pWState.OldStyle); + SetWindowLong(window->Window.Handle, GWL_EXSTYLE, window->State.pWState.OldStyleEx); + + /* Then set up resize/reposition, unless user already queued up reshape/position work */ + if (!(workMask & GLUT_POSITION_WORK)) + { + workMask |= GLUT_POSITION_WORK; + window->State.DesiredXpos = window->State.pWState.OldRect.left; + window->State.DesiredYpos = window->State.pWState.OldRect.top; + } + if (!(workMask & GLUT_SIZE_WORK)) + { + workMask |= GLUT_SIZE_WORK; + window->State.DesiredWidth = window->State.pWState.OldRect.right - window->State.pWState.OldRect.left; + window->State.DesiredHeight = window->State.pWState.OldRect.bottom - window->State.pWState.OldRect.top; + } + + /* We'll finish off the fullscreen operation below after the other GLUT_POSITION_WORK|GLUT_SIZE_WORK|GLUT_ZORDER_WORK */ + } + else + { + /* we are currently not fullscreen, go to fullscreen: + * remove window decoration and then maximize + */ + RECT rect; + HMONITOR hMonitor; + MONITORINFO mi; + + /* save current window rect, style, exstyle and maximized state */ + window->State.pWState.OldMaximized = !!IsZoomed(window->Window.Handle); + if (window->State.pWState.OldMaximized) + /* We force the window into restored mode before going + * fullscreen because Windows doesn't seem to hide the + * taskbar if the window is in the maximized state. + */ + SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_RESTORE, 0); + + fghGetClientArea( &window->State.pWState.OldRect, window, GL_TRUE ); + window->State.pWState.OldStyle = GetWindowLong(window->Window.Handle, GWL_STYLE); + window->State.pWState.OldStyleEx = GetWindowLong(window->Window.Handle, GWL_EXSTYLE); + + /* remove decorations from style */ + SetWindowLong(window->Window.Handle, GWL_STYLE, + window->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME)); + SetWindowLong(window->Window.Handle, GWL_EXSTYLE, + window->State.pWState.OldStyleEx & ~(WS_EX_DLGMODALFRAME | + WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); + + /* For fullscreen mode, find the monitor that is covered the most + * by the window and get its rect as the resize target. + */ + GetWindowRect(window->Window.Handle, &rect); + hMonitor= MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST); + mi.cbSize = sizeof(mi); + GetMonitorInfo(hMonitor, &mi); + rect = mi.rcMonitor; + + /* then setup window resize, overwriting other work queued on the window */ + window->State.WorkMask |= GLUT_POSITION_WORK | GLUT_SIZE_WORK; + window->State.WorkMask &= ~GLUT_ZORDER_WORK; + window->State.DesiredXpos = rect.left; + window->State.DesiredYpos = rect.top; + window->State.DesiredWidth = rect.right - rect.left; + window->State.DesiredHeight = rect.bottom - rect.top; + } + } +#endif /*!defined(_WIN32_WCE) */ + + /* Now deal with normal position, reshape and z order requests (some might have been set when handling GLUT_FULLSCREEN_WORK above */ + { + /* get rect describing window's current position and size, + * in screen coordinates and in FreeGLUT format + * (size (right-left, bottom-top) is client area size, top and left + * are outside of window including decorations). + */ + fghGetClientArea( &clientRect, window, TRUE ); + + if (workMask & GLUT_POSITION_WORK) + { + flags &= ~SWP_NOMOVE; + + /* Move rect so that top-left is at requested position */ + /* This also automatically makes sure that child window requested coordinates are relative + * to top-left of parent's client area (needed input for SetWindowPos on child windows), + * so no need to further correct rect for child windows below (childs don't have decorations either). + */ + OffsetRect(&clientRect,window->State.DesiredXpos-clientRect.left,window->State.DesiredYpos-clientRect.top); + } + if (workMask & GLUT_SIZE_WORK) + { + flags &= ~SWP_NOSIZE; + + /* Note on maximizing behavior of Windows: the resize borders are off + * the screen such that the client area extends all the way from the + * leftmost corner to the rightmost corner to maximize screen real + * estate. A caption is still shown however to allow interaction with + * the window controls. This is default behavior of Windows that + * FreeGLUT sticks with. To alter, one would have to check if + * WS_MAXIMIZE style is set when a resize event is triggered, and + * then manually correct the windowRect to put the borders back on + * screen. + */ + + /* Set new size of window, WxH specify client area */ + clientRect.right = clientRect.left + window->State.DesiredWidth; + clientRect.bottom = clientRect.top + window->State.DesiredHeight; + } + if (workMask & GLUT_ZORDER_WORK) + { + flags &= ~SWP_NOZORDER; + + /* Could change this to push it down or up one window at a time with some + * more code using GetWindow with GW_HWNDPREV and GW_HWNDNEXT. + * What would be consistent with X11? Win32 GLUT does what we do here... + */ + if (window->State.DesiredZOrder < 0) + insertAfter = HWND_BOTTOM; + } + } + + /* Adjust for window decorations + * Child windows don't have decoration, so no need to correct + */ + if (!window->Parent) + /* get the window rect from this to feed to SetWindowPos, correct for window decorations */ + fghComputeWindowRectFromClientArea_QueryWindow(&clientRect,window,TRUE); + + /* Do the requested positioning, moving, and z order push/pop. */ + SetWindowPos( window->Window.Handle, + insertAfter, + clientRect.left, clientRect.top, + clientRect.right - clientRect.left, + clientRect.bottom- clientRect.top, + flags + ); + + /* Finish off the fullscreen operation we were doing, if any */ + if (workMask & GLUT_FULL_SCREEN_WORK) + { + if (window->State.IsFullscreen) + { + /* leaving fullscreen, restore maximized state, if any */ + if (window->State.pWState.OldMaximized) + SendMessage(window->Window.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + + window->State.IsFullscreen = GL_FALSE; + } + else + window->State.IsFullscreen = GL_TRUE; + } + } + + if (workMask & GLUT_VISIBILITY_WORK) + { + /* Visibility status of window gets updated in the WM_SHOWWINDOW and WM_SIZE handlers */ + int cmdShow = 0; + SFG_Window *win = window; + switch (window->State.DesiredVisibility) + { + case DesireHiddenState: + cmdShow = SW_HIDE; + break; + case DesireIconicState: + cmdShow = SW_MINIMIZE; + /* Call on top-level window */ + while (win->Parent) + win = win->Parent; + break; + case DesireNormalState: + cmdShow = SW_SHOW; + break; + } + + ShowWindow( win->Window.Handle, cmdShow ); + } +} \ No newline at end of file diff --git a/src/mswin/fg_menu_mswin.c b/src/mswin/fg_menu_mswin.c index 48e3940..2db1ea7 100644 --- a/src/mswin/fg_menu_mswin.c +++ b/src/mswin/fg_menu_mswin.c @@ -29,7 +29,7 @@ #include #include "../fg_internal.h" -extern void fghGetClientArea( RECT *clientRect, const SFG_Window *window ); +extern void fghGetClientArea( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside ); extern SFG_Window* fghWindowUnderCursor(SFG_Window *window); @@ -77,7 +77,7 @@ void fgPlatformCheckMenuDeactivate() */ POINT mouse_pos; RECT clientArea; - fghGetClientArea(&clientArea,menu->ParentWindow); + fghGetClientArea(&clientArea,menu->ParentWindow, FALSE); GetCursorPos(&mouse_pos); if ( !PtInRect( &clientArea, mouse_pos ) ) fgDeactivateMenu(menu->ParentWindow); diff --git a/src/mswin/fg_state_mswin.c b/src/mswin/fg_state_mswin.c index a217747..4c0ca30 100644 --- a/src/mswin/fg_state_mswin.c +++ b/src/mswin/fg_state_mswin.c @@ -37,7 +37,7 @@ extern GLboolean fgSetupPixelFormat( SFG_Window* window, GLboolean checkOnly, * and the window rect from the client area given the style of the window * (or a valid window pointer from which the style can be queried). */ -extern void fghGetClientArea( RECT *clientRect, const SFG_Window *window ); +extern void fghGetClientArea( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside ); extern void fghGetStyleFromWindow( const SFG_Window *window, DWORD *windowStyle, DWORD *windowExStyle ); extern void fghComputeWindowRectFromClientArea_UseStyle( RECT *clientRect, const DWORD windowStyle, const DWORD windowExStyle, BOOL posIsOutside ); @@ -161,17 +161,7 @@ int fgPlatformGlutGet ( GLenum eWhat ) case GLUT_WINDOW_Y: { /* - * There is considerable confusion about the "right thing to - * do" concerning window size and position. GLUT itself is - * not consistent between Windows and UNIX/X11; since - * platform independence is a virtue for "freeglut", we - * decided to break with GLUT's behaviour. - * - * Under UNIX/X11, it is apparently not possible to get the - * window border sizes in order to subtract them off the - * window's initial position until some time after the window - * has been created. Therefore we decided on the following - * behaviour, both under Windows and under UNIX/X11: + * NB: * - When you create a window with position (x,y) and size * (w,h), the upper left hand corner of the outside of the * window is at (x,y) and the size of the drawable area is @@ -213,11 +203,20 @@ int fgPlatformGlutGet ( GLenum eWhat ) break; case GLUT_WINDOW_WIDTH: - freeglut_return_val_if_fail( fgStructure.CurrentWindow != NULL, 0 ); - return fgStructure.CurrentWindow->State.Width; case GLUT_WINDOW_HEIGHT: + { + RECT winRect; freeglut_return_val_if_fail( fgStructure.CurrentWindow != NULL, 0 ); - return fgStructure.CurrentWindow->State.Height; + + GetClientRect( fgStructure.CurrentWindow->Window.Handle, &winRect); + + switch( eWhat ) + { + case GLUT_WINDOW_WIDTH: return winRect.right-winRect.left; + case GLUT_WINDOW_HEIGHT: return winRect.bottom-winRect.top; + } + } + break; case GLUT_WINDOW_BORDER_WIDTH : case GLUT_WINDOW_BORDER_HEIGHT : @@ -240,7 +239,7 @@ int fgPlatformGlutGet ( GLenum eWhat ) /* Also get window rect (including non-client area) */ if (fgStructure.CurrentWindow && fgStructure.CurrentWindow->Window.Handle) { - fghGetClientArea(&clientRect,fgStructure.CurrentWindow); + fghGetClientArea(&clientRect,fgStructure.CurrentWindow, FALSE); GetWindowRect(fgStructure.CurrentWindow->Window.Handle,&winRect); } else diff --git a/src/mswin/fg_window_mswin.c b/src/mswin/fg_window_mswin.c index c0efc9b..3290913 100644 --- a/src/mswin/fg_window_mswin.c +++ b/src/mswin/fg_window_mswin.c @@ -84,7 +84,6 @@ typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShar typedef BOOL (WINAPI *pRegisterTouchWindow)(HWND,ULONG); static pRegisterTouchWindow fghRegisterTouchWindow = (pRegisterTouchWindow)0xDEADBEEF; #endif -extern void fghNotifyWindowStatus(SFG_Window *window); /* @@ -458,8 +457,12 @@ void fghComputeWindowRectFromClientArea_QueryWindow( RECT *clientRect, const SFG * specified window. Output is position of corners of client area (drawable area) on the screen. * Does not touch clientRect if window pointer or window handle is NULL. * (rect.right-rect.left,rect.bottom-rect.top) is the size of the drawable area. + * if posIsOutside is true, the output client Rect will follow freeGLUT's window + * specification convention in which the top-left corner is at the outside of + * the window, while the size (rect.right-rect.left,rect.bottom-rect.top) remains to be the + * size of the drawable area. */ -void fghGetClientArea( RECT *clientRect, const SFG_Window *window ) +void fghGetClientArea( RECT *clientRect, const SFG_Window *window, BOOL posIsOutside ) { POINT topLeftClient = {0,0}; @@ -467,10 +470,21 @@ void fghGetClientArea( RECT *clientRect, const SFG_Window *window ) /* Get size of client rect */ GetClientRect(window->Window.Handle, clientRect); - /* Get position of top-left of client area on the screen */ - ClientToScreen(window->Window.Handle,&topLeftClient); - /* Add top-left offset */ - OffsetRect(clientRect,topLeftClient.x,topLeftClient.y); + if (posIsOutside) + { + RECT windowRect; + /* Get position of outside of window, including decorations */ + GetWindowRect(window->Window.Handle,&windowRect); + /* Add top-left offset */ + OffsetRect(clientRect,windowRect.left,windowRect.top); + } + else + { + /* Get position of top-left of client area on the screen */ + ClientToScreen(window->Window.Handle,&topLeftClient); + /* Add top-left offset */ + OffsetRect(clientRect,topLeftClient.x,topLeftClient.y); + } } @@ -622,7 +636,7 @@ void fgPlatformOpenWindow( SFG_Window* window, const char* title, } if( !sizeUse ) { - if( ! window->IsMenu ) + if( !window->IsMenu ) { w = CW_USEDEFAULT; h = CW_USEDEFAULT; @@ -630,9 +644,6 @@ void fgPlatformOpenWindow( SFG_Window* window, const char* title, else /* fail safe - Windows can make a window of size (0, 0) */ w = h = 300; /* default window size */ } - /* store requested client area width and height */ - window->State.Width = w; - window->State.Height = h; #if !defined(_WIN32_WCE) /* no decorations for windows CE */ if( sizeUse ) @@ -697,6 +708,8 @@ void fgPlatformOpenWindow( SFG_Window* window, const char* title, ); #endif /* defined(_WIN32_WCE) */ + /* WM_CREATE message got sent and was handled by window proc */ + if( !( window->Window.Handle ) ) fgError( "Failed to create a window (%s)!", title ); @@ -740,99 +753,6 @@ void fgPlatformOpenWindow( SFG_Window* window, const char* title, } -void fgPlatformDisplayWindow ( SFG_Window *window ) -{ - /* This immediately generates a WM_PAINT message upon which we call the display callbacks to redraw the window */ - RedrawWindow( - window->Window.Handle, NULL, NULL, - RDW_NOERASE | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_UPDATENOW - ); -} - - -void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height ) -{ - RECT windowRect; - - /* - * HACK HACK HACK: - * Before we do anything else, check if this is a newly created window - * that did not have its windowStatus/visibility func called yet - * we do that on first paint, but because I want to keep the paint - * operation as lean as possible, we do it here. The first paint - * goes together with a first resize call before the display callback - * is called, so this is just in time. Shitty place to do it, but this - * is the only reliable way I can think of to call the callback upon - * first draw of the window. - * More broadly speaking, I know this is an ugly hack, but I'm not sure - * what else to do about it. Depending on WM_ACTIVATE would not work - * as not all windows get this when you are opening multiple before the - * mainloop starts. WM_SHOWWINDOW looked like an interesting candidate, - * but it is generated and processed before glutCreate(Sub)Window - * returns, so no callback can yet be set on the window. - */ - /* Check windowStatus/visibility func has been notified that window is visible (deferred from creation time to give user opportunity to register callbacks) */ - if (!window->State.pWState.WindowFuncCalled) - { - fghNotifyWindowStatus(window); - window->State.pWState.WindowFuncCalled = GL_TRUE; - } - - /* - * For windowed mode, get the current position of the - * window and resize taking the size of the frame - * decorations into account. - * - * Note on maximizing behavior of Windows: the resize borders are off - * the screen such that the client area extends all the way from the - * leftmost corner to the rightmost corner to maximize screen real - * estate. A caption is still shown however to allow interaction with - * the window controls. This is default behavior of Windows that - * FreeGLUT sticks with. To alter, one would have to check if - * WS_MAXIMIZE style is set when a resize event is triggered, and - * then manually correct the windowRect to put the borders back on - * screen. - */ - - /* "GetWindowRect" returns the pixel coordinates of the outside of the window */ - GetWindowRect( window->Window.Handle, &windowRect ); - - /* Create rect in FreeGLUT format, (X,Y) topleft outside window, WxH of client area */ - windowRect.right = windowRect.left+width; - windowRect.bottom = windowRect.top+height; - - if (window->Parent == NULL) - /* get the window rect from this to feed to SetWindowPos, correct for window decorations */ - fghComputeWindowRectFromClientArea_QueryWindow(&windowRect,window,TRUE); - else - { - /* correct rect for position client area of parent window - * (SetWindowPos input for child windows is in coordinates - * relative to the parent's client area). - * Child windows don't have decoration, so no need to correct - * for them. - */ - RECT parentRect; - fghGetClientArea( &parentRect, window->Parent ); - OffsetRect(&windowRect,-parentRect.left,-parentRect.top); - } - - /* Do the actual resizing */ - SetWindowPos( window->Window.Handle, - HWND_TOP, - windowRect.left, windowRect.top, - windowRect.right - windowRect.left, - windowRect.bottom- windowRect.top, - SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | - SWP_NOZORDER - ); - - /* Set new width and height so we can test for that in WM_SIZE message handler and don't do anything if not needed */ - window->State.Width = width; - window->State.Height = height; -} - - /* * Closes a window, destroying the frame and OpenGL context */ @@ -873,38 +793,6 @@ void fgPlatformCloseWindow( SFG_Window* window ) } - -/* - * This function makes the current window visible - */ -void fgPlatformGlutShowWindow( void ) -{ - ShowWindow( fgStructure.CurrentWindow->Window.Handle, SW_SHOW ); -} - -/* - * This function hides the current window - */ -void fgPlatformGlutHideWindow( void ) -{ - ShowWindow( fgStructure.CurrentWindow->Window.Handle, SW_HIDE ); -} - -/* - * Iconify the current window (top-level windows only) - */ -void fgPlatformGlutIconifyWindow( void ) -{ - SFG_Window *win = fgStructure.CurrentWindow; - - /* Call on parent window */ - while (win->Parent) - win = win->Parent; - - /* Visibility status of window gets updated in the WM_SHOWWINDOW handler */ - ShowWindow(win->Window.Handle, SW_MINIMIZE); -} - /* * Set the current window's title */ @@ -943,188 +831,6 @@ void fgPlatformGlutSetIconTitle( const char* title ) fgStructure.CurrentWindow->State.pWState.IconTitle = strdup(title); } -/* - * Change the current window's position - */ -void fgPlatformGlutPositionWindow( int x, int y ) -{ - RECT winRect; - - /* "GetWindowRect" returns the pixel coordinates of the outside of the window */ - GetWindowRect( fgStructure.CurrentWindow->Window.Handle, &winRect ); - MoveWindow( - fgStructure.CurrentWindow->Window.Handle, - x, - y, - winRect.right - winRect.left, - winRect.bottom - winRect.top, - TRUE - ); -} - -/* - * Lowers the current window (by Z order change) - */ -void fgPlatformGlutPushWindow( void ) -{ - SetWindowPos( - fgStructure.CurrentWindow->Window.Handle, - HWND_BOTTOM, - 0, 0, 0, 0, - SWP_NOSIZE | SWP_NOMOVE - ); -} - -/* - * Raises the current window (by Z order change) - */ -void fgPlatformGlutPopWindow( void ) -{ - SetWindowPos( - fgStructure.CurrentWindow->Window.Handle, - HWND_TOP, - 0, 0, 0, 0, - SWP_NOSIZE | SWP_NOMOVE - ); -} - -/* - * Resize the current window so that it fits the whole screen - */ -void fgPlatformGlutFullScreen( SFG_Window *win ) -{ -#if !defined(_WIN32_WCE) /* FIXME: what about WinCE */ - - if (glutGet(GLUT_FULL_SCREEN)) - { - /* Leave full screen state before entering fullscreen again (resizing?) */ - glutLeaveFullScreen(); - } - - { -#if(WINVER >= 0x0500) /* Windows 2000 or later */ - RECT rect; - HMONITOR hMonitor; - MONITORINFO mi; - - /* For fullscreen mode, first remove all window decoration - * and set style to popup so it will overlap the taskbar - * then force to maximize on the screen on which it has the most - * overlap. - */ - - - /* save current window rect, style, exstyle and maximized state */ - win->State.pWState.OldMaximized = !!IsZoomed(win->Window.Handle); - if (win->State.pWState.OldMaximized) - /* We force the window into restored mode before going - * fullscreen because Windows doesn't seem to hide the - * taskbar if the window is in the maximized state. - */ - SendMessage(win->Window.Handle, WM_SYSCOMMAND, SC_RESTORE, 0); - - GetWindowRect( win->Window.Handle, &win->State.pWState.OldRect ); - win->State.pWState.OldStyle = GetWindowLong(win->Window.Handle, GWL_STYLE); - win->State.pWState.OldStyleEx = GetWindowLong(win->Window.Handle, GWL_EXSTYLE); - - /* remove decorations from style */ - SetWindowLong(win->Window.Handle, GWL_STYLE, - win->State.pWState.OldStyle & ~(WS_CAPTION | WS_THICKFRAME)); - SetWindowLong(win->Window.Handle, GWL_EXSTYLE, - win->State.pWState.OldStyleEx & ~(WS_EX_DLGMODALFRAME | - WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); - - /* For fullscreen mode, find the monitor that is covered the most - * by the window and get its rect as the resize target. - */ - hMonitor= MonitorFromRect(&win->State.pWState.OldRect, MONITOR_DEFAULTTONEAREST); - mi.cbSize = sizeof(mi); - GetMonitorInfo(hMonitor, &mi); - rect = mi.rcMonitor; -#else /* if (WINVER >= 0x0500) */ - RECT rect; - - /* For fullscreen mode, force the top-left corner to 0,0 - * and adjust the window rectangle so that the client area - * covers the whole screen. - */ - - rect.left = 0; - rect.top = 0; - rect.right = fgDisplay.ScreenWidth; - rect.bottom = fgDisplay.ScreenHeight; - - AdjustWindowRect ( &rect, WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS | - WS_CLIPCHILDREN, FALSE ); -#endif /* (WINVER >= 0x0500) */ - - /* - * then resize window - * SWP_NOACTIVATE Do not activate the window - * SWP_NOOWNERZORDER Do not change position in z-order - * SWP_NOSENDCHANGING Suppress WM_WINDOWPOSCHANGING message - * SWP_NOZORDER Retains the current Z order (ignore 2nd param) - */ - SetWindowPos( fgStructure.CurrentWindow->Window.Handle, - HWND_TOP, - rect.left, - rect.top, - rect.right - rect.left, - rect.bottom - rect.top, - SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | - SWP_NOZORDER - ); - - win->State.IsFullscreen = GL_TRUE; - } -#endif -} - -/* - * If we are fullscreen, resize the current window back to its original size - */ -void fgPlatformGlutLeaveFullScreen( SFG_Window *win ) -{ -#if !defined(_WIN32_WCE) /* FIXME: what about WinCE */ - if (!glutGet(GLUT_FULL_SCREEN)) - { - /* nothing to do */ - return; - } - - /* restore style of window before making it fullscreen */ - SetWindowLong(win->Window.Handle, GWL_STYLE, win->State.pWState.OldStyle); - SetWindowLong(win->Window.Handle, GWL_EXSTYLE, win->State.pWState.OldStyleEx); - - /* Then resize */ - SetWindowPos(win->Window.Handle, - HWND_TOP, - win->State.pWState.OldRect.left, - win->State.pWState.OldRect.top, - win->State.pWState.OldRect.right - win->State.pWState.OldRect.left, - win->State.pWState.OldRect.bottom - win->State.pWState.OldRect.top, - SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOSENDCHANGING | - SWP_NOZORDER - ); - - if (win->State.pWState.OldMaximized) - SendMessage(win->Window.Handle, WM_SYSCOMMAND, SC_MAXIMIZE, 0); - - win->State.IsFullscreen = GL_FALSE; -#endif -} - -/* - * Toggle the window's full screen state. - */ -void fgPlatformGlutFullScreenToggle( SFG_Window *win ) -{ - if (!win->State.IsFullscreen) - glutFullScreen(); - else - glutLeaveFullScreen(); -} - /* -- PLATFORM-SPECIFIC INTERFACE FUNCTION -------------------------------------------------- */ diff --git a/src/x11/fg_internal_x11.h b/src/x11/fg_internal_x11.h index 9919ae5..2a3020f 100644 --- a/src/x11/fg_internal_x11.h +++ b/src/x11/fg_internal_x11.h @@ -113,6 +113,7 @@ struct tagSFG_PlatformWindowState { int OldWidth; /* Window width from before a resize */ int OldHeight; /* " height " " " " */ + GLboolean KeyRepeating; /* Currently in repeat mode? */ }; diff --git a/src/x11/fg_main_x11.c b/src/x11/fg_main_x11.c index cc57185..7af570f 100644 --- a/src/x11/fg_main_x11.c +++ b/src/x11/fg_main_x11.c @@ -56,6 +56,17 @@ # define MIN(a,b) (((a)<(b)) ? (a) : (b)) #endif +extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify); +extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify); +extern void fgPlatformFullScreenToggle( SFG_Window *win ); +extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y ); +extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height ); +extern void fgPlatformPushWindow( SFG_Window *window ); +extern void fgPlatformPopWindow( SFG_Window *window ); +extern void fgPlatformHideWindow( SFG_Window *window ); +extern void fgPlatformIconifyWindow( SFG_Window *window ); +extern void fgPlatformShowWindow( SFG_Window *window ); + /* used in the event handling code to match and discard stale mouse motion events */ static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg); @@ -63,8 +74,7 @@ static Bool match_motion(Display *dpy, XEvent *xev, XPointer arg); * TODO BEFORE THE STABLE RELEASE: * * There are some issues concerning window redrawing under X11, and maybe - * some events are not handled. The Win32 version lacks some more features, - * but seems acceptable for not demanding purposes. + * some events are not handled. * * Need to investigate why the X11 version breaks out with an error when * closing a window (using the window manager, not glutDestroyWindow)... @@ -644,29 +654,25 @@ void fgPlatformProcessSingleEvent ( void ) case CreateNotify: case ConfigureNotify: { - int width, height; + int width, height, x, y; if( event.type == CreateNotify ) { GETWINDOW( xcreatewindow ); width = event.xcreatewindow.width; height = event.xcreatewindow.height; + x = event.xcreatewindow.x; + y = event.xcreatewindow.y; } else { GETWINDOW( xconfigure ); width = event.xconfigure.width; height = event.xconfigure.height; + x = event.xconfigure.x; + y = event.xconfigure.y; } - if( ( width != window->State.pWState.OldWidth ) || - ( height != window->State.pWState.OldHeight ) ) - { - SFG_Window *current_window = fgStructure.CurrentWindow; - - window->State.pWState.OldWidth = width; - window->State.pWState.OldHeight = height; - INVOKE_WCB( *window, Reshape, ( width, height ) ); - glutPostRedisplay( ); - if( window->IsMenu ) - fgSetWindow( current_window ); - } + /* Update state and call callback, if there was a change */ + fghOnPositionNotify(window, x, y, GL_FALSE); + /* Update state and call callback, if there was a change */ + fghOnReshapeNotify(window, width, height, GL_FALSE); } break; @@ -918,20 +924,20 @@ void fgPlatformProcessSingleEvent ( void ) if ( event.xkey.keycode<256 ) /* XQueryKeymap is limited to 256 keycodes */ { if ( keys[event.xkey.keycode>>3] & (1<<(event.xkey.keycode%8)) ) - window->State.KeyRepeating = GL_TRUE; + window->State.pWState.KeyRepeating = GL_TRUE; else - window->State.KeyRepeating = GL_FALSE; + window->State.pWState.KeyRepeating = GL_FALSE; } } } else - window->State.KeyRepeating = GL_FALSE; + window->State.pWState.KeyRepeating = GL_FALSE; /* Cease processing this event if it is auto repeated */ - if (window->State.KeyRepeating) + if (window->State.pWState.KeyRepeating) { - if (event.type == KeyPress) window->State.KeyRepeating = GL_FALSE; + if (event.type == KeyPress) window->State.pWState.KeyRepeating = GL_FALSE; break; } @@ -1071,3 +1077,72 @@ void fgPlatformMainLoopPreliminaryWork ( void ) { } + +/* Step through the work list */ +void fgPlatformProcessWork(SFG_Window *window) +{ + unsigned int workMask = window->State.WorkMask; + /* Now clear it so that any callback generated by the actions below can set work again */ + window->State.WorkMask = 0; + + /* This is before the first display callback: call a few callbacks to inform user of window size, position, etc + * we know this is before the first display callback of a window as for all windows GLUT_INIT_WORK is set when + * they are opened, and work is done before displaying in the mainloop. + */ + if (workMask & GLUT_INIT_WORK) + { + /* Notify windowStatus/visibility, position and size get notified on window creation with message handlers above + * XXX CHECK: do the messages happen too early like on windows, so client code cannot have registered + * a callback yet and the message is thus never received by client? + */ + + /* Call init context callback */ + INVOKE_WCB( *window, InitContext, ()); + + /* Lastly, check if we have a display callback, error out if not + * This is the right place to do it, as the redisplay will be + * next right after we exit this function, so there is no more + * opportunity for the user to register a callback for this window. + */ + if (!FETCH_WCB(*window, Display)) + fgError ( "ERROR: No display callback registered for window %d\n", window->ID ); + } + + if (workMask & GLUT_FULL_SCREEN_WORK) + fgPlatformFullScreenToggle( window ); + if (workMask & GLUT_POSITION_WORK) + fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos ); + if (workMask & GLUT_SIZE_WORK) + fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight ); + if (workMask & GLUT_ZORDER_WORK) + { + if (window->State.DesiredZOrder < 0) + fgPlatformPushWindow( window ); + else + fgPlatformPopWindow( window ); + } + + if (workMask & GLUT_VISIBILITY_WORK) + { + /* Visibility status of window gets updated in the window message handlers above + * XXX: is this really the case? check + */ + SFG_Window *win = window; + switch (window->State.DesiredVisibility) + { + case DesireHiddenState: + fgPlatformHideWindow( window ); + break; + case DesireIconicState: + /* Call on top-level window */ + while (win->Parent) + win = win->Parent; + fgPlatformIconifyWindow( win ); + break; + case DesireNormalState: + fgPlatformShowWindow( window ); + break; + } + } +} + diff --git a/src/x11/fg_window_x11.c b/src/x11/fg_window_x11.c index 6f927c2..b6943d3 100644 --- a/src/x11/fg_window_x11.c +++ b/src/x11/fg_window_x11.c @@ -32,8 +32,6 @@ #include /* usleep */ #include "../fg_internal.h" -extern void fghRedrawWindow(SFG_Window *window); - #ifdef EGL_VERSION_1_0 #include "egl/fg_window_egl.h" #define fghCreateNewContext fghCreateNewContextEGL @@ -48,11 +46,14 @@ static int fghResizeFullscrToggle(void) if(glutGet(GLUT_FULL_SCREEN)) { /* restore original window size */ SFG_Window *win = fgStructure.CurrentWindow; - fgStructure.CurrentWindow->State.NeedToResize = GL_TRUE; - fgStructure.CurrentWindow->State.Width = win->State.pWState.OldWidth; - fgStructure.CurrentWindow->State.Height = win->State.pWState.OldHeight; + fgStructure.CurrentWindow->State.WorkMask = GLUT_SIZE_WORK; + fgStructure.CurrentWindow->State.DesiredWidth = win->State.pWState.OldWidth; + fgStructure.CurrentWindow->State.DesiredHeight = win->State.pWState.OldHeight; } else { + fgStructure.CurrentWindow->State.pWState.OldWidth = win->State.Width; + fgStructure.CurrentWindow->State.pWState.OldHeight = win->State.Height; + /* resize the window to cover the entire screen */ XGetWindowAttributes(fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle, @@ -383,15 +384,6 @@ void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height ) /* - * A static helper function to execute display callback for a window - */ -void fgPlatformDisplayWindow ( SFG_Window *window ) -{ - fghRedrawWindow ( window ) ; -} - - -/* * Closes a window, destroying the frame and OpenGL context */ void fgPlatformCloseWindow( SFG_Window* window ) @@ -412,35 +404,35 @@ void fgPlatformCloseWindow( SFG_Window* window ) /* - * This function makes the current window visible + * This function makes the specified window visible */ -void fgPlatformGlutShowWindow( void ) +void fgPlatformShowWindow( SFG_Window *window ) { - XMapWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle ); + XMapWindow( fgDisplay.pDisplay.Display, window->Window.Handle ); XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */ } /* - * This function hides the current window + * This function hides the specified window */ -void fgPlatformGlutHideWindow( void ) +void fgPlatformHideWindow( SFG_Window *window ) { - if( fgStructure.CurrentWindow->Parent == NULL ) + if( window->Parent == NULL ) XWithdrawWindow( fgDisplay.pDisplay.Display, - fgStructure.CurrentWindow->Window.Handle, + window->Window.Handle, fgDisplay.pDisplay.Screen ); else XUnmapWindow( fgDisplay.pDisplay.Display, - fgStructure.CurrentWindow->Window.Handle ); + window->Window.Handle ); XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */ } /* - * Iconify the current window (top-level windows only) + * Iconify the specified window (top-level windows only) */ -void fgPlatformGlutIconifyWindow( void ) +void fgPlatformIconifyWindow( SFG_Window *window ) { - XIconifyWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle, + XIconifyWindow( fgDisplay.pDisplay.Display, window->Window.Handle, fgDisplay.pDisplay.Screen ); XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */ @@ -490,59 +482,35 @@ void fgPlatformGlutSetIconTitle( const char* title ) } /* - * Change the current window's position + * Change the specified window's position */ -void fgPlatformGlutPositionWindow( int x, int y ) +void fgPlatformPositionWindow( SFG_Window *window, int x, int y ) { - XMoveWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle, + XMoveWindow( fgDisplay.pDisplay.Display, window->Window.Handle, x, y ); XFlush( fgDisplay.pDisplay.Display ); /* XXX Shouldn't need this */ } /* - * Lowers the current window (by Z order change) - */ -void fgPlatformGlutPushWindow( void ) -{ - XLowerWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle ); -} - -/* - * Raises the current window (by Z order change) - */ -void fgPlatformGlutPopWindow( void ) -{ - XRaiseWindow( fgDisplay.pDisplay.Display, fgStructure.CurrentWindow->Window.Handle ); -} - -/* - * Resize the current window so that it fits the whole screen + * Lowers the specified window (by Z order change) */ -void fgPlatformGlutFullScreen( SFG_Window *win ) +void fgPlatformPushWindow( SFG_Window *window ) { - if(!glutGet(GLUT_FULL_SCREEN)) { - if(fghToggleFullscreen() != -1) { - win->State.IsFullscreen = GL_TRUE; - } - } + XLowerWindow( fgDisplay.pDisplay.Display, window->Window.Handle ); } /* - * If we are fullscreen, resize the current window back to its original size + * Raises the specified window (by Z order change) */ -void fgPlatformGlutLeaveFullScreen( SFG_Window *win ) +void fgPlatformPopWindow( SFG_Window *window ) { - if(glutGet(GLUT_FULL_SCREEN)) { - if(fghToggleFullscreen() != -1) { - win->State.IsFullscreen = GL_FALSE; - } - } + XRaiseWindow( fgDisplay.pDisplay.Display, window->Window.Handle ); } /* * Toggle the window's full screen state. */ -void fgPlatformGlutFullScreenToggle( SFG_Window *win ) +void fgPlatformFullScreenToggle( SFG_Window *win ) { if(fghToggleFullscreen() != -1) { win->State.IsFullscreen = !win->State.IsFullscreen; -- 1.7.10.4