lazarus-ccr/bindings/android-ndk/android_native_app_glue.pas

799 lines
30 KiB
ObjectPascal

(*
* 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.
*
*)
unit android_native_app_glue;
interface
uses ctypes,baseunix,unixtype,
configuration,looper,log,input,rect,native_window,native_activity;
(**
* 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.
*)
(**
* Data associated with an ALooper fd that will be returned as the "outData"
* when that source has data ready.
*)
type
Pandroid_poll_source = ^android_poll_source;
Pandroid_app = ^Tandroid_app;
android_poll_source = packed record
// The identifier of this source. May be LOOPER_ID_MAIN or
// LOOPER_ID_INPUT.
id : cint32;
// The android_app this ident is associated with.
app : Pandroid_app;
// Function to call to perform the standard processing of data from
// this source.
process : procedure(app: Pandroid_app; source: Pandroid_poll_source); cdecl;
end;
(**
* 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.
*)
Tandroid_app = packed record
// The application can place a pointer to its own state object
// here if it likes.
userData : Pointer;
// Fill this in with the function to process main app commands (APP_CMD_*)
onAppCmd : procedure(app: Pandroid_app; cmd: cint32); cdecl;
// 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 if you have handled the event, 0 for any default
// dispatching.
onInputEvent : function(app: Pandroid_app; event: PAInputEvent): cint32; cdecl;
// The ANativeActivity object instance that this app is running in.
activity : PANativeActivity;
// The current configuration the app is running in.
config : PAConfiguration;
// 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.
savedState : Pointer;
savedStateSize : csize_t;
// The ALooper associated with the app's thread.
looper : PALooper;
// When non-NULL, this is the input queue from which the app will
// receive user input events.
inputQueue : PAInputQueue;
// When non-NULL, this is the window surface that the app can draw in.
window : PANativeWindow;
// Current content rectangle of the window; this is the area where the
// window's content should be placed to be seen by the user.
contentRect : ARect;
// 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.
activityState : cint;
// This is non-zero when the application's NativeActivity is being
// destroyed and waiting for the app thread to complete.
destroyRequested : cint;
// -------------------------------------------------
// Below are "private" implementation of the glue code.
mutex : pthread_mutex_t;
cond : pthread_cond_t;
msgread : cint;
msgwrite : cint;
thread : pthread_t;
cmdPollSource : android_poll_source;
inputPollSource : android_poll_source;
running : cint;
stateSaved : cint;
destroyed : cint;
redrawNeeded : cint;
pendingInputQueue : PAInputQueue;
pendingWindow : PANativeWindow;
pendingContentRect : ARect;
end;
const
(**
* 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;
const
(**
* 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 = 0;
(**
* 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 = 1;
(**
* 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 = 2;
(**
* Command from main thread: the current ANativeWindow has been resized.
* Please redraw with its new size.
*)
APP_CMD_WINDOW_RESIZED = 3;
(**
* 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 = 4;
(**
* 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 = 5;
(**
* Command from main thread: the app's activity window has gained
* input focus.
*)
APP_CMD_GAINED_FOCUS = 6;
(**
* Command from main thread: the app's activity window has lost
* input focus.
*)
APP_CMD_LOST_FOCUS = 7;
(**
* Command from main thread: the current device configuration has changed.
*)
APP_CMD_CONFIG_CHANGED = 8;
(**
* Command from main thread: the system is running low on memory.
* Try to reduce your memory use.
*)
APP_CMD_LOW_MEMORY = 9;
(**
* Command from main thread: the app's activity has been started.
*)
APP_CMD_START = 10;
(**
* Command from main thread: the app's activity has been resumed.
*)
APP_CMD_RESUME = 11;
(**
* 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 = 12;
(**
* Command from main thread: the app's activity has been paused.
*)
APP_CMD_PAUSE = 13;
(**
* Command from main thread: the app's activity has been stopped.
*)
APP_CMD_STOP = 14;
(**
* 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 = 15;
(**
* Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next
* app command message.
*)
function android_app_read_cmd(android_app: Pandroid_app): cint8; cdecl;
(**
* 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.
*)
procedure android_app_pre_exec_cmd(android_app: Pandroid_app; cmd: cint8); cdecl;
(**
* 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.
*)
procedure android_app_post_exec_cmd(android_app: Pandroid_app; cmd: cint8); cdecl;
(**
* Dummy function you can call to ensure glue code isn't stripped.
*)
procedure app_dummy;
(**
* This is the function that application code must implement, representing
* the main entry to the app.
*)
procedure android_main(app: Pandroid_app); cdecl; external;
procedure ANativeActivity_onCreate(activity: PANativeActivity; savedState: Pointer; savedStateSize: csize_t); cdecl;
implementation
uses cmem;
function strerror(i: longint): pchar; cdecl;
begin
result := 'Undefined!';
end;
type
ppthread_t = ^pthread_t;
ppthread_attr_t = ^pthread_attr_t;
ppthread_mutex_t = ^pthread_mutex_t;
ppthread_cond_t = ^pthread_cond_t;
ppthread_mutexattr_t = ^pthread_mutexattr_t;
ppthread_condattr_t = ^pthread_condattr_t;
__start_routine_t = pointer;
const
PTHREAD_CREATE_DETACHED = 1;
function pthread_create(__thread:ppthread_t; __attr:ppthread_attr_t;__start_routine: __start_routine_t;__arg:pointer):longint;cdecl;external 'libc.so';
function pthread_attr_init(__attr:ppthread_attr_t):longint;cdecl;external 'libc.so';
function pthread_attr_setdetachstate(__attr:ppthread_attr_t; __detachstate:longint):longint;cdecl;external 'libc.so';
function pthread_mutex_init(__mutex:ppthread_mutex_t; __mutex_attr:ppthread_mutexattr_t):longint;cdecl;external 'libc.so';
function pthread_mutex_destroy(__mutex:ppthread_mutex_t):longint;cdecl;external 'libc.so';
function pthread_mutex_lock(__mutex: ppthread_mutex_t):longint;cdecl;external 'libc.so';
function pthread_mutex_unlock(__mutex: ppthread_mutex_t):longint;cdecl;external 'libc.so';
function pthread_cond_init(__cond:ppthread_cond_t; __cond_attr:ppthread_condattr_t):longint;cdecl;external 'libc.so';
function pthread_cond_destroy(__cond:ppthread_cond_t):longint;cdecl;external 'libc.so';
function pthread_cond_signal(__cond:ppthread_cond_t):longint;cdecl;external 'libc.so';
function pthread_cond_broadcast(__cond:ppthread_cond_t):longint;cdecl;external 'libc.so';
function pthread_cond_wait(__cond:ppthread_cond_t; __mutex:ppthread_mutex_t):longint;cdecl;external 'libc.so';
procedure free_saved_state(android_app: Pandroid_app);
begin
pthread_mutex_lock(@android_app^.mutex);
if android_app^.savedState <> Nil then
begin
free(android_app^.savedState);
android_app^.savedState := nil;
android_app^.savedStateSize := 0;
end;
pthread_mutex_unlock(@android_app^.mutex);
end;
function android_app_read_cmd(android_app: Pandroid_app): cint8; cdecl;
var cmd: cint8;
begin
result := -1;
if fpread(android_app^.msgread, @cmd, sizeof(cmd)) = sizeof(cmd) then
begin
case cmd of
APP_CMD_SAVE_STATE:
free_saved_state(android_app);
end;
result := cmd;
end
else
LOGI(ANDROID_LOG_FATAL,'Crap','No data on command pipe!');
end;
procedure print_cur_config(android_app: Pandroid_app);
var lang, country: array[0..1] of char;
begin
AConfiguration_getLanguage(android_app^.config, @lang[0]);
AConfiguration_getCountry(android_app^.config, @country[0]);
LOGI(ANDROID_LOG_FATAL,'Crap','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));
end;
procedure android_app_pre_exec_cmd(android_app: Pandroid_app; cmd: cint8); cdecl;
begin
case cmd of
APP_CMD_INPUT_CHANGED:
begin
LOGI(ANDROID_LOG_FATAL,'Crap','APP_CMD_INPUT_CHANGED');
pthread_mutex_lock(@android_app^.mutex);
if android_app^.inputQueue <> nil then
AInputQueue_detachLooper(android_app^.inputQueue);
android_app^.inputQueue := android_app^.pendingInputQueue;
if android_app^.inputQueue <> nil then
begin
LOGI(ANDROID_LOG_FATAL,'Crap','Attaching input queue to looper');
AInputQueue_attachLooper(android_app^.inputQueue,
android_app^.looper, LOOPER_ID_INPUT, nil,
@android_app^.inputPollSource);
end;
pthread_cond_broadcast(@android_app^.cond);
pthread_mutex_unlock(@android_app^.mutex);
end;
APP_CMD_INIT_WINDOW:
begin
LOGI(ANDROID_LOG_FATAL,'Crap','APP_CMD_INIT_WINDOW');
pthread_mutex_lock(@android_app^.mutex);
android_app^.window := android_app^.pendingWindow;
pthread_cond_broadcast(@android_app^.cond);
pthread_mutex_unlock(@android_app^.mutex);
end;
APP_CMD_TERM_WINDOW:
begin
LOGI(ANDROID_LOG_FATAL,'Crap','APP_CMD_TERM_WINDOW');
pthread_mutex_lock(@android_app^.mutex);
android_app^.window := nil;
pthread_cond_broadcast(@android_app^.cond);
pthread_mutex_unlock(@android_app^.mutex);
end;
APP_CMD_RESUME,
APP_CMD_START,
APP_CMD_PAUSE,
APP_CMD_STOP:
begin
LOGI(ANDROID_LOG_FATAL,'Crap','activityState:=%d', cmd);
pthread_mutex_lock(@android_app^.mutex);
android_app^.activityState := cmd;
pthread_cond_broadcast(@android_app^.cond);
pthread_mutex_unlock(@android_app^.mutex);
end;
APP_CMD_CONFIG_CHANGED:
begin
LOGI(ANDROID_LOG_FATAL,'Crap','APP_CMD_CONFIG_CHANGED');
AConfiguration_fromAssetManager(android_app^.config,
android_app^.activity^.assetManager);
print_cur_config(android_app);
end;
APP_CMD_DESTROY:
begin
LOGI(ANDROID_LOG_FATAL,'Crap','APP_CMD_DESTROY');
android_app^.destroyRequested := 1;
end;
end;
end;
procedure android_app_post_exec_cmd(android_app: Pandroid_app; cmd: cint8); cdecl;
begin
case cmd of
APP_CMD_TERM_WINDOW:
begin
LOGI(ANDROID_LOG_FATAL,'Crap','APP_CMD_TERM_WINDOW');
pthread_mutex_lock(@android_app^.mutex);
android_app^.window := nil;
pthread_cond_broadcast(@android_app^.cond);
pthread_mutex_unlock(@android_app^.mutex);
end;
APP_CMD_SAVE_STATE:
begin
LOGI(ANDROID_LOG_FATAL,'Crap','APP_CMD_SAVE_STATE');
pthread_mutex_lock(@android_app^.mutex);
android_app^.stateSaved := 1;
pthread_cond_broadcast(@android_app^.cond);
pthread_mutex_unlock(@android_app^.mutex);
end;
APP_CMD_RESUME:
free_saved_state(android_app);
end;
end;
procedure app_dummy;
begin
end;
procedure android_app_destroy(android_app: Pandroid_app);
begin
LOGI(ANDROID_LOG_FATAL,'Crap','android_app_destroy!');
free_saved_state(android_app);
pthread_mutex_lock(@android_app^.mutex);
if (android_app^.inputQueue <> nil) then
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.
end;
procedure process_input(app: Pandroid_app; source: Pandroid_poll_source); cdecl;
var event: PAInputEvent;
handled: cint32;
begin
event := nil;
if (AInputQueue_getEvent(app^.inputQueue, @event) >= 0) then
begin
LOGI(ANDROID_LOG_FATAL,'Crap','New input event: type:=%d',AInputEvent_getType(event));
if AInputQueue_preDispatchEvent(app^.inputQueue, event) <> 0 then exit;
handled := 0;
if (app^.onInputEvent <> nil) then handled := app^.onInputEvent(app, event);
AInputQueue_finishEvent(app^.inputQueue, event, handled);
end
else
LOGI(ANDROID_LOG_FATAL,'Crap','Failure reading next input event: %s', strerror(errno));
end;
procedure process_cmd(app: Pandroid_app; source: Pandroid_poll_source); cdecl;
var cmd: cint8;
begin
cmd := android_app_read_cmd(app);
android_app_pre_exec_cmd(app, cmd);
if (app^.onAppCmd <> nil) then app^.onAppCmd(app, cmd);
android_app_post_exec_cmd(app, cmd);
end;
function android_app_entry(param: pointer): Pointer; cdecl;
var android_app: Pandroid_app;
looper: PALooper;
begin
android_app := Pandroid_app(param);
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, nil,
@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);
result := nil;
end;
// --------------------------------------------------------------------
// Native activity interaction (called from main thread)
// --------------------------------------------------------------------
function android_app_create(activity: PANativeActivity; savedState: Pointer; savedStateSize: csize_t): Pandroid_app;
var android_app: Pandroid_app;
msgpipe: array[0..1] of cint;
attr: pthread_attr_t;
begin
android_app := Pandroid_app(malloc(sizeof(Tandroid_app)));
fillchar(android_app^, sizeof(tandroid_app), 0);
android_app^.activity := activity;
pthread_mutex_init(@android_app^.mutex, nil);
pthread_cond_init(@android_app^.cond, nil);
if (savedState <> nil) then
begin
android_app^.savedState := malloc(savedStateSize);
android_app^.savedStateSize := savedStateSize;
move(pbyte(savedState)^, pbyte(android_app^.savedState)^, savedStateSize);
end;
if FpPipe(msgpipe) <> 0 then
LOGI(ANDROID_LOG_FATAL,'Crap','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 = 0 do
pthread_cond_wait(@android_app^.cond, @android_app^.mutex);
pthread_mutex_unlock(@android_app^.mutex);
result := android_app;
end;
procedure android_app_write_cmd(android_app: Pandroid_app; cmd: cint8);
begin
if fpwrite(android_app^.msgwrite, cmd, sizeof(cmd)) <> sizeof(cmd) then
LOGI(ANDROID_LOG_FATAL,'Crap','Failure writing android_app cmd: %s', strerror(errno));
end;
procedure android_app_set_input(android_app: Pandroid_app; inputQueue: PAInputQueue);
begin
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) do
pthread_cond_wait(@android_app^.cond, @android_app^.mutex);
pthread_mutex_unlock(@android_app^.mutex);
end;
procedure android_app_set_window(android_app: Pandroid_app; window: PANativeWindow);
begin
pthread_mutex_lock(@android_app^.mutex);
if (android_app^.pendingWindow <> nil) then
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
android_app^.pendingWindow := window;
if (window <> nil) then
android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
while (android_app^.window <> android_app^.pendingWindow) do
pthread_cond_wait(@android_app^.cond, @android_app^.mutex);
pthread_mutex_unlock(@android_app^.mutex);
end;
procedure android_app_set_activity_state(android_app: Pandroid_app; cmd: cint8);
begin
logw(' Setting activity state!');
pthread_mutex_lock(@android_app^.mutex);
android_app_write_cmd(android_app, cmd);
while (android_app^.activityState <> cmd) do
pthread_cond_wait(@android_app^.cond, @android_app^.mutex);
pthread_mutex_unlock(@android_app^.mutex);
end;
procedure android_app_free(android_app: Pandroid_app);
begin
pthread_mutex_lock(@android_app^.mutex);
android_app_write_cmd(android_app, APP_CMD_DESTROY);
while android_app^.destroyed = 0 do
pthread_cond_wait(@android_app^.cond, @android_app^.mutex);
pthread_mutex_unlock(@android_app^.mutex);
fpclose(android_app^.msgread);
fpclose(android_app^.msgwrite);
pthread_cond_destroy(@android_app^.cond);
pthread_mutex_destroy(@android_app^.mutex);
free(android_app);
end;
procedure onDestroy(activity: PANativeActivity); cdecl;
begin
LOGI(ANDROID_LOG_FATAL,'Crap','Destroy: %p', activity);
android_app_free(Pandroid_app(activity^.instance));
end;
procedure onStart(activity: PANativeActivity); cdecl;
begin
LOGI(ANDROID_LOG_FATAL,'Crap','Start: %p', activity);
android_app_set_activity_state(Pandroid_app(activity^.instance), APP_CMD_START);
end;
procedure onResume(activity: PANativeActivity); cdecl;
begin
LOGI(ANDROID_LOG_FATAL,'Crap','Resume: %p', activity);
android_app_set_activity_state(Pandroid_app(activity^.instance), APP_CMD_RESUME);
end;
function onSaveInstanceState(activity: PANativeActivity; outLen: pcsize_t): Pointer; cdecl;
var android_app: Pandroid_app;
savedState: pointer;
begin
android_app := activity^.instance;
savedState := nil;
LOGI(ANDROID_LOG_FATAL,'Crap','SaveInstanceState: %p', 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 = 0 do
pthread_cond_wait(@android_app^.cond, @android_app^.mutex);
if android_app^.savedState <> nil then
begin
savedState := android_app^.savedState;
outLen^ := android_app^.savedStateSize;
android_app^.savedState := nil;
android_app^.savedStateSize := 0;
end;
pthread_mutex_unlock(@android_app^.mutex);
result := savedState;
end;
procedure onPause(activity: PANativeActivity); cdecl;
begin
LOGI(ANDROID_LOG_FATAL,'Crap','Pause: %p', activity);
android_app_set_activity_state(Pandroid_app(activity^.instance), APP_CMD_PAUSE);
end;
procedure onStop(activity: PANativeActivity); cdecl;
begin
LOGI(ANDROID_LOG_FATAL,'Crap','Stop: %p', activity);
android_app_set_activity_state(Pandroid_app(activity^.instance), APP_CMD_STOP);
end;
procedure onConfigurationChanged(activity: PANativeActivity); cdecl;
var android_app: Pandroid_app;
begin
android_app := activity^.instance;
LOGI(ANDROID_LOG_FATAL,'Crap','ConfigurationChanged: %p', activity);
android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
end;
procedure onLowMemory(activity: PANativeActivity); cdecl;
var android_app: Pandroid_app;
begin
android_app := activity^.instance;
LOGI(ANDROID_LOG_FATAL,'Crap','LowMemory: %p', activity);
android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
end;
procedure onWindowFocusChanged(activity: PANativeActivity; focused: cint); cdecl;
begin
LOGI(ANDROID_LOG_FATAL,'Crap','WindowFocusChanged: %p -- %d', activity, focused);
if focused <> 0 then
android_app_write_cmd(activity^.instance, APP_CMD_GAINED_FOCUS)
else
android_app_write_cmd(activity^.instance, APP_CMD_LOST_FOCUS);
end;
procedure onNativeWindowCreated(activity: PANativeActivity; window: PANativeWindow); cdecl;
begin
LOGI(ANDROID_LOG_FATAL,'Crap','NativeWindowCreated: %p -- %p', activity, window);
android_app_set_window(activity^.instance, window);
end;
procedure onNativeWindowDestroyed(activity: PANativeActivity; window: PANativeWindow); cdecl;
begin
LOGI(ANDROID_LOG_FATAL,'Crap','NativeWindowDestroyed: %p -- %p', activity, window);
android_app_set_window(activity^.instance, nil);
end;
procedure onInputQueueCreated(activity: PANativeActivity; queue: PAInputQueue); cdecl;
begin
LOGI(ANDROID_LOG_FATAL,'Crap','InputQueueCreated: %p -- %p', activity, queue);
android_app_set_input(activity^.instance, queue);
end;
procedure onInputQueueDestroyed(activity: PANativeActivity; queue: PAInputQueue); cdecl;
begin
LOGI(ANDROID_LOG_FATAL,'Crap','InputQueueDestroyed: %p -- %p', activity, queue);
android_app_set_input(activity^.instance, nil);
end;
procedure ANativeActivity_onCreate(activity: PANativeActivity; savedState: Pointer; savedStateSize: csize_t); cdecl;
begin
LOGI(ANDROID_LOG_FATAL,'Crap','Creating: %p', 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);
end;
end.