\r
int nWindow, nChildWindow = -1;\r
int nLoopMain = 0;\r
-\r
-int nPosX, nPosY;\r
-int nWidth, nHeight;\r
-\r
GLboolean bChildPosDone = GL_FALSE, bChildSizeDone = GL_FALSE;\r
\r
void SampleKeyboard( unsigned char cChar, int nMouseX, int nMouseY );\r
\r
void DrawQuad()\r
{\r
- nWidth = glutGet(GLUT_WINDOW_WIDTH);\r
- nHeight = glutGet(GLUT_WINDOW_HEIGHT);\r
+ int width = glutGet(GLUT_WINDOW_WIDTH);\r
+ int height = glutGet(GLUT_WINDOW_HEIGHT);\r
\r
glBegin(GL_QUADS);\r
- glVertex2d(nWidth*.25, nHeight*.75);\r
- glVertex2d(nWidth*.75, nHeight*.75);\r
- glVertex2d(nWidth*.75, nHeight*.25);\r
- glVertex2d(nWidth*.25, nHeight*.25);\r
+ glVertex2d(width*.25, height*.75);\r
+ glVertex2d(width*.75, height*.75);\r
+ glVertex2d(width*.75, height*.25);\r
+ glVertex2d(width*.25, height*.25);\r
glEnd();\r
}\r
\r
glutShowWindow();\r
}\r
\r
+void ChangeTitleTimer(int unused)\r
+{\r
+ glutSetIconTitle("new icon title");\r
+ glutSetWindowTitle("new test title");\r
+}\r
+\r
void SampleKeyboard( unsigned char cChar, int nMouseX, int nMouseY )\r
{\r
switch (cChar)\r
{\r
glutSetWindow(nWindow);\r
printf("main window resize\n");\r
- if (nWidth<400)\r
+ if (glutGet(GLUT_WINDOW_WIDTH)<400)\r
glutReshapeWindow(600,300);\r
else\r
glutReshapeWindow(300,300);\r
* the client area is at a different position if the window has borders\r
* and/or a title bar.\r
*/\r
- if (nPosX<400)\r
+ if (glutGet(GLUT_WINDOW_X)<400)\r
glutPositionWindow(600,300);\r
else\r
glutPositionWindow(300,300);\r
break;\r
\r
\r
+ case 'd':\r
+ case 'D':\r
+ if (nChildWindow!=-1 && cChar=='d') /* Capital D always moves+resizes the main window*/\r
+ {\r
+ glutSetWindow(nChildWindow);\r
+ if (!bChildPosDone)\r
+ glutPositionWindow(glutGet(GLUT_WINDOW_X)+50,glutGet(GLUT_WINDOW_Y)+50);\r
+ else\r
+ glutPositionWindow(glutGet(GLUT_WINDOW_X)-50,glutGet(GLUT_WINDOW_Y)-50);\r
+ bChildPosDone = !bChildPosDone;\r
+ if (!bChildSizeDone)\r
+ glutReshapeWindow(glutGet(GLUT_WINDOW_WIDTH)+50,glutGet(GLUT_WINDOW_HEIGHT)+50);\r
+ else\r
+ glutReshapeWindow(glutGet(GLUT_WINDOW_WIDTH)-50,glutGet(GLUT_WINDOW_HEIGHT)-50);\r
+ bChildSizeDone = !bChildSizeDone;\r
+ }\r
+ else\r
+ {\r
+ if (glutGet(GLUT_WINDOW_X)<400)\r
+ glutPositionWindow(600,300);\r
+ else\r
+ glutPositionWindow(300,300);\r
+ if (glutGet(GLUT_WINDOW_WIDTH)<400)\r
+ glutReshapeWindow(600,300);\r
+ else\r
+ glutReshapeWindow(300,300);\r
+ }\r
+ break;\r
+\r
+\r
case 'c':\r
case 'C':\r
if (nChildWindow==-1)\r
{\r
+ int width = glutGet(GLUT_WINDOW_WIDTH);\r
+ int height = glutGet(GLUT_WINDOW_HEIGHT);\r
+\r
/* open child window */\r
printf("open child window\n");\r
- nWidth = glutGet(GLUT_WINDOW_WIDTH);\r
- nHeight = glutGet(GLUT_WINDOW_HEIGHT);\r
\r
- nChildWindow = glutCreateSubWindow(nWindow,(int)(nWidth*.35),(int)(nHeight*.35),(int)(nWidth*.3),(int)(nHeight*.3));\r
+ nChildWindow = glutCreateSubWindow(nWindow,(int)(width*.35),(int)(height*.35),(int)(width*.3),(int)(height*.3));\r
glutKeyboardFunc( SampleKeyboard );\r
glutDisplayFunc( Redisplay );\r
glutReshapeFunc( Reshape );\r
case 'i':\r
case 'I':\r
glutIconifyWindow();\r
+ glutTimerFunc(1500, ChangeTitleTimer, 0);\r
break;\r
\r
\r
{\r
int win = glutGetWindow();\r
\r
- printf("reshape %s, %dx%d\n",win==nWindow?"main":"child",\r
+ printf("reshape %s, client area: %dx%d\n",win==nWindow?"main":"child",\r
width, height);\r
\r
glViewport(0,0,width,height);\r
\r
if (win==nWindow && nChildWindow!=-1)\r
{\r
+ /* Put child window in right place */\r
+ int x = (int)(width*.35), y=(int)(height*.35), w=(int)(width*.3), h = (int)(height*.3);\r
+ if (bChildPosDone)\r
+ {\r
+ x += 50;\r
+ y += 50;\r
+ }\r
+ if (bChildSizeDone)\r
+ {\r
+ w += 50;\r
+ h += 50;\r
+ }\r
glutSetWindow(nChildWindow);\r
- glutPositionWindow((int)(width*.35),(int)(height*.35));\r
- glutReshapeWindow((int)(width*.3),(int)(height*.3));\r
+ glutPositionWindow(x,y);\r
+ glutReshapeWindow(w,h);\r
glutSetWindow(nWindow);\r
}\r
}\r
{\r
int win = glutGetWindow();\r
\r
- printf("position, %s: (%d,%d)\n",win==nWindow?"top-left of main":"top-left of child relative to parent",\r
+ printf("position, %s: (%d,%d)\n",win==nWindow?"top-left (non-client) of main":"top-left of child relative to parent",\r
x, y);\r
}\r
\r
void Timer(int unused)\r
{\r
int win = glutGetWindow();\r
+ int x, y;\r
+ int width, height;\r
int border, caption;\r
\r
- nPosX = glutGet(GLUT_WINDOW_X);\r
- nPosY = glutGet(GLUT_WINDOW_Y);\r
- nWidth = glutGet(GLUT_WINDOW_WIDTH);\r
- nHeight = glutGet(GLUT_WINDOW_HEIGHT);\r
+ x = glutGet(GLUT_WINDOW_X);\r
+ y = glutGet(GLUT_WINDOW_Y);\r
+ width = glutGet(GLUT_WINDOW_WIDTH);\r
+ height = glutGet(GLUT_WINDOW_HEIGHT);\r
border = glutGet(GLUT_WINDOW_BORDER_WIDTH);\r
caption = glutGet(GLUT_WINDOW_HEADER_HEIGHT);\r
/* returned position is top-left of client area, to get top-left of\r
/* printf("window border: %dpx, caption: %dpx\n",border,caption); */\r
if (win==nWindow)\r
printf("main window %dx%d, top-left of client at: (%d,%d), of window at: (%d,%d)\n",\r
- nWidth, nHeight,\r
- nPosX ,nPosY,\r
- nPosX-border,\r
- nPosY-border-caption);\r
+ width, height,\r
+ x ,y,\r
+ x-border,\r
+ y-border-caption);\r
else\r
printf("child window %dx%d, top-left of client at: (%d,%d), relative to parent\n",\r
- nWidth, nHeight,\r
- nPosX ,nPosY);\r
+ width, height,\r
+ x ,y);\r
\r
/* (re)set the timer callback and ask glut to call it in 500 ms */\r
glutTimerFunc(500, Timer, 0);\r
glutInitWindowSize(200,200);\r
\r
nWindow = glutCreateWindow("test");\r
+ glutSetIconTitle("test icon title");\r
printf("main window id: %d\n", nWindow);\r
\r
glutKeyboardFunc( SampleKeyboard );\r
typedef struct tagSFG_PlatformWindowState SFG_PlatformWindowState;
struct tagSFG_PlatformWindowState
{
- int32_t LastHeight;
- int32_t LastWidth;
+ char unused;
};
#endif /* FREEGLUT_INTERNAL_ANDROID_H */
-/*
- * 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, <olszta@sourceforge.net>
- * Copied for Platform code by Evan Felix <karcaw at gmail.com>
- * 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 <GL/freeglut.h>
-#include "fg_internal.h"
-#include "egl/fg_window_egl.h"
-
-#include <android/log.h>
-#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 <android/native_app_glue/android_native_app_glue.h>
-#include <android/keycodes.h>
-
-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 <android/keycodes.h> */
-/* Don't convert to enum, since it may conflict with future version of
- that <android/keycodes.h> */
-#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, "<init>", "(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();
-}
+/*\r
+ * fg_main_android.c\r
+ *\r
+ * The Android-specific windows message processing methods.\r
+ *\r
+ * Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.\r
+ * Written by Pawel W. Olszta, <olszta@sourceforge.net>\r
+ * Copied for Platform code by Evan Felix <karcaw at gmail.com>\r
+ * Copyright (C) 2012 Sylvain Beucler\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining a\r
+ * copy of this software and associated documentation files (the "Software"),\r
+ * to deal in the Software without restriction, including without limitation\r
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,\r
+ * and/or sell copies of the Software, and to permit persons to whom the\r
+ * Software is furnished to do so, subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be included\r
+ * in all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS\r
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r
+ * PAWEL W. OLSZTA BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ */\r
+\r
+#include <GL/freeglut.h>\r
+#include "fg_internal.h"\r
+#include "egl/fg_window_egl.h"\r
+\r
+#include <android/log.h>\r
+#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FreeGLUT", __VA_ARGS__))\r
+#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "FreeGLUT", __VA_ARGS__))\r
+#include <android/native_app_glue/android_native_app_glue.h>\r
+#include <android/keycodes.h>\r
+\r
+extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify);\r
+extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify);\r
+extern void fgPlatformFullScreenToggle( SFG_Window *win );\r
+extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y );\r
+extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );\r
+extern void fgPlatformPushWindow( SFG_Window *window );\r
+extern void fgPlatformPopWindow( SFG_Window *window );\r
+extern void fgPlatformHideWindow( SFG_Window *window );\r
+extern void fgPlatformIconifyWindow( SFG_Window *window );\r
+extern void fgPlatformShowWindow( SFG_Window *window );\r
+\r
+static struct touchscreen touchscreen;\r
+static unsigned char key_a2fg[256];\r
+\r
+/* Cf. http://developer.android.com/reference/android/view/KeyEvent.html */\r
+/* These codes are missing in <android/keycodes.h> */\r
+/* Don't convert to enum, since it may conflict with future version of\r
+ that <android/keycodes.h> */\r
+#define AKEYCODE_FORWARD_DEL 112\r
+#define AKEYCODE_CTRL_LEFT 113\r
+#define AKEYCODE_CTRL_RIGHT 114\r
+#define AKEYCODE_MOVE_HOME 122\r
+#define AKEYCODE_MOVE_END 123\r
+#define AKEYCODE_INSERT 124\r
+#define AKEYCODE_ESCAPE 127\r
+#define AKEYCODE_F1 131\r
+#define AKEYCODE_F2 132\r
+#define AKEYCODE_F3 133\r
+#define AKEYCODE_F4 134\r
+#define AKEYCODE_F5 135\r
+#define AKEYCODE_F6 136\r
+#define AKEYCODE_F7 137\r
+#define AKEYCODE_F8 138\r
+#define AKEYCODE_F9 139\r
+#define AKEYCODE_F10 140\r
+#define AKEYCODE_F11 141\r
+#define AKEYCODE_F12 142\r
+\r
+#define EVENT_HANDLED 1\r
+#define EVENT_NOT_HANDLED 0\r
+\r
+/**\r
+ * Initialize Android keycode to GLUT keycode mapping\r
+ */\r
+static void key_init() {\r
+ memset(key_a2fg, 0, sizeof(key_a2fg));\r
+\r
+ key_a2fg[AKEYCODE_F1] = GLUT_KEY_F1;\r
+ key_a2fg[AKEYCODE_F2] = GLUT_KEY_F2;\r
+ key_a2fg[AKEYCODE_F3] = GLUT_KEY_F3;\r
+ key_a2fg[AKEYCODE_F4] = GLUT_KEY_F4;\r
+ key_a2fg[AKEYCODE_F5] = GLUT_KEY_F5;\r
+ key_a2fg[AKEYCODE_F6] = GLUT_KEY_F6;\r
+ key_a2fg[AKEYCODE_F7] = GLUT_KEY_F7;\r
+ key_a2fg[AKEYCODE_F8] = GLUT_KEY_F8;\r
+ key_a2fg[AKEYCODE_F9] = GLUT_KEY_F9;\r
+ key_a2fg[AKEYCODE_F10] = GLUT_KEY_F10;\r
+ key_a2fg[AKEYCODE_F11] = GLUT_KEY_F11;\r
+ key_a2fg[AKEYCODE_F12] = GLUT_KEY_F12;\r
+\r
+ key_a2fg[AKEYCODE_PAGE_UP] = GLUT_KEY_PAGE_UP;\r
+ key_a2fg[AKEYCODE_PAGE_DOWN] = GLUT_KEY_PAGE_DOWN;\r
+ key_a2fg[AKEYCODE_MOVE_HOME] = GLUT_KEY_HOME;\r
+ key_a2fg[AKEYCODE_MOVE_END] = GLUT_KEY_END;\r
+ key_a2fg[AKEYCODE_INSERT] = GLUT_KEY_INSERT;\r
+\r
+ key_a2fg[AKEYCODE_DPAD_UP] = GLUT_KEY_UP;\r
+ key_a2fg[AKEYCODE_DPAD_DOWN] = GLUT_KEY_DOWN;\r
+ key_a2fg[AKEYCODE_DPAD_LEFT] = GLUT_KEY_LEFT;\r
+ key_a2fg[AKEYCODE_DPAD_RIGHT] = GLUT_KEY_RIGHT;\r
+\r
+ key_a2fg[AKEYCODE_ALT_LEFT] = GLUT_KEY_ALT_L;\r
+ key_a2fg[AKEYCODE_ALT_RIGHT] = GLUT_KEY_ALT_R;\r
+ key_a2fg[AKEYCODE_SHIFT_LEFT] = GLUT_KEY_SHIFT_L;\r
+ key_a2fg[AKEYCODE_SHIFT_RIGHT] = GLUT_KEY_SHIFT_R;\r
+ key_a2fg[AKEYCODE_CTRL_LEFT] = GLUT_KEY_CTRL_L;\r
+ key_a2fg[AKEYCODE_CTRL_RIGHT] = GLUT_KEY_CTRL_R;\r
+}\r
+\r
+/**\r
+ * Convert an Android key event to ASCII.\r
+ */\r
+static unsigned char key_ascii(struct android_app* app, AInputEvent* event) {\r
+ int32_t code = AKeyEvent_getKeyCode(event);\r
+\r
+ /* Handle a few special cases: */\r
+ switch (code) {\r
+ case AKEYCODE_DEL:\r
+ return 8;\r
+ case AKEYCODE_FORWARD_DEL:\r
+ return 127;\r
+ case AKEYCODE_ESCAPE:\r
+ return 27;\r
+ }\r
+\r
+ /* Get usable JNI context */\r
+ JNIEnv* env = app->activity->env;\r
+ JavaVM* vm = app->activity->vm;\r
+ (*vm)->AttachCurrentThread(vm, &env, NULL);\r
+\r
+ jclass KeyEventClass = (*env)->FindClass(env, "android/view/KeyEvent");\r
+ jmethodID KeyEventConstructor = (*env)->GetMethodID(env, KeyEventClass, "<init>", "(II)V");\r
+ jobject keyEvent = (*env)->NewObject(env, KeyEventClass, KeyEventConstructor,\r
+ AKeyEvent_getAction(event), AKeyEvent_getKeyCode(event));\r
+ jmethodID KeyEvent_getUnicodeChar = (*env)->GetMethodID(env, KeyEventClass, "getUnicodeChar", "(I)I");\r
+ int ascii = (*env)->CallIntMethod(env, keyEvent, KeyEvent_getUnicodeChar, AKeyEvent_getMetaState(event));\r
+\r
+ /* LOGI("getUnicodeChar(%d) = %d ('%c')", AKeyEvent_getKeyCode(event), ascii, ascii); */\r
+ (*vm)->DetachCurrentThread(vm);\r
+\r
+ return ascii;\r
+}\r
+\r
+unsigned long fgPlatformSystemTime ( void )\r
+{\r
+ struct timeval now;\r
+ gettimeofday( &now, NULL );\r
+ return now.tv_usec/1000 + now.tv_sec*1000;\r
+}\r
+\r
+/*\r
+ * Does the magic required to relinquish the CPU until something interesting\r
+ * happens.\r
+ */\r
+void fgPlatformSleepForEvents( long msec )\r
+{\r
+ /* Android's NativeActivity relies on a Looper/ALooper object to\r
+ notify about events. The Looper object is plugged on two\r
+ internal pipe(2)s to detect system and input events. Sadly you\r
+ can only ask the Looper for an event, not just ask whether\r
+ there is a pending event (and process it later). Consequently,\r
+ short of redesigning NativeActivity, we cannot\r
+ SleepForEvents. */\r
+}\r
+\r
+/**\r
+ * Process the next input event.\r
+ */\r
+int32_t handle_input(struct android_app* app, AInputEvent* event) {\r
+ SFG_Window* window = fgWindowByHandle(app->window);\r
+ if (window == NULL)\r
+ return EVENT_NOT_HANDLED;\r
+\r
+ /* FIXME: in Android, when a key is repeated, down\r
+ and up events happen most often at the exact same time. This\r
+ makes it impossible to animate based on key press time. */\r
+ /* e.g. down/up/wait/down/up rather than down/wait/down/wait/up */ \r
+ /* This looks like a bug in the Android virtual keyboard system :/\r
+ Real buttons such as the Back button appear to work correctly\r
+ (series of down events with proper getRepeatCount value). */\r
+ \r
+ if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) {\r
+ /* LOGI("action: %d", AKeyEvent_getAction(event)); */\r
+ /* LOGI("keycode: %d", code); */\r
+ int32_t code = AKeyEvent_getKeyCode(event);\r
+\r
+ if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) {\r
+ int32_t keypress = 0;\r
+ unsigned char ascii = 0;\r
+ if ((keypress = key_a2fg[code]) && FETCH_WCB(*window, Special)) {\r
+ INVOKE_WCB(*window, Special, (keypress, window->State.MouseX, window->State.MouseY));\r
+ return EVENT_HANDLED;\r
+ } else if ((ascii = key_ascii(app, event)) && FETCH_WCB(*window, Keyboard)) {\r
+ INVOKE_WCB(*window, Keyboard, (ascii, window->State.MouseX, window->State.MouseY));\r
+ return EVENT_HANDLED;\r
+ }\r
+ }\r
+ else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_UP) {\r
+ int32_t keypress = 0;\r
+ unsigned char ascii = 0;\r
+ if ((keypress = key_a2fg[code]) && FETCH_WCB(*window, Special)) {\r
+ INVOKE_WCB(*window, SpecialUp, (keypress, window->State.MouseX, window->State.MouseY));\r
+ return EVENT_HANDLED;\r
+ } else if ((ascii = key_ascii(app, event)) && FETCH_WCB(*window, Keyboard)) {\r
+ INVOKE_WCB(*window, KeyboardUp, (ascii, window->State.MouseX, window->State.MouseY));\r
+ return EVENT_HANDLED;\r
+ }\r
+ }\r
+ }\r
+\r
+ int32_t source = AInputEvent_getSource(event);\r
+ if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION\r
+ && source == AINPUT_SOURCE_TOUCHSCREEN) {\r
+ int32_t action = AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK;\r
+ /* Pointer ID for clicks */\r
+ int32_t pidx = AMotionEvent_getAction(event) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;\r
+ /* TODO: Handle multi-touch; also handle multiple sources/devices */\r
+ /* cf. http://sourceforge.net/mailarchive/forum.php?thread_name=20120518071314.GA28061%40perso.beuc.net&forum_name=freeglut-developer */\r
+ if (0) {\r
+ LOGI("motion action=%d index=%d source=%d", action, pidx, source);\r
+ int count = AMotionEvent_getPointerCount(event);\r
+ int i;\r
+ for (i = 0; i < count; i++) {\r
+ LOGI("multi(%d): %.01f,%.01f",\r
+ AMotionEvent_getPointerId(event, i),\r
+ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i));\r
+ }\r
+ }\r
+ float x = AMotionEvent_getX(event, 0);\r
+ float y = AMotionEvent_getY(event, 0);\r
+\r
+ /* Virtual arrows PAD */\r
+ /* Don't interfere with existing mouse move event */\r
+ if (!touchscreen.in_mmotion) {\r
+ struct vpad_state prev_vpad = touchscreen.vpad;\r
+ touchscreen.vpad.left = touchscreen.vpad.right\r
+ = touchscreen.vpad.up = touchscreen.vpad.down = false;\r
+\r
+ /* int32_t width = ANativeWindow_getWidth(window->Window.Handle); */\r
+ int32_t height = ANativeWindow_getHeight(window->Window.Handle);\r
+ if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_MOVE) {\r
+ if ((x > 0 && x < 100) && (y > (height - 100) && y < height))\r
+ touchscreen.vpad.left = true;\r
+ if ((x > 200 && x < 300) && (y > (height - 100) && y < height))\r
+ touchscreen.vpad.right = true;\r
+ if ((x > 100 && x < 200) && (y > (height - 100) && y < height))\r
+ touchscreen.vpad.down = true;\r
+ if ((x > 100 && x < 200) && (y > (height - 200) && y < (height - 100)))\r
+ touchscreen.vpad.up = true;\r
+ }\r
+ if (action == AMOTION_EVENT_ACTION_DOWN && \r
+ (touchscreen.vpad.left || touchscreen.vpad.right || touchscreen.vpad.down || touchscreen.vpad.up))\r
+ touchscreen.vpad.on = true;\r
+ if (action == AMOTION_EVENT_ACTION_UP)\r
+ touchscreen.vpad.on = false;\r
+ if (prev_vpad.left != touchscreen.vpad.left\r
+ || prev_vpad.right != touchscreen.vpad.right\r
+ || prev_vpad.up != touchscreen.vpad.up\r
+ || prev_vpad.down != touchscreen.vpad.down\r
+ || prev_vpad.on != touchscreen.vpad.on) {\r
+ if (FETCH_WCB(*window, Special)) {\r
+ if (prev_vpad.left == false && touchscreen.vpad.left == true)\r
+ INVOKE_WCB(*window, Special, (GLUT_KEY_LEFT, x, y));\r
+ else if (prev_vpad.right == false && touchscreen.vpad.right == true)\r
+ INVOKE_WCB(*window, Special, (GLUT_KEY_RIGHT, x, y));\r
+ else if (prev_vpad.up == false && touchscreen.vpad.up == true)\r
+ INVOKE_WCB(*window, Special, (GLUT_KEY_UP, x, y));\r
+ else if (prev_vpad.down == false && touchscreen.vpad.down == true)\r
+ INVOKE_WCB(*window, Special, (GLUT_KEY_DOWN, x, y));\r
+ }\r
+ if (FETCH_WCB(*window, SpecialUp)) {\r
+ if (prev_vpad.left == true && touchscreen.vpad.left == false)\r
+ INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_LEFT, x, y));\r
+ if (prev_vpad.right == true && touchscreen.vpad.right == false)\r
+ INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_RIGHT, x, y));\r
+ if (prev_vpad.up == true && touchscreen.vpad.up == false)\r
+ INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_UP, x, y));\r
+ if (prev_vpad.down == true && touchscreen.vpad.down == false)\r
+ INVOKE_WCB(*window, SpecialUp, (GLUT_KEY_DOWN, x, y));\r
+ }\r
+ return EVENT_HANDLED;\r
+ }\r
+ }\r
+ \r
+ /* Normal mouse events */\r
+ if (!touchscreen.vpad.on) {\r
+ window->State.MouseX = x;\r
+ window->State.MouseY = y;\r
+ if (action == AMOTION_EVENT_ACTION_DOWN && FETCH_WCB(*window, Mouse)) {\r
+ touchscreen.in_mmotion = true;\r
+ INVOKE_WCB(*window, Mouse, (GLUT_LEFT_BUTTON, GLUT_DOWN, x, y));\r
+ } else if (action == AMOTION_EVENT_ACTION_UP && FETCH_WCB(*window, Mouse)) {\r
+ touchscreen.in_mmotion = false;\r
+ INVOKE_WCB(*window, Mouse, (GLUT_LEFT_BUTTON, GLUT_UP, x, y));\r
+ } else if (action == AMOTION_EVENT_ACTION_MOVE && FETCH_WCB(*window, Motion)) {\r
+ INVOKE_WCB(*window, Motion, (x, y));\r
+ }\r
+ }\r
+ \r
+ return EVENT_HANDLED;\r
+ }\r
+\r
+ /* Let Android handle other events (e.g. Back and Menu buttons) */\r
+ return EVENT_NOT_HANDLED;\r
+}\r
+\r
+/**\r
+ * Process the next main command.\r
+ */\r
+void handle_cmd(struct android_app* app, int32_t cmd) {\r
+ SFG_Window* window = fgWindowByHandle(app->window); /* may be NULL */\r
+ switch (cmd) {\r
+ /* App life cycle, in that order: */\r
+ case APP_CMD_START:\r
+ LOGI("handle_cmd: APP_CMD_START");\r
+ break;\r
+ case APP_CMD_RESUME:\r
+ LOGI("handle_cmd: APP_CMD_RESUME");\r
+ /* Cf. fgPlatformProcessSingleEvent */\r
+ break;\r
+ case APP_CMD_INIT_WINDOW: /* surfaceCreated */\r
+ /* The window is being shown, get it ready. */\r
+ LOGI("handle_cmd: APP_CMD_INIT_WINDOW %p", app->window);\r
+ fgDisplay.pDisplay.single_native_window = app->window;\r
+ window->State.WorkMask |= GLUT_INIT_WORK;\r
+ /* start|resume: glPlatformOpenWindow was waiting for Handle to be\r
+ defined and will now continue processing */\r
+ break;\r
+ case APP_CMD_GAINED_FOCUS:\r
+ LOGI("handle_cmd: APP_CMD_GAINED_FOCUS");\r
+ break;\r
+ case APP_CMD_WINDOW_RESIZED:\r
+ LOGI("handle_cmd: APP_CMD_WINDOW_RESIZED");\r
+ if (window->Window.pContext.egl.Surface != EGL_NO_SURFACE)\r
+ /* Make ProcessSingleEvent detect the new size, only available\r
+ after the next SwapBuffer */\r
+ glutPostRedisplay();\r
+ break;\r
+\r
+ case APP_CMD_SAVE_STATE: /* onSaveInstanceState */\r
+ /* The system has asked us to save our current state, when it\r
+ pauses the application without destroying it right after. */\r
+ app->savedState = strdup("Detect me as non-NULL on next android_main");\r
+ app->savedStateSize = strlen(app->savedState) + 1;\r
+ LOGI("handle_cmd: APP_CMD_SAVE_STATE");\r
+ break;\r
+ case APP_CMD_PAUSE:\r
+ LOGI("handle_cmd: APP_CMD_PAUSE");\r
+ /* Cf. fgPlatformProcessSingleEvent */\r
+ break;\r
+ case APP_CMD_LOST_FOCUS:\r
+ LOGI("handle_cmd: APP_CMD_LOST_FOCUS");\r
+ break;\r
+ case APP_CMD_TERM_WINDOW: /* surfaceDestroyed */\r
+ /* The application is being hidden, but may be restored */\r
+ LOGI("handle_cmd: APP_CMD_TERM_WINDOW");\r
+ fghPlatformCloseWindowEGL(window);\r
+ fgDisplay.pDisplay.single_native_window = NULL;\r
+ break;\r
+ case APP_CMD_STOP:\r
+ LOGI("handle_cmd: APP_CMD_STOP");\r
+ break;\r
+ case APP_CMD_DESTROY: /* Activity.onDestroy */\r
+ LOGI("handle_cmd: APP_CMD_DESTROY");\r
+ /* User closed the application for good, let's kill the window */\r
+ {\r
+ /* Can't use fgWindowByHandle as app->window is NULL */\r
+ SFG_Window* window = fgStructure.CurrentWindow;\r
+ if (window != NULL) {\r
+ fgDestroyWindow(window);\r
+ } else {\r
+ LOGI("APP_CMD_DESTROY: No current window");\r
+ }\r
+ }\r
+ /* glue has already set android_app->destroyRequested=1 */\r
+ break;\r
+\r
+ case APP_CMD_CONFIG_CHANGED:\r
+ /* Handle rotation / orientation change */\r
+ LOGI("handle_cmd: APP_CMD_CONFIG_CHANGED");\r
+ break;\r
+ case APP_CMD_LOW_MEMORY:\r
+ LOGI("handle_cmd: APP_CMD_LOW_MEMORY");\r
+ break;\r
+ default:\r
+ LOGI("handle_cmd: unhandled cmd=%d", cmd);\r
+ }\r
+}\r
+\r
+void fgPlatformOpenWindow( SFG_Window* window, const char* title,\r
+ GLboolean positionUse, int x, int y,\r
+ GLboolean sizeUse, int w, int h,\r
+ GLboolean gameMode, GLboolean isSubWindow );\r
+\r
+void fgPlatformProcessSingleEvent ( void )\r
+{\r
+ /* When the screen is resized, the window handle still points to the\r
+ old window until the next SwapBuffer, while it's crucial to set\r
+ the size (onShape) correctly before the next onDisplay callback.\r
+ Plus we don't know if the next SwapBuffer already occurred at the\r
+ time we process the event (e.g. during onDisplay). */\r
+ /* So we do the check each time rather than on event. */\r
+ /* Interestingly, on a Samsung Galaxy S/PowerVR SGX540 GPU/Android\r
+ 2.3, that next SwapBuffer is fake (but still necessary to get the\r
+ new size). */\r
+ SFG_Window* window = fgStructure.CurrentWindow;\r
+ if (window != NULL && window->Window.Handle != NULL) {\r
+ int32_t width = ANativeWindow_getWidth(window->Window.Handle);\r
+ int32_t height = ANativeWindow_getHeight(window->Window.Handle);\r
+ fghOnReshapeNotify(width,height);\r
+ }\r
+\r
+ /* Read pending event. */\r
+ int ident;\r
+ int events;\r
+ struct android_poll_source* source;\r
+ /* This is called "ProcessSingleEvent" but this means we'd only\r
+ process ~60 (screen Hz) mouse events per second, plus other ports\r
+ are processing all events already. So let's process all pending\r
+ events. */\r
+ /* if ((ident=ALooper_pollOnce(0, NULL, &events, (void**)&source)) >= 0) { */\r
+ while ((ident=ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) {\r
+ /* Process this event. */\r
+ if (source != NULL) {\r
+ source->process(source->app, source);\r
+ }\r
+ }\r
+\r
+ /* If we're not in RESUME state, Android paused us, so wait */\r
+ struct android_app* app = fgDisplay.pDisplay.app;\r
+ if (app->destroyRequested != 1 && app->activityState != APP_CMD_RESUME) {\r
+ INVOKE_WCB(*window, Pause, ());\r
+\r
+ int FOREVER = -1;\r
+ while (app->destroyRequested != 1 && (app->activityState != APP_CMD_RESUME)) {\r
+ if ((ident=ALooper_pollOnce(FOREVER, NULL, &events, (void**)&source)) >= 0) {\r
+ /* Process this event. */\r
+ if (source != NULL) {\r
+ source->process(source->app, source);\r
+ }\r
+ }\r
+ }\r
+ /* Coming back from a pause: */\r
+ /* - Recreate window context and surface */\r
+ /* - Call user-defined hook to restore resources (textures...) */\r
+ /* - Exit pause loop */\r
+ if (app->destroyRequested != 1) {\r
+ /* Android is full-screen only, simplified call.. */\r
+ /* Ideally we'd have a fgPlatformReopenWindow() */\r
+ /* If we're hidden by a non-fullscreen or translucent activity,\r
+ we'll be paused but not stopped, and keep the current\r
+ surface; in which case fgPlatformOpenWindow will no-op. */\r
+ fgPlatformOpenWindow(window, "", GL_FALSE, 0, 0, GL_FALSE, 0, 0, GL_FALSE, GL_FALSE);\r
+\r
+ /* TODO: should there be a whole GLUT_INIT_WORK forced? probably...\r
+ * Could queue that up in APP_CMD_TERM_WINDOW handler, but it'll\r
+ * be not possible to ensure InitContext CB gets called before\r
+ * Resume CB like that.. so maybe just force calling initContext CB\r
+ * here is best. Or we could force work on the window in question..\r
+ * 1) save old work mask, 2) set mask to init only, 3) call fgPlatformProcessWork directly\r
+ * 4) set work mask back to the one saved in step 1.\r
+ */\r
+ if (!FETCH_WCB(*window, InitContext))\r
+ fgWarning("Resuming application, but no callback to reload context resources (glutInitContextFunc)");\r
+ }\r
+\r
+ INVOKE_WCB(*window, Resume, ());\r
+ }\r
+}\r
+\r
+void fgPlatformMainLoopPreliminaryWork ( void )\r
+{\r
+ LOGI("fgPlatformMainLoopPreliminaryWork\n");\r
+\r
+ key_init();\r
+\r
+ /* Make sure glue isn't stripped. */\r
+ /* JNI entry points need to be bundled even when linking statically */\r
+ app_dummy();\r
+}\r
+\r
+\r
+/* Step through the work list */\r
+void fgPlatformProcessWork(SFG_Window *window)\r
+{\r
+ unsigned int workMask = window->State.WorkMask;\r
+ /* Now clear it so that any callback generated by the actions below can set work again */\r
+ window->State.WorkMask = 0;\r
+\r
+ /* This is before the first display callback: call a few callbacks to inform user of window size, position, etc\r
+ * we know this is before the first display callback of a window as for all windows GLUT_INIT_WORK is set when\r
+ * they are opened, and work is done before displaying in the mainloop.\r
+ */\r
+ if (workMask & GLUT_INIT_WORK)\r
+ {\r
+ /* notify windowStatus/visibility */\r
+ INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );\r
+\r
+ /* Position callback, always at 0,0 */\r
+ fghOnPositionNotify(window, 0, 0, GL_TRUE);\r
+\r
+ /* Size gets notified on window creation with size detection in mainloop above\r
+ * XXX CHECK: does this messages happen too early like on windows,\r
+ * so client code cannot have registered a callback yet and the message\r
+ * is thus never received by client?\r
+ */\r
+\r
+ /* Call init context callback */\r
+ INVOKE_WCB( *window, InitContext, ());\r
+\r
+ /* Lastly, check if we have a display callback, error out if not\r
+ * This is the right place to do it, as the redisplay will be\r
+ * next right after we exit this function, so there is no more\r
+ * opportunity for the user to register a callback for this window.\r
+ */\r
+ if (!FETCH_WCB(*window, Display))\r
+ fgError ( "ERROR: No display callback registered for window %d\n", window->ID );\r
+ }\r
+\r
+ if (workMask & GLUT_FULL_SCREEN_WORK)\r
+ fgPlatformFullScreenToggle( window );\r
+ if (workMask & GLUT_POSITION_WORK)\r
+ fgPlatformPositionWindow( window, window->State.DesiredXpos, window->State.DesiredYpos );\r
+ if (workMask & GLUT_SIZE_WORK)\r
+ fgPlatformReshapeWindow ( window, window->State.DesiredWidth, window->State.DesiredHeight );\r
+ if (workMask & GLUT_ZORDER_WORK)\r
+ {\r
+ if (window->State.DesiredZOrder < 0)\r
+ fgPlatformPushWindow( window );\r
+ else\r
+ fgPlatformPopWindow( window );\r
+ }\r
+\r
+ if (workMask & GLUT_VISIBILITY_WORK)\r
+ {\r
+ /* Visibility status of window should get updated in the window message handlers\r
+ * For now, none of these functions called below do anything, so don't worry\r
+ * about it\r
+ */\r
+ SFG_Window *win = window;\r
+ switch (window->State.DesiredVisibility)\r
+ {\r
+ case DesireHiddenState:\r
+ fgPlatformHideWindow( window );\r
+ break;\r
+ case DesireIconicState:\r
+ /* Call on top-level window */\r
+ while (win->Parent)\r
+ win = win->Parent;\r
+ fgPlatformIconifyWindow( win );\r
+ break;\r
+ case DesireNormalState:\r
+ fgPlatformShowWindow( window );\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
void fgPlatformCreateWindow ( SFG_Window *window )
{
fghPlatformCreateWindowEGL(window);
- window->State.pWState.LastWidth = -1;
- window->State.pWState.LastHeight = -1;
}
#include "egl/fg_window_egl.h"
#include <android/native_app_glue/android_native_app_glue.h>
-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.
}
/*
- * 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 )
}
/*
- * 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");
}
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");
}
#include <GL/freeglut.h>
#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 );
};
+/*
+ * 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 */
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? */
};
# 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 );
/* -- 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 );
}
window->State.Visible )
{
window->State.Redisplay = GL_FALSE;
- fgPlatformDisplayWindow ( window );
+ fghRedrawWindow ( window );
}
fgEnumSubWindows( window, fghcbDisplayWindow, enumerator );
*/
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( );
+++ /dev/null
-/*
- * 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 <GL/freeglut.h>
-#include "fg_internal.h"
-
-extern void fghRedrawWindow(SFG_Window *window);
-
-#endif
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 ---------------------------------------------------- */
fgInitGL2();
- window->State.NeedToInitContext = GL_TRUE;
+ window->State.WorkMask |= GLUT_INIT_WORK;
}
/*
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;
}
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;
}
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;
}
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;
}
/*
glutLeaveFullScreen();
}
- fgPlatformGlutPositionWindow ( x, y );
+ fgStructure.CurrentWindow->State.WorkMask |= GLUT_POSITION_WORK;
+ fgStructure.CurrentWindow->State.DesiredXpos = x;
+ fgStructure.CurrentWindow->State.DesiredYpos = y;
}
/*
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;
}
/*
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;
}
/*
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;
}
/*
*/
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;
}
/*
*/
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;
}
/*
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
#include <GL/freeglut.h>
#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,
-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)
/* 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 */
#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)
/* 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.
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);
}
}
/* 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;
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 */
}
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
#include <GL/freeglut.h>
#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);
*/
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);
* 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 );
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
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 :
/* 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
typedef BOOL (WINAPI *pRegisterTouchWindow)(HWND,ULONG);
static pRegisterTouchWindow fghRegisterTouchWindow = (pRegisterTouchWindow)0xDEADBEEF;
#endif
-extern void fghNotifyWindowStatus(SFG_Window *window);
/*
* 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};
/* 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);
+ }
}
}
if( !sizeUse )
{
- if( ! window->IsMenu )
+ if( !window->IsMenu )
{
w = CW_USEDEFAULT;
h = CW_USEDEFAULT;
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 )
);
#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 );
}
-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
*/
}
-
-/*
- * 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
*/
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 -------------------------------------------------- */
{
int OldWidth; /* Window width from before a resize */
int OldHeight; /* " height " " " " */
+ GLboolean KeyRepeating; /* Currently in repeat mode? */
};
# 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);
* 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)...
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;
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;
}
{
}
+
+/* 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;
+ }
+ }
+}
+
#include <unistd.h> /* 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
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,
/*
- * 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 )
/*
- * 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 */
}
/*
- * 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;