Add base Android and EGL code
git-svn-id: https://svn.code.sf.net/p/freeglut/code/trunk@1101 7f0cb862-5218-0410-a997-914c9d46530a
This commit is contained in:
parent
cf7613066d
commit
c8a8d03fc5
20
.gitattributes
vendored
20
.gitattributes
vendored
@ -47,6 +47,8 @@ freeglut/freeglut/VisualStudio/2010/smooth_opengl3/smooth_opengl3.vcxproj -text
|
||||
freeglut/freeglut/VisualStudio/2010/smooth_opengl3/smooth_opengl3.vcxproj.filters -text
|
||||
freeglut/freeglut/VisualStudio/2010/subwin/subwin.vcxproj -text
|
||||
freeglut/freeglut/VisualStudio/2010/subwin/subwin.vcxproj.filters -text
|
||||
freeglut/freeglut/android/Android.mk -text
|
||||
freeglut/freeglut/android/README -text
|
||||
freeglut/freeglut/autogen.sh svn_keywords=Author+Date+Id+Revision
|
||||
freeglut/freeglut/config.h.in svn_keywords=Author+Date+Id+Revision
|
||||
freeglut/freeglut/configure.ac svn_keywords=Author+Date+Id+Revision
|
||||
@ -156,6 +158,24 @@ freeglut/freeglut/src/Common/freeglutdll.def svn_keywords=Author+Date+Id+Revisio
|
||||
freeglut/freeglut/src/Common/xparsegeometry_repl.c -text
|
||||
freeglut/freeglut/src/Common/xparsegeometry_repl.h -text
|
||||
freeglut/freeglut/src/Makefile.am svn_keywords=Author+Date+Id+Revision
|
||||
freeglut/freeglut/src/android/freeglut_gamemode_android.c -text
|
||||
freeglut/freeglut/src/android/freeglut_input_devices_android.c -text
|
||||
freeglut/freeglut/src/android/freeglut_internal_android.h -text
|
||||
freeglut/freeglut/src/android/freeglut_joystick_android.c -text
|
||||
freeglut/freeglut/src/android/freeglut_main_android.c -text
|
||||
freeglut/freeglut/src/android/freeglut_runtime_android.c -text
|
||||
freeglut/freeglut/src/android/freeglut_spaceball_android.c -text
|
||||
freeglut/freeglut/src/android/freeglut_state_android.c -text
|
||||
freeglut/freeglut/src/android/freeglut_window_android.c -text
|
||||
freeglut/freeglut/src/android/native_app_glue/README -text
|
||||
freeglut/freeglut/src/android/native_app_glue/android_native_app_glue.c -text
|
||||
freeglut/freeglut/src/android/native_app_glue/android_native_app_glue.h -text
|
||||
freeglut/freeglut/src/android/opengles_stubs.c -text
|
||||
freeglut/freeglut/src/egl/freeglut_display_egl.c -text
|
||||
freeglut/freeglut/src/egl/freeglut_init_egl.c -text
|
||||
freeglut/freeglut/src/egl/freeglut_internal_egl.h -text
|
||||
freeglut/freeglut/src/egl/freeglut_structure_egl.c -text
|
||||
freeglut/freeglut/src/egl/freeglut_window_egl.c -text
|
||||
freeglut/freeglut/src/mswin/freeglut_cursor_mswin.c svn_keywords=Author+Date+Id+Revision
|
||||
freeglut/freeglut/src/mswin/freeglut_display_mswin.c svn_keywords=Author+Date+Id+Revision
|
||||
freeglut/freeglut/src/mswin/freeglut_ext_mswin.c svn_keywords=Author+Date+Id+Revision
|
||||
|
9
freeglut/freeglut/android/Android.mk
Normal file
9
freeglut/freeglut/android/Android.mk
Normal file
@ -0,0 +1,9 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := freeglut
|
||||
LOCAL_SRC_FILES := lib/libglut.a
|
||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
|
||||
|
||||
include $(PREBUILT_STATIC_LIBRARY)
|
2
freeglut/freeglut/android/README
Normal file
2
freeglut/freeglut/android/README
Normal file
@ -0,0 +1,2 @@
|
||||
- Android.mk : used to create a module compatible with the NDK build
|
||||
system. See ../README.android for details.
|
57
freeglut/freeglut/src/android/freeglut_gamemode_android.c
Normal file
57
freeglut/freeglut/src/android/freeglut_gamemode_android.c
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* freeglut_gamemode_x11.c
|
||||
*
|
||||
* The game mode handling code.
|
||||
*
|
||||
* 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 "../Common/freeglut_internal.h"
|
||||
|
||||
/*
|
||||
* Changes the current display mode to match user's settings
|
||||
*/
|
||||
GLboolean fgPlatformChangeDisplayMode( GLboolean haveToTest )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformChangeDisplayMode: STUB\n");
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
void fgPlatformEnterGameMode( void )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformEnterGameMode: STUB\n");
|
||||
}
|
||||
|
||||
void fgPlatformRememberState( void )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformRememberState: STUB\n");
|
||||
}
|
||||
|
||||
void fgPlatformRestoreState( void )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformRestoreState: STUB\n");
|
||||
}
|
||||
|
||||
void fgPlatformLeaveGameMode( void )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformLeaveGameMode: STUB\n");
|
||||
}
|
||||
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* freeglut_input_devices_android.c
|
||||
*
|
||||
* Handles miscellaneous input devices via direct serial-port access.
|
||||
*
|
||||
* Written by Joe Krahn <krahn@niehs.nih.gov> 2005
|
||||
* Copyright (c) 2005 Stephen J. Baker. All Rights Reserved.
|
||||
* Copied for Platform code by Evan Felix <karcaw at gmail.com>
|
||||
* Copyright 2012 (C) Sylvain Beucler
|
||||
* Creation date: Thur Feb 2 2012
|
||||
*
|
||||
* 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 OR STEPHEN J. BAKER 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 "../Common/freeglut_internal.h"
|
||||
typedef struct _serialport SERIALPORT;
|
||||
|
||||
/*
|
||||
* Try initializing the input device(s)
|
||||
*/
|
||||
void fgPlatformRegisterDialDevice ( const char *dial_device )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformRegisterDialDevice: STUB\n");
|
||||
}
|
||||
|
||||
SERIALPORT *serial_open ( const char *device )
|
||||
{
|
||||
fprintf(stderr, "serial_open: STUB\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void serial_close(SERIALPORT *port)
|
||||
{
|
||||
fprintf(stderr, "serial_close: STUB\n");
|
||||
}
|
||||
|
||||
int serial_getchar(SERIALPORT *port)
|
||||
{
|
||||
fprintf(stderr, "serial_getchar: STUB\n");
|
||||
return EOF;
|
||||
}
|
||||
|
||||
int serial_putchar(SERIALPORT *port, unsigned char ch)
|
||||
{
|
||||
fprintf(stderr, "serial_putchar: STUB\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void serial_flush ( SERIALPORT *port )
|
||||
{
|
||||
fprintf(stderr, "serial_flush: STUB\n");
|
||||
}
|
86
freeglut/freeglut/src/android/freeglut_internal_android.h
Normal file
86
freeglut/freeglut/src/android/freeglut_internal_android.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* freeglut_internal_android.h
|
||||
*
|
||||
* The freeglut library private include file.
|
||||
*
|
||||
* 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 FREEGLUT_INTERNAL_ANDROID_H
|
||||
#define FREEGLUT_INTERNAL_ANDROID_H
|
||||
|
||||
|
||||
/* -- PLATFORM-SPECIFIC INCLUDES ------------------------------------------- */
|
||||
/* Android OpenGL ES is accessed through EGL */
|
||||
#include "../egl/freeglut_internal_egl.h"
|
||||
|
||||
/**
|
||||
* Virtual PAD (spots on touchscreen that simulate keys)
|
||||
*/
|
||||
struct vpad_state {
|
||||
bool on;
|
||||
bool left;
|
||||
bool right;
|
||||
bool up;
|
||||
bool down;
|
||||
};
|
||||
struct touchscreen {
|
||||
struct vpad_state vpad;
|
||||
bool in_mmotion;
|
||||
};
|
||||
|
||||
|
||||
/* -- JOYSTICK-SPECIFIC STRUCTURES AND TYPES ------------------------------- */
|
||||
/*
|
||||
* Initial defines from "js.h" starting around line 33 with the existing "freeglut_joystick.c"
|
||||
* interspersed
|
||||
*/
|
||||
|
||||
/*
|
||||
* We'll put these values in and that should
|
||||
* allow the code to at least compile when there is
|
||||
* no support. The JS open routine should error out
|
||||
* and shut off all the code downstream anyway and if
|
||||
* the application doesn't use a joystick we'll be fine.
|
||||
*/
|
||||
|
||||
struct JS_DATA_TYPE
|
||||
{
|
||||
int buttons;
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
# define JS_RETURN (sizeof(struct JS_DATA_TYPE))
|
||||
|
||||
/* XXX It might be better to poll the operating system for the numbers of buttons and
|
||||
* XXX axes and then dynamically allocate the arrays.
|
||||
*/
|
||||
# define _JS_MAX_AXES 16
|
||||
typedef struct tagSFG_PlatformJoystick SFG_PlatformJoystick;
|
||||
struct tagSFG_PlatformJoystick
|
||||
{
|
||||
struct JS_DATA_TYPE js;
|
||||
|
||||
char fname [ 128 ];
|
||||
int fd;
|
||||
};
|
||||
|
||||
#endif /* FREEGLUT_INTERNAL_ANDROID_H */
|
50
freeglut/freeglut/src/android/freeglut_joystick_android.c
Normal file
50
freeglut/freeglut/src/android/freeglut_joystick_android.c
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* freeglut_joystick_android.c
|
||||
*
|
||||
* Joystick handling code
|
||||
*
|
||||
* Copyright (c) 1999-2000 Pawel W. Olszta. All Rights Reserved.
|
||||
* Written by Steve Baker, <sjbaker1@airmail.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 "../Common/freeglut_internal.h"
|
||||
|
||||
void fgPlatformJoystickRawRead( SFG_Joystick* joy, int* buttons, float* axes )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformJoystickRawRead: STUB\n");
|
||||
}
|
||||
|
||||
void fgPlatformJoystickOpen( SFG_Joystick* joy )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformJoystickOpen: STUB\n");
|
||||
}
|
||||
|
||||
void fgPlatformJoystickInit( SFG_Joystick *fgJoystick[], int ident )
|
||||
{
|
||||
fprintf(stderr, "fgJoystick: STUB\n");
|
||||
}
|
||||
|
||||
void fgPlatformJoystickClose ( int ident )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformJoystickClose: STUB\n");
|
||||
}
|
403
freeglut/freeglut/src/android/freeglut_main_android.c
Normal file
403
freeglut/freeglut/src/android/freeglut_main_android.c
Normal file
@ -0,0 +1,403 @@
|
||||
/*
|
||||
* freeglut_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 "Common/freeglut_internal.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); */
|
||||
|
||||
return ascii;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a window configuration change. When no reshape
|
||||
* callback is hooked, the viewport size is updated to
|
||||
* match the new window size.
|
||||
*/
|
||||
void fgPlatformReshapeWindow ( SFG_Window *window, int width, int height )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformReshapeWindow: STUB\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* A static helper function to execute display callback for a window
|
||||
*/
|
||||
void fgPlatformDisplayWindow ( SFG_Window *window )
|
||||
{
|
||||
fghRedrawWindow ( window ) ;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
/* fprintf(stderr, "fgPlatformSleepForEvents: STUB\n"); */
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the next input event.
|
||||
*/
|
||||
int32_t handle_input(struct android_app* app, AInputEvent* event) {
|
||||
SFG_Window* window = fgStructure.CurrentWindow;
|
||||
|
||||
/* FIXME: in Android, when 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 */
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
|
||||
int32_t action = AMotionEvent_getAction(event);
|
||||
float x = AMotionEvent_getX(event, 0);
|
||||
float y = AMotionEvent_getY(event, 0);
|
||||
LOGI("motion %.01f,%.01f action=%d", x, y, AMotionEvent_getAction(event));
|
||||
|
||||
/* 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;
|
||||
LOGI("Changed mouse position: %d,%d", x, 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) {
|
||||
switch (cmd) {
|
||||
case APP_CMD_SAVE_STATE:
|
||||
/* The system has asked us to save our current state. Do so. */
|
||||
LOGI("handle_cmd: APP_CMD_SAVE_STATE");
|
||||
break;
|
||||
case APP_CMD_INIT_WINDOW:
|
||||
/* The window is being shown, get it ready. */
|
||||
LOGI("handle_cmd: APP_CMD_INIT_WINDOW");
|
||||
fgDisplay.pDisplay.single_window->Window.Handle = app->window;
|
||||
/* glPlatformOpenWindow was waiting for Handle to be defined and
|
||||
will now return from fgPlatformProcessSingleEvent() */
|
||||
break;
|
||||
case APP_CMD_TERM_WINDOW:
|
||||
/* The window is being hidden or closed, clean it up. */
|
||||
LOGI("handle_cmd: APP_CMD_TERM_WINDOW");
|
||||
fgDestroyWindow(fgDisplay.pDisplay.single_window);
|
||||
break;
|
||||
case APP_CMD_DESTROY:
|
||||
/* Not reached because GLUT exit()s when last window is closed */
|
||||
LOGI("handle_cmd: APP_CMD_DESTROY");
|
||||
break;
|
||||
case APP_CMD_GAINED_FOCUS:
|
||||
LOGI("handle_cmd: APP_CMD_GAINED_FOCUS");
|
||||
break;
|
||||
case APP_CMD_LOST_FOCUS:
|
||||
LOGI("handle_cmd: APP_CMD_LOST_FOCUS");
|
||||
break;
|
||||
case APP_CMD_CONFIG_CHANGED:
|
||||
/* Handle rotation / orientation change */
|
||||
LOGI("handle_cmd: APP_CMD_CONFIG_CHANGED");
|
||||
break;
|
||||
case APP_CMD_WINDOW_RESIZED:
|
||||
LOGI("handle_cmd: APP_CMD_WINDOW_RESIZED");
|
||||
if (fgDisplay.pDisplay.single_window->Window.pContext.eglSurface != EGL_NO_SURFACE)
|
||||
/* Make ProcessSingleEvent detect the new size, only available
|
||||
after the next SwapBuffer */
|
||||
glutPostRedisplay();
|
||||
break;
|
||||
default:
|
||||
LOGI("handle_cmd: unhandled cmd=%d", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void fgPlatformProcessSingleEvent ( void )
|
||||
{
|
||||
static int32_t last_width = -1;
|
||||
static int32_t last_height = -1;
|
||||
|
||||
/* 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 = fgDisplay.pDisplay.single_window;
|
||||
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 != last_width || height != last_height) {
|
||||
last_width = width;
|
||||
last_height = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fgPlatformMainLoopPreliminaryWork ( void )
|
||||
{
|
||||
printf("fgPlatformMainLoopPreliminaryWork\n");
|
||||
|
||||
key_init();
|
||||
|
||||
/* Make sure glue isn't stripped. */
|
||||
/* JNI entry points need to be bundled even when linking statically */
|
||||
app_dummy();
|
||||
}
|
||||
|
||||
void fgPlatformDeinitialiseInputDevices ( void )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformDeinitialiseInputDevices: STUB\n");
|
||||
}
|
160
freeglut/freeglut/src/android/freeglut_runtime_android.c
Normal file
160
freeglut/freeglut/src/android/freeglut_runtime_android.c
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* freeglut_runtime_android.c
|
||||
*
|
||||
* Android runtime
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* Parts taken from Android NDK's 'native-activity' sample : */
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#include <android/asset_manager.h>
|
||||
#include <android/native_window.h>
|
||||
#include "android/native_app_glue/android_native_app_glue.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__))
|
||||
|
||||
/* Cf. freeglut_main_android.c */
|
||||
extern int32_t handle_input(struct android_app* app, AInputEvent* event);
|
||||
extern void handle_cmd(struct android_app* app, int32_t cmd);
|
||||
|
||||
extern int main(int argc, char* argv[]);
|
||||
|
||||
/** NativeActivity Callbacks **/
|
||||
/* Caution: they are called in the native_activity thread, not the
|
||||
FreeGLUT thread. Use android_app_write_cmd. */
|
||||
|
||||
/* Could be used instead of onNativeWindowRedrawNeeded */
|
||||
/* Deals with status bar presence */
|
||||
static void onContentRectChanged(ANativeActivity* activity, const ARect* rect) {
|
||||
LOGI("onContentRectChanged: l=%d,t=%d,r=%d,b=%d", rect->left, rect->top, rect->right, rect->bottom);
|
||||
}
|
||||
|
||||
/* Bug: not called during a resize in android-9, only once on startup :/ */
|
||||
static void onNativeWindowResized(ANativeActivity* activity, ANativeWindow* window) {
|
||||
LOGI("onNativeWindowResized: %p\n", (void*)activity);
|
||||
}
|
||||
|
||||
/* Called after a resize, compensate broken onNativeWindowResized */
|
||||
static void onNativeWindowRedrawNeeded(ANativeActivity* activity, ANativeWindow* window) {
|
||||
LOGI("onNativeWindowRedrawNeeded: %p\n", (void*)activity);
|
||||
struct android_app* app = (struct android_app*)activity->instance;
|
||||
//if (fgDisplay.pDisplay.single_window->Window.pContext.eglSurface != EGL_NO_SURFACE)
|
||||
android_app_write_cmd(app, APP_CMD_WINDOW_RESIZED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all .apk assets to the application directory so they can be
|
||||
* accessed using accessed.
|
||||
* TODO: parse directories recursively
|
||||
*/
|
||||
static void extract_assets(struct android_app* app) {
|
||||
/* Get usable JNI context */
|
||||
JNIEnv* env = app->activity->env;
|
||||
JavaVM* vm = app->activity->vm;
|
||||
(*vm)->AttachCurrentThread(vm, &env, NULL);
|
||||
|
||||
{
|
||||
/* Get a handle on our calling NativeActivity class */
|
||||
jclass activityClass = (*env)->GetObjectClass(env, app->activity->clazz);
|
||||
|
||||
/* Get path to cache dir (/data/data/org.myapp/cache) */
|
||||
jmethodID getCacheDir = (*env)->GetMethodID(env, activityClass, "getCacheDir", "()Ljava/io/File;");
|
||||
jobject file = (*env)->CallObjectMethod(env, app->activity->clazz, getCacheDir);
|
||||
jclass fileClass = (*env)->FindClass(env, "java/io/File");
|
||||
jmethodID getAbsolutePath = (*env)->GetMethodID(env, fileClass, "getAbsolutePath", "()Ljava/lang/String;");
|
||||
jstring jpath = (jstring)(*env)->CallObjectMethod(env, file, getAbsolutePath);
|
||||
const char* app_dir = (*env)->GetStringUTFChars(env, jpath, NULL);
|
||||
|
||||
/* chdir in the application cache directory */
|
||||
LOGI("app_dir: %s", app_dir);
|
||||
chdir(app_dir);
|
||||
(*env)->ReleaseStringUTFChars(env, jpath, app_dir);
|
||||
|
||||
/* Pre-extract assets, to avoid Android-specific file opening */
|
||||
{
|
||||
AAssetManager* mgr = app->activity->assetManager;
|
||||
AAssetDir* assetDir = AAssetManager_openDir(mgr, "");
|
||||
const char* filename = (const char*)NULL;
|
||||
while ((filename = AAssetDir_getNextFileName(assetDir)) != NULL) {
|
||||
AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_STREAMING);
|
||||
char buf[BUFSIZ];
|
||||
int nb_read = 0;
|
||||
FILE* out = fopen(filename, "w");
|
||||
while ((nb_read = AAsset_read(asset, buf, BUFSIZ)) > 0)
|
||||
fwrite(buf, nb_read, 1, out);
|
||||
fclose(out);
|
||||
AAsset_close(asset);
|
||||
}
|
||||
AAssetDir_close(assetDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the main entry point of a native application that is using
|
||||
* android_native_app_glue. It runs in its own thread, with its own
|
||||
* event loop for receiving input events and doing other things.
|
||||
*/
|
||||
void android_main(struct android_app* app) {
|
||||
LOGI("android_main");
|
||||
|
||||
// Register window resize callback
|
||||
app->activity->callbacks->onNativeWindowResized = onNativeWindowResized;
|
||||
app->activity->callbacks->onContentRectChanged = onContentRectChanged;
|
||||
app->activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
|
||||
|
||||
app->onAppCmd = handle_cmd;
|
||||
app->onInputEvent = handle_input;
|
||||
|
||||
extract_assets(app);
|
||||
|
||||
/* Call user's main */
|
||||
{
|
||||
char progname[5] = "self";
|
||||
char* argv[] = {progname, NULL};
|
||||
main(1, argv);
|
||||
}
|
||||
|
||||
LOGI("android_main: end");
|
||||
exit(0);
|
||||
}
|
58
freeglut/freeglut/src/android/freeglut_spaceball_android.c
Normal file
58
freeglut/freeglut/src/android/freeglut_spaceball_android.c
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* freeglut_spaceball_android.c
|
||||
*
|
||||
* Spaceball support for Windows
|
||||
*
|
||||
* Copyright (c) 2012 Stephen J. Baker. All Rights Reserved.
|
||||
* Written by Evan Felix <karcaw at gmail.com>
|
||||
* Creation date: Sat Feb 4, 2012
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* This code is a very complicated way of doing nothing.
|
||||
* But is needed for Android platform builds.
|
||||
*/
|
||||
|
||||
#include <GL/freeglut.h>
|
||||
#include "../Common/freeglut_internal.h"
|
||||
|
||||
void fgPlatformInitializeSpaceball(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void fgPlatformSpaceballClose(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int fgPlatformHasSpaceball(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fgPlatformSpaceballNumButtons(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fgPlatformSpaceballSetWindow(SFG_Window *window)
|
||||
{
|
||||
return;
|
||||
}
|
113
freeglut/freeglut/src/android/freeglut_state_android.c
Normal file
113
freeglut/freeglut/src/android/freeglut_state_android.c
Normal file
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* freeglut_state_android.c
|
||||
*
|
||||
* Android-specific freeglut state query methods.
|
||||
*
|
||||
* Copyright (c) 2012 Stephen J. Baker. All Rights Reserved.
|
||||
* Written by John F. Fay, <fayjf@sourceforge.net>
|
||||
* 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 "../Common/freeglut_internal.h"
|
||||
|
||||
#include <android/native_window.h>
|
||||
|
||||
int fgPlatformGlutGet ( GLenum eWhat )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutGet: STUB\n");
|
||||
|
||||
switch (eWhat) {
|
||||
case GLUT_WINDOW_WIDTH:
|
||||
case GLUT_WINDOW_HEIGHT:
|
||||
{
|
||||
if ( fgStructure.CurrentWindow == NULL )
|
||||
return 0;
|
||||
int32_t width = ANativeWindow_getWidth(fgStructure.CurrentWindow->Window.Handle);
|
||||
int32_t height = ANativeWindow_getHeight(fgStructure.CurrentWindow->Window.Handle);
|
||||
switch ( eWhat )
|
||||
{
|
||||
case GLUT_WINDOW_WIDTH:
|
||||
return width;
|
||||
case GLUT_WINDOW_HEIGHT:
|
||||
return height;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fgPlatformGlutDeviceGet ( GLenum eWhat )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutDeviceGet: STUB\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fgPlatformGlutLayerGet( GLenum eWhat )
|
||||
{
|
||||
/*
|
||||
* This is easy as layers are not implemented ;-)
|
||||
*
|
||||
* XXX Can we merge the UNIX/X11 and WIN32 sections? Or
|
||||
* XXX is overlay support planned?
|
||||
*/
|
||||
switch( eWhat )
|
||||
{
|
||||
case GLUT_OVERLAY_POSSIBLE:
|
||||
return 0;
|
||||
|
||||
case GLUT_LAYER_IN_USE:
|
||||
return GLUT_NORMAL;
|
||||
|
||||
case GLUT_HAS_OVERLAY:
|
||||
return 0;
|
||||
|
||||
case GLUT_TRANSPARENT_INDEX:
|
||||
/*
|
||||
* Return just anything, which is always defined as zero
|
||||
*
|
||||
* XXX HUH?
|
||||
*/
|
||||
return 0;
|
||||
|
||||
case GLUT_NORMAL_DAMAGED:
|
||||
/* XXX Actually I do not know. Maybe. */
|
||||
return 0;
|
||||
|
||||
case GLUT_OVERLAY_DAMAGED:
|
||||
return -1;
|
||||
|
||||
default:
|
||||
fgWarning( "glutLayerGet(): missing enum handle %d", eWhat );
|
||||
break;
|
||||
}
|
||||
|
||||
/* And fail. That's good. Programs do love failing. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int *fgPlatformGlutGetModeValues(GLenum eWhat, int *size)
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutGetModeValues: STUB\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
165
freeglut/freeglut/src/android/freeglut_window_android.c
Normal file
165
freeglut/freeglut/src/android/freeglut_window_android.c
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* freeglut_window_android.c
|
||||
*
|
||||
* Window management methods for Android
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define FREEGLUT_BUILDING_LIB
|
||||
#include <GL/freeglut.h>
|
||||
#include "../Common/freeglut_internal.h"
|
||||
extern EGLSurface fghEGLPlatformOpenWindow( EGLNativeWindowType handle );
|
||||
|
||||
/*
|
||||
* Opens a window. Requires a SFG_Window object created and attached
|
||||
* to the freeglut structure. OpenGL context is created here.
|
||||
*/
|
||||
void fgPlatformOpenWindow( SFG_Window* window, const char* title,
|
||||
GLboolean positionUse, int x, int y,
|
||||
GLboolean sizeUse, int w, int h,
|
||||
GLboolean gameMode, GLboolean isSubWindow )
|
||||
{
|
||||
printf("fgPlatformOpenWindow %p ID=%d\n", (void*)window, window->ID);
|
||||
|
||||
/* TODO: only one full-screen window possible? */
|
||||
static int nb_windows = 0;
|
||||
if (nb_windows == 0) {
|
||||
nb_windows++;
|
||||
fgDisplay.pDisplay.single_window = window;
|
||||
printf("=> %p ID=%d\n", (void*)fgDisplay.pDisplay.single_window, fgDisplay.pDisplay.single_window->ID);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wait until window is available and OpenGL context is created */
|
||||
/* Normally events are processed through glutMainLoop(), but the
|
||||
user didn't call it yet, and the Android may not have initialized
|
||||
the View yet. So we need to wait for that to happen. */
|
||||
/* We can't return from this function before the OpenGL context is
|
||||
properly made current with a valid surface. So we wait for the
|
||||
surface. */
|
||||
while (fgDisplay.pDisplay.single_window->Window.Handle == NULL) {
|
||||
/* APP_CMD_INIT_WINDOW will do the job */
|
||||
fgPlatformProcessSingleEvent();
|
||||
}
|
||||
|
||||
EGLDisplay display = fgDisplay.pDisplay.eglDisplay;
|
||||
EGLint format = fgDisplay.pDisplay.eglContextFormat;
|
||||
ANativeWindow_setBuffersGeometry(window->Window.Handle, 0, 0, format);
|
||||
window->Window.pContext.eglSurface = fghEGLPlatformOpenWindow(window->Window.Handle);
|
||||
|
||||
window->State.Visible = GL_TRUE;
|
||||
}
|
||||
|
||||
void fgPlatformSetWindow ( SFG_Window *window )
|
||||
{
|
||||
/* TODO: only a single window possible? */
|
||||
}
|
||||
|
||||
/*
|
||||
* This function makes the current window visible
|
||||
*/
|
||||
void fgPlatformGlutShowWindow( void )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutShowWindow: STUB\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* This function hides the current window
|
||||
*/
|
||||
void fgPlatformGlutHideWindow( void )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutHideWindow: STUB\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Iconify the current window (top-level windows only)
|
||||
*/
|
||||
void fgPlatformGlutIconifyWindow( void )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutIconifyWindow: STUB\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the current window's title
|
||||
*/
|
||||
void fgPlatformGlutSetWindowTitle( const char* title )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutSetWindowTitle: STUB\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the current window's iconified title
|
||||
*/
|
||||
void fgPlatformGlutSetIconTitle( const char* title )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutSetIconTitle: STUB\n");}
|
||||
|
||||
/*
|
||||
* Change the current window's position
|
||||
*/
|
||||
void fgPlatformGlutPositionWindow( int x, int y )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutPositionWindow: STUB\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Lowers the current window (by Z order change)
|
||||
*/
|
||||
void fgPlatformGlutPushWindow( void )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutPushWindow: STUB\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Raises the current window (by Z order change)
|
||||
*/
|
||||
void fgPlatformGlutPopWindow( void )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutPopWindow: STUB\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Resize the current window so that it fits the whole screen
|
||||
*/
|
||||
void fgPlatformGlutFullScreen( SFG_Window *win )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutFullScreen: STUB\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are fullscreen, resize the current window back to its original size
|
||||
*/
|
||||
void fgPlatformGlutLeaveFullScreen( SFG_Window *win )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutLeaveFullScreen: STUB\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Toggle the window's full screen state.
|
||||
*/
|
||||
void fgPlatformGlutFullScreenToggle( SFG_Window *win )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformGlutFullScreenToggle: STUB\n");
|
||||
}
|
7
freeglut/freeglut/src/android/native_app_glue/README
Normal file
7
freeglut/freeglut/src/android/native_app_glue/README
Normal file
@ -0,0 +1,7 @@
|
||||
This code is copied from the Android NDK r7, from
|
||||
source/android/native_app_glue/ .
|
||||
|
||||
A few GCC warnings were suppressed.
|
||||
|
||||
'android_app_write_cmd' was made non-static so that resize events can
|
||||
be injected from FreeGLUT.
|
@ -0,0 +1,436 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "android_native_app_glue.h"
|
||||
#include <android/log.h>
|
||||
|
||||
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
|
||||
|
||||
static void free_saved_state(struct android_app* android_app) {
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
if (android_app->savedState != NULL) {
|
||||
free(android_app->savedState);
|
||||
android_app->savedState = NULL;
|
||||
android_app->savedStateSize = 0;
|
||||
}
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
}
|
||||
|
||||
int8_t android_app_read_cmd(struct android_app* android_app) {
|
||||
int8_t cmd;
|
||||
if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
|
||||
switch (cmd) {
|
||||
case APP_CMD_SAVE_STATE:
|
||||
free_saved_state(android_app);
|
||||
break;
|
||||
}
|
||||
return cmd;
|
||||
} else {
|
||||
LOGI("No data on command pipe!");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void print_cur_config(struct android_app* android_app) {
|
||||
char lang[2], country[2];
|
||||
AConfiguration_getLanguage(android_app->config, lang);
|
||||
AConfiguration_getCountry(android_app->config, country);
|
||||
|
||||
LOGI("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
|
||||
"keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
|
||||
"modetype=%d modenight=%d",
|
||||
AConfiguration_getMcc(android_app->config),
|
||||
AConfiguration_getMnc(android_app->config),
|
||||
lang[0], lang[1], country[0], country[1],
|
||||
AConfiguration_getOrientation(android_app->config),
|
||||
AConfiguration_getTouchscreen(android_app->config),
|
||||
AConfiguration_getDensity(android_app->config),
|
||||
AConfiguration_getKeyboard(android_app->config),
|
||||
AConfiguration_getNavigation(android_app->config),
|
||||
AConfiguration_getKeysHidden(android_app->config),
|
||||
AConfiguration_getNavHidden(android_app->config),
|
||||
AConfiguration_getSdkVersion(android_app->config),
|
||||
AConfiguration_getScreenSize(android_app->config),
|
||||
AConfiguration_getScreenLong(android_app->config),
|
||||
AConfiguration_getUiModeType(android_app->config),
|
||||
AConfiguration_getUiModeNight(android_app->config));
|
||||
}
|
||||
|
||||
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
|
||||
switch (cmd) {
|
||||
case APP_CMD_INPUT_CHANGED:
|
||||
LOGI("APP_CMD_INPUT_CHANGED\n");
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
if (android_app->inputQueue != NULL) {
|
||||
AInputQueue_detachLooper(android_app->inputQueue);
|
||||
}
|
||||
android_app->inputQueue = android_app->pendingInputQueue;
|
||||
if (android_app->inputQueue != NULL) {
|
||||
LOGI("Attaching input queue to looper");
|
||||
AInputQueue_attachLooper(android_app->inputQueue,
|
||||
android_app->looper, LOOPER_ID_INPUT, NULL,
|
||||
&android_app->inputPollSource);
|
||||
}
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
break;
|
||||
|
||||
case APP_CMD_INIT_WINDOW:
|
||||
LOGI("APP_CMD_INIT_WINDOW\n");
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->window = android_app->pendingWindow;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
break;
|
||||
|
||||
case APP_CMD_TERM_WINDOW:
|
||||
LOGI("APP_CMD_TERM_WINDOW\n");
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
break;
|
||||
|
||||
case APP_CMD_RESUME:
|
||||
case APP_CMD_START:
|
||||
case APP_CMD_PAUSE:
|
||||
case APP_CMD_STOP:
|
||||
LOGI("activityState=%d\n", cmd);
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->activityState = cmd;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
break;
|
||||
|
||||
case APP_CMD_CONFIG_CHANGED:
|
||||
LOGI("APP_CMD_CONFIG_CHANGED\n");
|
||||
AConfiguration_fromAssetManager(android_app->config,
|
||||
android_app->activity->assetManager);
|
||||
print_cur_config(android_app);
|
||||
break;
|
||||
|
||||
case APP_CMD_DESTROY:
|
||||
LOGI("APP_CMD_DESTROY\n");
|
||||
android_app->destroyRequested = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
|
||||
switch (cmd) {
|
||||
case APP_CMD_TERM_WINDOW:
|
||||
LOGI("APP_CMD_TERM_WINDOW\n");
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->window = NULL;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
break;
|
||||
|
||||
case APP_CMD_SAVE_STATE:
|
||||
LOGI("APP_CMD_SAVE_STATE\n");
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->stateSaved = 1;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
break;
|
||||
|
||||
case APP_CMD_RESUME:
|
||||
free_saved_state(android_app);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void app_dummy() {
|
||||
|
||||
}
|
||||
|
||||
static void android_app_destroy(struct android_app* android_app) {
|
||||
LOGI("android_app_destroy!");
|
||||
free_saved_state(android_app);
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
if (android_app->inputQueue != NULL) {
|
||||
AInputQueue_detachLooper(android_app->inputQueue);
|
||||
}
|
||||
AConfiguration_delete(android_app->config);
|
||||
android_app->destroyed = 1;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
/* // Can't touch android_app object after this. */
|
||||
}
|
||||
|
||||
static void process_input(struct android_app* app, struct android_poll_source* source) {
|
||||
AInputEvent* event = NULL;
|
||||
if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
|
||||
LOGI("New input event: type=%d\n", AInputEvent_getType(event));
|
||||
if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
|
||||
return;
|
||||
}
|
||||
{
|
||||
int32_t handled = 0;
|
||||
if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
|
||||
AInputQueue_finishEvent(app->inputQueue, event, handled);
|
||||
}
|
||||
} else {
|
||||
LOGI("Failure reading next input event: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void process_cmd(struct android_app* app, struct android_poll_source* source) {
|
||||
int8_t cmd = android_app_read_cmd(app);
|
||||
android_app_pre_exec_cmd(app, cmd);
|
||||
if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
|
||||
android_app_post_exec_cmd(app, cmd);
|
||||
}
|
||||
|
||||
static void* android_app_entry(void* param) {
|
||||
struct android_app* android_app = (struct android_app*)param;
|
||||
ALooper* looper;
|
||||
|
||||
android_app->config = AConfiguration_new();
|
||||
AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
|
||||
|
||||
print_cur_config(android_app);
|
||||
|
||||
android_app->cmdPollSource.id = LOOPER_ID_MAIN;
|
||||
android_app->cmdPollSource.app = android_app;
|
||||
android_app->cmdPollSource.process = process_cmd;
|
||||
android_app->inputPollSource.id = LOOPER_ID_INPUT;
|
||||
android_app->inputPollSource.app = android_app;
|
||||
android_app->inputPollSource.process = process_input;
|
||||
|
||||
looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
|
||||
ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
|
||||
&android_app->cmdPollSource);
|
||||
android_app->looper = looper;
|
||||
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->running = 1;
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
|
||||
android_main(android_app);
|
||||
|
||||
android_app_destroy(android_app);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* // -------------------------------------------------------------------- */
|
||||
/* // Native activity interaction (called from main thread) */
|
||||
/* // -------------------------------------------------------------------- */
|
||||
|
||||
static struct android_app* android_app_create(ANativeActivity* activity,
|
||||
void* savedState, size_t savedStateSize) {
|
||||
struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
|
||||
int msgpipe[2];
|
||||
pthread_attr_t attr;
|
||||
memset(android_app, 0, sizeof(struct android_app));
|
||||
android_app->activity = activity;
|
||||
|
||||
pthread_mutex_init(&android_app->mutex, NULL);
|
||||
pthread_cond_init(&android_app->cond, NULL);
|
||||
|
||||
if (savedState != NULL) {
|
||||
android_app->savedState = malloc(savedStateSize);
|
||||
android_app->savedStateSize = savedStateSize;
|
||||
memcpy(android_app->savedState, savedState, savedStateSize);
|
||||
}
|
||||
|
||||
if (pipe(msgpipe)) {
|
||||
LOGI("could not create pipe: %s", strerror(errno));
|
||||
}
|
||||
android_app->msgread = msgpipe[0];
|
||||
android_app->msgwrite = msgpipe[1];
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
|
||||
|
||||
/* // Wait for thread to start. */
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
while (!android_app->running) {
|
||||
pthread_cond_wait(&android_app->cond, &android_app->mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
|
||||
return android_app;
|
||||
}
|
||||
|
||||
/* static */void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
|
||||
if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
|
||||
LOGI("Failure writing android_app cmd: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->pendingInputQueue = inputQueue;
|
||||
android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
|
||||
while (android_app->inputQueue != android_app->pendingInputQueue) {
|
||||
pthread_cond_wait(&android_app->cond, &android_app->mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
}
|
||||
|
||||
static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
if (android_app->pendingWindow != NULL) {
|
||||
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
|
||||
}
|
||||
android_app->pendingWindow = window;
|
||||
if (window != NULL) {
|
||||
android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
|
||||
}
|
||||
while (android_app->window != android_app->pendingWindow) {
|
||||
pthread_cond_wait(&android_app->cond, &android_app->mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
}
|
||||
|
||||
static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app_write_cmd(android_app, cmd);
|
||||
while (android_app->activityState != cmd) {
|
||||
pthread_cond_wait(&android_app->cond, &android_app->mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
}
|
||||
|
||||
static void android_app_free(struct android_app* android_app) {
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app_write_cmd(android_app, APP_CMD_DESTROY);
|
||||
while (!android_app->destroyed) {
|
||||
pthread_cond_wait(&android_app->cond, &android_app->mutex);
|
||||
}
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
|
||||
close(android_app->msgread);
|
||||
close(android_app->msgwrite);
|
||||
pthread_cond_destroy(&android_app->cond);
|
||||
pthread_mutex_destroy(&android_app->mutex);
|
||||
free(android_app);
|
||||
}
|
||||
|
||||
static void onDestroy(ANativeActivity* activity) {
|
||||
LOGI("Destroy: %p\n", (void*)activity);
|
||||
android_app_free((struct android_app*)activity->instance);
|
||||
}
|
||||
|
||||
static void onStart(ANativeActivity* activity) {
|
||||
LOGI("Start: %p\n", (void*)activity);
|
||||
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
|
||||
}
|
||||
|
||||
static void onResume(ANativeActivity* activity) {
|
||||
LOGI("Resume: %p\n", (void*)activity);
|
||||
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
|
||||
}
|
||||
|
||||
static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
|
||||
struct android_app* android_app = (struct android_app*)activity->instance;
|
||||
void* savedState = NULL;
|
||||
|
||||
LOGI("SaveInstanceState: %p\n", (void*)activity);
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
android_app->stateSaved = 0;
|
||||
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
|
||||
while (!android_app->stateSaved) {
|
||||
pthread_cond_wait(&android_app->cond, &android_app->mutex);
|
||||
}
|
||||
|
||||
if (android_app->savedState != NULL) {
|
||||
savedState = android_app->savedState;
|
||||
*outLen = android_app->savedStateSize;
|
||||
android_app->savedState = NULL;
|
||||
android_app->savedStateSize = 0;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
|
||||
return savedState;
|
||||
}
|
||||
|
||||
static void onPause(ANativeActivity* activity) {
|
||||
LOGI("Pause: %p\n", (void*)activity);
|
||||
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
|
||||
}
|
||||
|
||||
static void onStop(ANativeActivity* activity) {
|
||||
LOGI("Stop: %p\n", (void*)activity);
|
||||
android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
|
||||
}
|
||||
|
||||
static void onConfigurationChanged(ANativeActivity* activity) {
|
||||
struct android_app* android_app = (struct android_app*)activity->instance;
|
||||
LOGI("ConfigurationChanged: %p\n", (void*)activity);
|
||||
android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
|
||||
}
|
||||
|
||||
static void onLowMemory(ANativeActivity* activity) {
|
||||
struct android_app* android_app = (struct android_app*)activity->instance;
|
||||
LOGI("LowMemory: %p\n", (void*)activity);
|
||||
android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
|
||||
}
|
||||
|
||||
static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
|
||||
LOGI("WindowFocusChanged: %p -- %d\n", (void*)activity, focused);
|
||||
android_app_write_cmd((struct android_app*)activity->instance,
|
||||
focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
|
||||
}
|
||||
|
||||
static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
|
||||
LOGI("NativeWindowCreated: %p -- %p\n", (void*)activity, (void*)window);
|
||||
android_app_set_window((struct android_app*)activity->instance, window);
|
||||
}
|
||||
|
||||
static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
|
||||
LOGI("NativeWindowDestroyed: %p -- %p\n", (void*)activity, (void*)window);
|
||||
android_app_set_window((struct android_app*)activity->instance, NULL);
|
||||
}
|
||||
|
||||
static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
|
||||
LOGI("InputQueueCreated: %p -- %p\n", (void*)activity, (void*)queue);
|
||||
android_app_set_input((struct android_app*)activity->instance, queue);
|
||||
}
|
||||
|
||||
static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
|
||||
LOGI("InputQueueDestroyed: %p -- %p\n", (void*)activity, (void*)queue);
|
||||
android_app_set_input((struct android_app*)activity->instance, NULL);
|
||||
}
|
||||
|
||||
void ANativeActivity_onCreate(ANativeActivity* activity,
|
||||
void* savedState, size_t savedStateSize) {
|
||||
LOGI("Creating: %p\n", (void*)activity);
|
||||
activity->callbacks->onDestroy = onDestroy;
|
||||
activity->callbacks->onStart = onStart;
|
||||
activity->callbacks->onResume = onResume;
|
||||
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
|
||||
activity->callbacks->onPause = onPause;
|
||||
activity->callbacks->onStop = onStop;
|
||||
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
|
||||
activity->callbacks->onLowMemory = onLowMemory;
|
||||
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
|
||||
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
|
||||
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
|
||||
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
|
||||
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
|
||||
|
||||
activity->instance = android_app_create(activity, savedState, savedStateSize);
|
||||
}
|
@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _ANDROID_NATIVE_APP_GLUE_H
|
||||
#define _ANDROID_NATIVE_APP_GLUE_H
|
||||
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
|
||||
#include <android/configuration.h>
|
||||
#include <android/looper.h>
|
||||
#include <android/native_activity.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The native activity interface provided by <android/native_activity.h>
|
||||
* is based on a set of application-provided callbacks that will be called
|
||||
* by the Activity's main thread when certain events occur.
|
||||
*
|
||||
* This means that each one of this callbacks _should_ _not_ block, or they
|
||||
* risk having the system force-close the application. This programming
|
||||
* model is direct, lightweight, but constraining.
|
||||
*
|
||||
* The 'threaded_native_app' static library is used to provide a different
|
||||
* execution model where the application can implement its own main event
|
||||
* loop in a different thread instead. Here's how it works:
|
||||
*
|
||||
* 1/ The application must provide a function named "android_main()" that
|
||||
* will be called when the activity is created, in a new thread that is
|
||||
* distinct from the activity's main thread.
|
||||
*
|
||||
* 2/ android_main() receives a pointer to a valid "android_app" structure
|
||||
* that contains references to other important objects, e.g. the
|
||||
* ANativeActivity obejct instance the application is running in.
|
||||
*
|
||||
* 3/ the "android_app" object holds an ALooper instance that already
|
||||
* listens to two important things:
|
||||
*
|
||||
* - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX
|
||||
* declarations below.
|
||||
*
|
||||
* - input events coming from the AInputQueue attached to the activity.
|
||||
*
|
||||
* Each of these correspond to an ALooper identifier returned by
|
||||
* ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT,
|
||||
* respectively.
|
||||
*
|
||||
* Your application can use the same ALooper to listen to additional
|
||||
* file-descriptors. They can either be callback based, or with return
|
||||
* identifiers starting with LOOPER_ID_USER.
|
||||
*
|
||||
* 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event,
|
||||
* the returned data will point to an android_poll_source structure. You
|
||||
* can call the process() function on it, and fill in android_app->onAppCmd
|
||||
* and android_app->onInputEvent to be called for your own processing
|
||||
* of the event.
|
||||
*
|
||||
* Alternatively, you can call the low-level functions to read and process
|
||||
* the data directly... look at the process_cmd() and process_input()
|
||||
* implementations in the glue to see how to do this.
|
||||
*
|
||||
* See the sample named "native-activity" that comes with the NDK with a
|
||||
* full usage example. Also look at the JavaDoc of NativeActivity.
|
||||
*/
|
||||
|
||||
struct android_app;
|
||||
|
||||
/**
|
||||
* Data associated with an ALooper fd that will be returned as the "outData"
|
||||
* when that source has data ready.
|
||||
*/
|
||||
struct android_poll_source {
|
||||
/* // The identifier of this source. May be LOOPER_ID_MAIN or */
|
||||
/* // LOOPER_ID_INPUT. */
|
||||
int32_t id;
|
||||
|
||||
/* // The android_app this ident is associated with. */
|
||||
struct android_app* app;
|
||||
|
||||
/* // Function to call to perform the standard processing of data from */
|
||||
/* // this source. */
|
||||
void (*process)(struct android_app* app, struct android_poll_source* source);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the interface for the standard glue code of a threaded
|
||||
* application. In this model, the application's code is running
|
||||
* in its own thread separate from the main thread of the process.
|
||||
* It is not required that this thread be associated with the Java
|
||||
* VM, although it will need to be in order to make JNI calls any
|
||||
* Java objects.
|
||||
*/
|
||||
struct android_app {
|
||||
/* // The application can place a pointer to its own state object */
|
||||
/* // here if it likes. */
|
||||
void* userData;
|
||||
|
||||
/* // Fill this in with the function to process main app commands (APP_CMD_*) */
|
||||
void (*onAppCmd)(struct android_app* app, int32_t cmd);
|
||||
|
||||
/* // Fill this in with the function to process input events. At this point */
|
||||
/* // the event has already been pre-dispatched, and it will be finished upon */
|
||||
/* // return. Return 1 if you have handled the event, 0 for any default */
|
||||
/* // dispatching. */
|
||||
int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event);
|
||||
|
||||
/* // The ANativeActivity object instance that this app is running in. */
|
||||
ANativeActivity* activity;
|
||||
|
||||
/* // The current configuration the app is running in. */
|
||||
AConfiguration* config;
|
||||
|
||||
/* // This is the last instance's saved state, as provided at creation time. */
|
||||
/* // It is NULL if there was no state. You can use this as you need; the */
|
||||
/* // memory will remain around until you call android_app_exec_cmd() for */
|
||||
/* // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. */
|
||||
/* // These variables should only be changed when processing a APP_CMD_SAVE_STATE, */
|
||||
/* // at which point they will be initialized to NULL and you can malloc your */
|
||||
/* // state and place the information here. In that case the memory will be */
|
||||
/* // freed for you later. */
|
||||
void* savedState;
|
||||
size_t savedStateSize;
|
||||
|
||||
/* // The ALooper associated with the app's thread. */
|
||||
ALooper* looper;
|
||||
|
||||
/* // When non-NULL, this is the input queue from which the app will */
|
||||
/* // receive user input events. */
|
||||
AInputQueue* inputQueue;
|
||||
|
||||
/* // When non-NULL, this is the window surface that the app can draw in. */
|
||||
ANativeWindow* window;
|
||||
|
||||
/* // Current content rectangle of the window; this is the area where the */
|
||||
/* // window's content should be placed to be seen by the user. */
|
||||
ARect contentRect;
|
||||
|
||||
/* // Current state of the app's activity. May be either APP_CMD_START, */
|
||||
/* // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. */
|
||||
int activityState;
|
||||
|
||||
/* // This is non-zero when the application's NativeActivity is being */
|
||||
/* // destroyed and waiting for the app thread to complete. */
|
||||
int destroyRequested;
|
||||
|
||||
/* // ------------------------------------------------- */
|
||||
/* // Below are "private" implementation of the glue code. */
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
|
||||
int msgread;
|
||||
int msgwrite;
|
||||
|
||||
pthread_t thread;
|
||||
|
||||
struct android_poll_source cmdPollSource;
|
||||
struct android_poll_source inputPollSource;
|
||||
|
||||
int running;
|
||||
int stateSaved;
|
||||
int destroyed;
|
||||
int redrawNeeded;
|
||||
AInputQueue* pendingInputQueue;
|
||||
ANativeWindow* pendingWindow;
|
||||
ARect pendingContentRect;
|
||||
};
|
||||
|
||||
enum {
|
||||
/**
|
||||
* Looper data ID of commands coming from the app's main thread, which
|
||||
* is returned as an identifier from ALooper_pollOnce(). The data for this
|
||||
* identifier is a pointer to an android_poll_source structure.
|
||||
* These can be retrieved and processed with android_app_read_cmd()
|
||||
* and android_app_exec_cmd().
|
||||
*/
|
||||
LOOPER_ID_MAIN = 1,
|
||||
|
||||
/**
|
||||
* Looper data ID of events coming from the AInputQueue of the
|
||||
* application's window, which is returned as an identifier from
|
||||
* ALooper_pollOnce(). The data for this identifier is a pointer to an
|
||||
* android_poll_source structure. These can be read via the inputQueue
|
||||
* object of android_app.
|
||||
*/
|
||||
LOOPER_ID_INPUT = 2,
|
||||
|
||||
/**
|
||||
* Start of user-defined ALooper identifiers.
|
||||
*/
|
||||
LOOPER_ID_USER = 3
|
||||
};
|
||||
|
||||
enum {
|
||||
/**
|
||||
* Command from main thread: the AInputQueue has changed. Upon processing
|
||||
* this command, android_app->inputQueue will be updated to the new queue
|
||||
* (or NULL).
|
||||
*/
|
||||
APP_CMD_INPUT_CHANGED,
|
||||
|
||||
/**
|
||||
* Command from main thread: a new ANativeWindow is ready for use. Upon
|
||||
* receiving this command, android_app->window will contain the new window
|
||||
* surface.
|
||||
*/
|
||||
APP_CMD_INIT_WINDOW,
|
||||
|
||||
/**
|
||||
* Command from main thread: the existing ANativeWindow needs to be
|
||||
* terminated. Upon receiving this command, android_app->window still
|
||||
* contains the existing window; after calling android_app_exec_cmd
|
||||
* it will be set to NULL.
|
||||
*/
|
||||
APP_CMD_TERM_WINDOW,
|
||||
|
||||
/**
|
||||
* Command from main thread: the current ANativeWindow has been resized.
|
||||
* Please redraw with its new size.
|
||||
*/
|
||||
APP_CMD_WINDOW_RESIZED,
|
||||
|
||||
/**
|
||||
* Command from main thread: the system needs that the current ANativeWindow
|
||||
* be redrawn. You should redraw the window before handing this to
|
||||
* android_app_exec_cmd() in order to avoid transient drawing glitches.
|
||||
*/
|
||||
APP_CMD_WINDOW_REDRAW_NEEDED,
|
||||
|
||||
/**
|
||||
* Command from main thread: the content area of the window has changed,
|
||||
* such as from the soft input window being shown or hidden. You can
|
||||
* find the new content rect in android_app::contentRect.
|
||||
*/
|
||||
APP_CMD_CONTENT_RECT_CHANGED,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity window has gained
|
||||
* input focus.
|
||||
*/
|
||||
APP_CMD_GAINED_FOCUS,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity window has lost
|
||||
* input focus.
|
||||
*/
|
||||
APP_CMD_LOST_FOCUS,
|
||||
|
||||
/**
|
||||
* Command from main thread: the current device configuration has changed.
|
||||
*/
|
||||
APP_CMD_CONFIG_CHANGED,
|
||||
|
||||
/**
|
||||
* Command from main thread: the system is running low on memory.
|
||||
* Try to reduce your memory use.
|
||||
*/
|
||||
APP_CMD_LOW_MEMORY,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been started.
|
||||
*/
|
||||
APP_CMD_START,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been resumed.
|
||||
*/
|
||||
APP_CMD_RESUME,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app should generate a new saved state
|
||||
* for itself, to restore from later if needed. If you have saved state,
|
||||
* allocate it with malloc and place it in android_app.savedState with
|
||||
* the size in android_app.savedStateSize. The will be freed for you
|
||||
* later.
|
||||
*/
|
||||
APP_CMD_SAVE_STATE,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been paused.
|
||||
*/
|
||||
APP_CMD_PAUSE,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity has been stopped.
|
||||
*/
|
||||
APP_CMD_STOP,
|
||||
|
||||
/**
|
||||
* Command from main thread: the app's activity is being destroyed,
|
||||
* and waiting for the app thread to clean up and exit before proceeding.
|
||||
*/
|
||||
APP_CMD_DESTROY
|
||||
};
|
||||
|
||||
/**
|
||||
* Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
|
||||
* app command message.
|
||||
*/
|
||||
int8_t android_app_read_cmd(struct android_app* android_app);
|
||||
|
||||
/**
|
||||
* Call with the command returned by android_app_read_cmd() to do the
|
||||
* initial pre-processing of the given command. You can perform your own
|
||||
* actions for the command after calling this function.
|
||||
*/
|
||||
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);
|
||||
|
||||
/**
|
||||
* Call with the command returned by android_app_read_cmd() to do the
|
||||
* final post-processing of the given command. You must have done your own
|
||||
* actions for the command before calling this function.
|
||||
*/
|
||||
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);
|
||||
|
||||
/**
|
||||
* Dummy function you can call to ensure glue code isn't stripped.
|
||||
*/
|
||||
void app_dummy();
|
||||
|
||||
/**
|
||||
* This is the function that application code must implement, representing
|
||||
* the main entry to the app.
|
||||
*/
|
||||
extern void android_main(struct android_app* app);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ANDROID_NATIVE_APP_GLUE_H */
|
9
freeglut/freeglut/src/android/opengles_stubs.c
Normal file
9
freeglut/freeglut/src/android/opengles_stubs.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include <GL/freeglut.h>
|
||||
#include "../Common/freeglut_internal.h"
|
||||
|
||||
void fgDeactivateMenu( SFG_Window *window ) {
|
||||
fprintf(stderr, "fgDeactivateMenu: STUB\n");
|
||||
}
|
||||
void fgDisplayMenu( void ) {
|
||||
fprintf(stderr, "fgDisplayMenu: STUB\n");
|
||||
}
|
36
freeglut/freeglut/src/egl/freeglut_display_egl.c
Normal file
36
freeglut/freeglut/src/egl/freeglut_display_egl.c
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* freeglut_display_android.c
|
||||
*
|
||||
* Display message posting, context buffer swapping.
|
||||
*
|
||||
* 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 "../Common/freeglut_internal.h"
|
||||
|
||||
#include <android/log.h>
|
||||
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "FreeGLUT", __VA_ARGS__))
|
||||
|
||||
void fgPlatformGlutSwapBuffers( SFG_PlatformDisplay *pDisplayPtr, SFG_Window* CurrentWindow )
|
||||
{
|
||||
/* LOGI("Swap!"); */
|
||||
eglSwapBuffers( pDisplayPtr->eglDisplay, CurrentWindow->Window.pContext.eglSurface );
|
||||
}
|
80
freeglut/freeglut/src/egl/freeglut_init_egl.c
Normal file
80
freeglut/freeglut/src/egl/freeglut_init_egl.c
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* freeglut_init_android.c
|
||||
*
|
||||
* Various freeglut initialization functions.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define FREEGLUT_BUILDING_LIB
|
||||
#include <GL/freeglut.h>
|
||||
#include "../Common/freeglut_internal.h"
|
||||
|
||||
#include <android/native_app_glue/android_native_app_glue.h>
|
||||
|
||||
/*
|
||||
* A call to this function should initialize all the display stuff...
|
||||
*/
|
||||
void fgPlatformInitialize( const char* displayName )
|
||||
{
|
||||
fprintf(stderr, "fgPlatformInitialize\n");
|
||||
fgState.Initialised = GL_TRUE;
|
||||
|
||||
/* CreateDisplay */
|
||||
/* Using EGL_DEFAULT_DISPLAY, or a specific native display */
|
||||
EGLNativeDisplayType nativeDisplay = EGL_DEFAULT_DISPLAY;
|
||||
fgDisplay.pDisplay.eglDisplay = eglGetDisplay(nativeDisplay);
|
||||
|
||||
FREEGLUT_INTERNAL_ERROR_EXIT(fgDisplay.pDisplay.eglDisplay != EGL_NO_DISPLAY,
|
||||
"No display available", "fgPlatformInitialize");
|
||||
if (!eglInitialize(fgDisplay.pDisplay.eglDisplay, NULL, NULL))
|
||||
fgError("eglInitialize: error %x\n", eglGetError());
|
||||
|
||||
/* CreateContext */
|
||||
fghCreateContext();
|
||||
|
||||
// fgDisplay.ScreenWidth = ...;
|
||||
// fgDisplay.ScreenHeight = ...;
|
||||
// fgDisplay.ScreenWidthMM = ...;
|
||||
// fgDisplay.ScreenHeightMM = ...;
|
||||
}
|
||||
|
||||
void fgPlatformCloseDisplay ( void )
|
||||
{
|
||||
eglMakeCurrent(fgDisplay.pDisplay.eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
if (fgDisplay.pDisplay.eglContext != EGL_NO_CONTEXT) {
|
||||
eglDestroyContext(fgDisplay.pDisplay.eglDisplay, fgDisplay.pDisplay.eglContext);
|
||||
fgDisplay.pDisplay.eglContext = EGL_NO_CONTEXT;
|
||||
}
|
||||
|
||||
if (fgDisplay.pDisplay.eglDisplay != EGL_NO_DISPLAY) {
|
||||
eglTerminate(fgDisplay.pDisplay.eglDisplay);
|
||||
fgDisplay.pDisplay.eglDisplay = EGL_NO_DISPLAY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a menu context
|
||||
*/
|
||||
void fgPlatformDestroyContext ( SFG_PlatformDisplay pDisplay, SFG_WindowContextType MContext )
|
||||
{
|
||||
if (MContext != EGL_NO_CONTEXT)
|
||||
eglDestroyContext(pDisplay.eglDisplay, MContext);
|
||||
}
|
68
freeglut/freeglut/src/egl/freeglut_internal_egl.h
Normal file
68
freeglut/freeglut/src/egl/freeglut_internal_egl.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* freeglut_internal_android.h
|
||||
*
|
||||
* The freeglut library private include file.
|
||||
*
|
||||
* 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 FREEGLUT_INTERNAL_EGL_H
|
||||
#define FREEGLUT_INTERNAL_EGL_H
|
||||
|
||||
#include <EGL/egl.h>
|
||||
|
||||
/* -- GLOBAL TYPE DEFINITIONS ---------------------------------------------- */
|
||||
/* The structure used by display initialization in freeglut_init.c */
|
||||
typedef struct tagSFG_PlatformDisplay SFG_PlatformDisplay;
|
||||
struct tagSFG_Window;
|
||||
struct tagSFG_PlatformDisplay
|
||||
{
|
||||
/* Used to initialize and deinitialize EGL */
|
||||
EGLDisplay eglDisplay;
|
||||
EGLContext eglContext;
|
||||
EGLConfig eglContextConfig;
|
||||
EGLint eglContextFormat;
|
||||
struct tagSFG_Window* single_window;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Make "freeglut" window handle and context types so that we don't need so
|
||||
* much conditionally-compiled code later in the library.
|
||||
*/
|
||||
typedef EGLNativeWindowType SFG_WindowHandleType ;
|
||||
typedef EGLContext SFG_WindowContextType ;
|
||||
typedef struct tagSFG_PlatformContext SFG_PlatformContext;
|
||||
/* SFG_PlatformContext is used for SFG_Window.Window */
|
||||
struct tagSFG_PlatformContext
|
||||
{
|
||||
EGLSurface eglSurface;
|
||||
};
|
||||
|
||||
|
||||
/* Window's state description. This structure should be kept portable. */
|
||||
typedef struct tagSFG_PlatformWindowState SFG_PlatformWindowState;
|
||||
struct tagSFG_PlatformWindowState
|
||||
{
|
||||
int OldWidth; /* Window width from before a resize */
|
||||
int OldHeight; /* " height " " " " */
|
||||
};
|
||||
|
||||
#endif
|
34
freeglut/freeglut/src/egl/freeglut_structure_egl.c
Normal file
34
freeglut/freeglut/src/egl/freeglut_structure_egl.c
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* freeglut_structure_egl.c
|
||||
*
|
||||
* Windows and menus need tree structure
|
||||
*
|
||||
* 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 "../Common/freeglut_internal.h"
|
||||
|
||||
extern SFG_Structure fgStructure;
|
||||
|
||||
void fgPlatformCreateWindow ( SFG_Window *window )
|
||||
{
|
||||
window->Window.pContext.eglSurface = EGL_NO_SURFACE;
|
||||
}
|
129
freeglut/freeglut/src/egl/freeglut_window_egl.c
Normal file
129
freeglut/freeglut/src/egl/freeglut_window_egl.c
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* freeglut_display_android.c
|
||||
*
|
||||
* Window management methods for EGL
|
||||
*
|
||||
* 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 "../Common/freeglut_internal.h"
|
||||
|
||||
/**
|
||||
* Initialize an EGL context for the current display.
|
||||
*/
|
||||
void fghCreateContext( ) {
|
||||
/*
|
||||
* Here specify the attributes of the desired configuration.
|
||||
* Below, we select an EGLConfig with at least 8 bits per color
|
||||
* component compatible with on-screen windows
|
||||
*/
|
||||
/* Ensure OpenGLES 2.0 context */
|
||||
printf("DisplayMode: %d (DEPTH %d)\n", fgState.DisplayMode, (fgState.DisplayMode & GLUT_DEPTH));
|
||||
const EGLint attribs[] = {
|
||||
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
||||
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL_BLUE_SIZE, 1,
|
||||
EGL_GREEN_SIZE, 1,
|
||||
EGL_RED_SIZE, 1,
|
||||
EGL_ALPHA_SIZE, (fgState.DisplayMode & GLUT_ALPHA) ? 1 : 0,
|
||||
EGL_DEPTH_SIZE, (fgState.DisplayMode & GLUT_DEPTH) ? 1 : 0,
|
||||
EGL_STENCIL_SIZE, (fgState.DisplayMode & GLUT_STENCIL) ? 1 : 0,
|
||||
EGL_SAMPLE_BUFFERS, (fgState.DisplayMode & GLUT_MULTISAMPLE) ? 1 : 0,
|
||||
EGL_SAMPLES, (fgState.DisplayMode & GLUT_MULTISAMPLE) ? fgState.SampleNumber : 0,
|
||||
EGL_NONE
|
||||
};
|
||||
|
||||
EGLint format;
|
||||
EGLint numConfigs;
|
||||
EGLConfig config;
|
||||
EGLContext context;
|
||||
|
||||
EGLDisplay eglDisplay = fgDisplay.pDisplay.eglDisplay;
|
||||
|
||||
/* TODO : apply DisplayMode */
|
||||
/* (GLUT_DEPTH already applied in attribs[] above) */
|
||||
|
||||
/* Here, the application chooses the configuration it desires. In this
|
||||
* sample, we have a very simplified selection process, where we pick
|
||||
* the first EGLConfig that matches our criteria */
|
||||
eglChooseConfig(eglDisplay, attribs, &config, 1, &numConfigs);
|
||||
|
||||
/* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is
|
||||
* guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
|
||||
* As soon as we picked a EGLConfig, we can safely reconfigure the
|
||||
* ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */
|
||||
eglGetConfigAttrib(eglDisplay, config, EGL_NATIVE_VISUAL_ID, &format);
|
||||
|
||||
/* Default, but doesn't hurt */
|
||||
eglBindAPI(EGL_OPENGL_ES_API);
|
||||
|
||||
/* Ensure OpenGLES 2.0 context */
|
||||
static const EGLint ctx_attribs[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
EGL_NONE
|
||||
};
|
||||
context = eglCreateContext(eglDisplay, config, EGL_NO_CONTEXT, ctx_attribs);
|
||||
if (context == EGL_NO_CONTEXT) {
|
||||
fgWarning("Cannot initialize EGL context, err=%x\n", eglGetError());
|
||||
fghContextCreationError();
|
||||
}
|
||||
EGLint ver = -1;
|
||||
eglQueryContext(fgDisplay.pDisplay.eglDisplay, context, EGL_CONTEXT_CLIENT_VERSION, &ver);
|
||||
if (ver != 2)
|
||||
fgError("Wrong GLES major version: %d\n", ver);
|
||||
|
||||
fgDisplay.pDisplay.eglContext = context;
|
||||
fgDisplay.pDisplay.eglContextConfig = config;
|
||||
fgDisplay.pDisplay.eglContextFormat = format;
|
||||
}
|
||||
|
||||
/*
|
||||
* Really opens a window when handle is available
|
||||
*/
|
||||
EGLSurface fghEGLPlatformOpenWindow( EGLNativeWindowType handle )
|
||||
{
|
||||
EGLDisplay display = fgDisplay.pDisplay.eglDisplay;
|
||||
EGLContext context = fgDisplay.pDisplay.eglContext;
|
||||
EGLConfig config = fgDisplay.pDisplay.eglContextConfig;
|
||||
|
||||
EGLSurface surface = eglCreateWindowSurface(display, config, handle, NULL);
|
||||
if (surface == EGL_NO_SURFACE)
|
||||
fgError("Cannot create EGL window surface, err=%x\n", eglGetError());
|
||||
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
|
||||
fgError("eglMakeCurrent: err=%x\n", eglGetError());
|
||||
|
||||
//EGLint w, h;
|
||||
//eglQuerySurface(display, surface, EGL_WIDTH, &w);
|
||||
//eglQuerySurface(display, surface, EGL_HEIGHT, &h);
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
/*
|
||||
* Closes a window, destroying the frame and OpenGL context
|
||||
*/
|
||||
void fgPlatformCloseWindow( SFG_Window* window )
|
||||
{
|
||||
if (window->Window.pContext.eglSurface != EGL_NO_SURFACE) {
|
||||
eglDestroySurface(fgDisplay.pDisplay.eglDisplay, window->Window.pContext.eglSurface);
|
||||
window->Window.pContext.eglSurface = EGL_NO_SURFACE;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user