Final commit before implementing main loop. All other code is implemented to the same level as Android.

git-svn-id: https://svn.code.sf.net/p/freeglut/code/trunk@1647 7f0cb862-5218-0410-a997-914c9d46530a
This commit is contained in:
dcnieho 2014-01-24 10:25:29 +00:00
parent 8dba97d8a1
commit 1357eb9fcf
3 changed files with 522 additions and 6 deletions

View File

@ -142,12 +142,10 @@ ELSEIF(ANDROID OR BLACKBERRY)
)
ELSE()
LIST(APPEND FREEGLUT_SRCS
# TODO: Anything similar to Android's app_glue?
src/blackberry/fg_init_blackberry.c
src/blackberry/fg_internal_blackberry.h
# TODO: src/blackberry/fg_main_blackberry.c
src/blackberry/fg_main_blackberry.c
src/blackberry/fg_main_blackberry.h
# TODO: Anything similar to fg_spaceball_android.c needed?
src/blackberry/fg_state_blackberry.c
src/blackberry/fg_window_blackberry.c
)

View File

@ -0,0 +1,518 @@
/*
* fg_main_blackberry.c
*
* The BlackBerry-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
* Copyright (C) 2013 Vincent Simonetti
*
* 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 <slog2.h>
#define LOGI(...) ((void)slog2fa(NULL, 1337, SLOG2_INFO, __VA_ARGS__))
#define LOGW(...) ((void)slog2fa(NULL, 1337, SLOG2_WARNING, __VA_ARGS__))
#include <sys/keycodes.h>
extern void fghOnReshapeNotify(SFG_Window *window, int width, int height, GLboolean forceNotify);
extern void fghOnPositionNotify(SFG_Window *window, int x, int y, GLboolean forceNotify);
extern void fgPlatformFullScreenToggle( SFG_Window *win );
extern void fgPlatformPositionWindow( SFG_Window *window, int x, int y );
extern void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height );
extern void fgPlatformPushWindow( SFG_Window *window );
extern void fgPlatformPopWindow( SFG_Window *window );
extern void fgPlatformHideWindow( SFG_Window *window );
extern void fgPlatformIconifyWindow( SFG_Window *window );
extern void fgPlatformShowWindow( SFG_Window *window );
static struct touchscreen touchscreen;
static unsigned char key_a2fg[256];
/**
* Initialize BlackBerry keycode to GLUT keycode mapping
*/
static void key_init() {
memset(key_a2fg, 0, sizeof(key_a2fg));
//XXX Any others?
key_a2fg[KEYCODE_F1] = GLUT_KEY_F1;
key_a2fg[KEYCODE_F2] = GLUT_KEY_F2;
key_a2fg[KEYCODE_F3] = GLUT_KEY_F3;
key_a2fg[KEYCODE_F4] = GLUT_KEY_F4;
key_a2fg[KEYCODE_F5] = GLUT_KEY_F5;
key_a2fg[KEYCODE_F6] = GLUT_KEY_F6;
key_a2fg[KEYCODE_F7] = GLUT_KEY_F7;
key_a2fg[KEYCODE_F8] = GLUT_KEY_F8;
key_a2fg[KEYCODE_F9] = GLUT_KEY_F9;
key_a2fg[KEYCODE_F10] = GLUT_KEY_F10;
key_a2fg[KEYCODE_F11] = GLUT_KEY_F11;
key_a2fg[KEYCODE_F12] = GLUT_KEY_F12;
key_a2fg[KEYCODE_PG_UP] = GLUT_KEY_PAGE_UP;
key_a2fg[KEYCODE_PG_DOWN] = GLUT_KEY_PAGE_DOWN;
key_a2fg[KEYCODE_HOME] = GLUT_KEY_HOME;
key_a2fg[KEYCODE_END] = GLUT_KEY_END;
key_a2fg[KEYCODE_INSERT] = GLUT_KEY_INSERT;
key_a2fg[KEYCODE_UP] = GLUT_KEY_UP;
key_a2fg[KEYCODE_DOWN] = GLUT_KEY_DOWN;
key_a2fg[KEYCODE_LEFT] = GLUT_KEY_LEFT;
key_a2fg[KEYCODE_RIGHT] = GLUT_KEY_RIGHT;
key_a2fg[KEYCODE_LEFT_ALT] = GLUT_KEY_ALT_L;
key_a2fg[KEYCODE_RIGHT_ALT] = GLUT_KEY_ALT_R;
key_a2fg[KEYCODE_LEFT_SHIFT] = GLUT_KEY_SHIFT_L;
key_a2fg[KEYCODE_RIGHT_SHIFT] = GLUT_KEY_SHIFT_R;
key_a2fg[KEYCODE_LEFT_CTRL] = GLUT_KEY_CTRL_L;
key_a2fg[KEYCODE_RIGHT_CTRL] = GLUT_KEY_CTRL_R;
}
//XXX
/* *
* 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;
}
*/
uint64_t 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 )
{
//TODO
}
/* *
* 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);
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 )
{
//TODO
//--------------------------------
/* When the screen is resized, the window handle still points to the
old window until the next SwapBuffer, while it's crucial to set
the size (onShape) correctly before the next onDisplay callback.
Plus we don't know if the next SwapBuffer already occurred at the
time we process the event (e.g. during onDisplay). * /
/* So we do the check each time rather than on event. * /
/* Interestingly, on a Samsung Galaxy S/PowerVR SGX540 GPU/Android
2.3, that next SwapBuffer is fake (but still necessary to get the
new size). * /
SFG_Window* window = fgStructure.CurrentWindow;
if (window != NULL && window->Window.Handle != NULL) {
int32_t width = ANativeWindow_getWidth(window->Window.Handle);
int32_t height = ANativeWindow_getHeight(window->Window.Handle);
fghOnReshapeNotify(window,width,height,GL_FALSE);
}
/* 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, AppStatus, (GLUT_APPSTATUS_PAUSE));
int FOREVER = -1;
while (app->destroyRequested != 1 && (app->activityState != APP_CMD_RESUME)) {
if ((ident=ALooper_pollOnce(FOREVER, NULL, &events, (void**)&source)) >= 0) {
/* Process this event. * /
if (source != NULL) {
source->process(source->app, source);
}
}
}
/* Coming back from a pause: * /
/* - Recreate window context and surface */
/* - Call user-defined hook to restore resources (textures...) * /
/* - Exit pause loop * /
if (app->destroyRequested != 1) {
/* Android is full-screen only, simplified call.. * /
/* Ideally we'd have a fgPlatformReopenWindow() * /
/* If we're hidden by a non-fullscreen or translucent activity,
we'll be paused but not stopped, and keep the current
surface; in which case fgPlatformOpenWindow will no-op. * /
fgPlatformOpenWindow(window, "", GL_FALSE, 0, 0, GL_FALSE, 0, 0, GL_FALSE, GL_FALSE);
/* TODO: should there be a whole GLUT_INIT_WORK forced? probably...
* Could queue that up in APP_CMD_TERM_WINDOW handler, but it'll
* be not possible to ensure InitContext CB gets called before
* Resume CB like that.. so maybe just force calling initContext CB
* here is best. Or we could force work on the window in question..
* 1) save old work mask, 2) set mask to init only, 3) call fgProcessWork directly
* 4) set work mask back to the one saved in step 1.
/
if (!FETCH_WCB(*window, InitContext))
fgWarning("Resuming application, but no callback to reload context resources (glutInitContextFunc)");
}
INVOKE_WCB(*window, AppStatus, (GLUT_APPSTATUS_RESUME));
}
*/
}
void fgPlatformMainLoopPreliminaryWork ( void )
{
LOGI("fgPlatformMainLoopPreliminaryWork\n", SLOG2_FA_END);
key_init();
}
/* deal with work list items */
void fgPlatformInitWork(SFG_Window* window)
{
/* notify windowStatus/visibility */
INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
/* Position callback, always at 0,0 */
fghOnPositionNotify(window, 0, 0, GL_TRUE);
/* Size gets notified on window creation with size detection in mainloop above
* XXX CHECK: does this messages happen too early like on windows,
* so client code cannot have registered a callback yet and the message
* is thus never received by client?
*/
}
void fgPlatformPosResZordWork(SFG_Window* window, unsigned int workMask)
{
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 );
}
}
void fgPlatformVisibilityWork(SFG_Window* window)
{
/* Visibility status of window should get updated in the window message handlers
* For now, none of these functions called below do anything, so don't worry
* about it
*/
SFG_Window *win = window;
switch (window->State.DesiredVisibility)
{
case DesireHiddenState:
fgPlatformHideWindow( window );
break;
case DesireIconicState:
/* Call on top-level window */
while (win->Parent)
win = win->Parent;
fgPlatformIconifyWindow( win );
break;
case DesireNormalState:
fgPlatformShowWindow( window );
break;
}
}

View File

@ -29,9 +29,9 @@
#include <GL/freeglut.h>
#include "fg_internal.h"
/*extern void fgPlatformProcessSingleEvent(void);
extern unsigned long fgPlatformSystemTime(void);
extern void fgPlatformProcessSingleEvent(void);
extern uint64_t fgPlatformSystemTime(void);
extern void fgPlatformSleepForEvents(long msec);
extern void fgPlatformMainLoopPreliminaryWork(void);*/
extern void fgPlatformMainLoopPreliminaryWork(void);
#endif