mirror of
https://github.com/rust-mobile/android-activity.git
synced 2026-07-15 02:58:53 +00:00
native-activity: complete (first-pass) port of C code to Rust
At this point the C code has been fully ported to Rust but it's not yet well integrated with the pre-existing Rust code and the port is not yet particularly idiomatic Rust code.
This commit is contained in:
@@ -1,13 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
fn build_glue_for_native_activity() {
|
||||
cc::Build::new()
|
||||
.include("native-activity-csrc")
|
||||
.include("native-activity-csrc/native-activity/native_app_glue")
|
||||
.file("native-activity-csrc/native-activity/native_app_glue/android_native_app_glue.c")
|
||||
.compile("libnative_app_glue.a");
|
||||
}
|
||||
|
||||
fn build_glue_for_game_activity() {
|
||||
cc::Build::new()
|
||||
.cpp(true)
|
||||
@@ -38,6 +30,4 @@ fn build_glue_for_game_activity() {
|
||||
fn main() {
|
||||
#[cfg(feature = "game-activity")]
|
||||
build_glue_for_game_activity();
|
||||
#[cfg(feature = "native-activity")]
|
||||
build_glue_for_native_activity();
|
||||
}
|
||||
|
||||
@@ -37,33 +37,6 @@ while read ARCH && read TARGET ; do
|
||||
-Igame-activity-csrc \
|
||||
--sysroot="$SYSROOT" --target=$TARGET
|
||||
|
||||
bindgen native-activity-ffi.h -o src/native_activity/ffi_$ARCH.rs \
|
||||
--blocklist-item 'JNI\w+' \
|
||||
--blocklist-item 'C?_?JNIEnv' \
|
||||
--blocklist-item '_?JavaVM' \
|
||||
--blocklist-item '_?j\w+' \
|
||||
--blocklist-item 'size_t' \
|
||||
--blocklist-item 'pthread_\w*' \
|
||||
--blocklist-function 'pthread_\w' \
|
||||
--blocklist-item 'ARect' \
|
||||
--blocklist-item 'ALooper\w*' \
|
||||
--blocklist-function 'ALooper\w*' \
|
||||
--blocklist-item 'AAsset\w*' \
|
||||
--blocklist-item 'AAssetManager\w*' \
|
||||
--blocklist-function 'AAssetManager\w*' \
|
||||
--blocklist-item 'ANativeWindow\w*' \
|
||||
--blocklist-function 'ANativeWindow\w*' \
|
||||
--blocklist-item 'AConfiguration\w*' \
|
||||
--blocklist-function 'AConfiguration\w*' \
|
||||
--blocklist-function 'android_main' \
|
||||
--blocklist-item 'AInputQueue\w*' \
|
||||
--blocklist-function 'AInputQueue\w*' \
|
||||
--blocklist-item 'GameActivity_onCreate' \
|
||||
--blocklist-function 'GameActivity_onCreate_C' \
|
||||
--newtype-enum '\w+_(result|status)_t' \
|
||||
-- \
|
||||
-Inative-activity-csrc \
|
||||
--sysroot="$SYSROOT" --target=$TARGET
|
||||
done << EOF
|
||||
arm
|
||||
arm-linux-androideabi
|
||||
|
||||
-202
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* 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 <stdlib.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__))
|
||||
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
|
||||
|
||||
/* For debug builds, always enable the debug traces in this library */
|
||||
#ifndef NDEBUG
|
||||
# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__))
|
||||
#else
|
||||
# define LOGV(...) ((void)0)
|
||||
#endif
|
||||
|
||||
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 {
|
||||
LOGE("No data on command pipe!");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
LOGV("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_attach_input_queue_looper(struct android_app* android_app) {
|
||||
if (android_app->inputQueue != NULL) {
|
||||
LOGV("Attaching input queue to looper");
|
||||
AInputQueue_attachLooper(android_app->inputQueue,
|
||||
android_app->looper, LOOPER_ID_INPUT, NULL,
|
||||
&android_app->inputPollSource);
|
||||
}
|
||||
}
|
||||
|
||||
void android_app_detach_input_queue_looper(struct android_app* android_app) {
|
||||
if (android_app->inputQueue != NULL) {
|
||||
LOGV("Detaching input queue from looper");
|
||||
AInputQueue_detachLooper(android_app->inputQueue);
|
||||
}
|
||||
}
|
||||
|
||||
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
|
||||
switch (cmd) {
|
||||
case APP_CMD_INPUT_CHANGED:
|
||||
LOGV("APP_CMD_INPUT_CHANGED\n");
|
||||
pthread_mutex_lock(&android_app->mutex);
|
||||
if (android_app->inputQueue != NULL) {
|
||||
android_app_detach_input_queue_looper(android_app);
|
||||
}
|
||||
android_app->inputQueue = android_app->pendingInputQueue;
|
||||
if (android_app->inputQueue != NULL) {
|
||||
android_app_attach_input_queue_looper(android_app);
|
||||
}
|
||||
pthread_cond_broadcast(&android_app->cond);
|
||||
pthread_mutex_unlock(&android_app->mutex);
|
||||
break;
|
||||
|
||||
case APP_CMD_INIT_WINDOW:
|
||||
LOGV("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:
|
||||
LOGV("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:
|
||||
LOGV("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:
|
||||
LOGV("APP_CMD_CONFIG_CHANGED\n");
|
||||
AConfiguration_fromAssetManager(android_app->config,
|
||||
android_app->activity->assetManager);
|
||||
print_cur_config(android_app);
|
||||
break;
|
||||
|
||||
case APP_CMD_DESTROY:
|
||||
LOGV("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:
|
||||
LOGV("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:
|
||||
LOGV("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 android_app_destroy(struct android_app* android_app) {
|
||||
LOGV("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.
|
||||
}
|
||||
|
||||
void process_cmd(struct android_app* app, __attribute__((unused)) 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);
|
||||
}
|
||||
-345
@@ -1,345 +0,0 @@
|
||||
/*
|
||||
* 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 'android_native_app_glue' 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);
|
||||
|
||||
void android_app_attach_input_queue_looper(struct android_app* android_app);
|
||||
void android_app_detach_input_queue_looper(struct android_app* android_app);
|
||||
|
||||
void print_cur_config(struct android_app* android_app);
|
||||
void process_cmd(struct android_app* app, __attribute__((unused)) struct android_poll_source* source);
|
||||
void android_app_destroy(struct android_app* android_app);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ANDROID_NATIVE_APP_GLUE_H */
|
||||
@@ -1,32 +0,0 @@
|
||||
//! The bindings are pre-generated and the right one for the platform is selected at compile time.
|
||||
|
||||
// Bindgen lints
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(improper_ctypes)]
|
||||
#![allow(clippy::all)]
|
||||
// Temporarily allow UB nullptr dereference in bindgen layout tests until fixed upstream:
|
||||
// https://github.com/rust-lang/rust-bindgen/pull/2055
|
||||
// https://github.com/rust-lang/rust-bindgen/pull/2064
|
||||
#![allow(deref_nullptr)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use jni_sys::*;
|
||||
use ndk_sys::{ARect, AConfiguration, AInputQueue, ALooper, AAssetManager, ANativeWindow};
|
||||
use libc::{size_t, pthread_t, pthread_mutex_t, pthread_cond_t};
|
||||
|
||||
#[cfg(all(
|
||||
any(target_os = "android", feature = "test"),
|
||||
any(target_arch = "arm", target_arch = "armv7")
|
||||
))]
|
||||
include!("ffi_arm.rs");
|
||||
|
||||
#[cfg(all(any(target_os = "android", feature = "test"), target_arch = "aarch64"))]
|
||||
include!("ffi_aarch64.rs");
|
||||
|
||||
#[cfg(all(any(target_os = "android", feature = "test"), target_arch = "x86"))]
|
||||
include!("ffi_i686.rs");
|
||||
|
||||
#[cfg(all(any(target_os = "android", feature = "test"), target_arch = "x86_64"))]
|
||||
include!("ffi_x86_64.rs");
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -25,8 +25,6 @@ use crate::{
|
||||
util, AndroidApp, ConfigurationRef, InputStatus, MainEvent, PollEvent, Rect, WindowManagerFlags,
|
||||
};
|
||||
|
||||
mod ffi;
|
||||
|
||||
pub mod input {
|
||||
pub use ndk::event::{
|
||||
Axis, ButtonState, EdgeFlags, KeyAction, KeyEvent, KeyEventFlags, Keycode, MetaState,
|
||||
@@ -128,6 +126,73 @@ impl AndroidAppWaker {
|
||||
}
|
||||
}
|
||||
|
||||
/// These are the original C structs / constants from android_native_app_glue.c naively
|
||||
/// ported to Rust via bindgen.
|
||||
///
|
||||
/// TODO: start integrating all this state directly into `AndroidApp`/`NativeAppGlue`
|
||||
mod ffi {
|
||||
pub const LOOPER_ID_MAIN: ::std::os::raw::c_uint = 1;
|
||||
pub const LOOPER_ID_INPUT: ::std::os::raw::c_uint = 2;
|
||||
//pub const LOOPER_ID_USER: ::std::os::raw::c_uint = 3;
|
||||
|
||||
pub const APP_CMD_INPUT_CHANGED: ::std::os::raw::c_uint = 0;
|
||||
pub const APP_CMD_INIT_WINDOW: ::std::os::raw::c_uint = 1;
|
||||
pub const APP_CMD_TERM_WINDOW: ::std::os::raw::c_uint = 2;
|
||||
pub const APP_CMD_WINDOW_RESIZED: ::std::os::raw::c_uint = 3;
|
||||
pub const APP_CMD_WINDOW_REDRAW_NEEDED: ::std::os::raw::c_uint = 4;
|
||||
pub const APP_CMD_CONTENT_RECT_CHANGED: ::std::os::raw::c_uint = 5;
|
||||
pub const APP_CMD_GAINED_FOCUS: ::std::os::raw::c_uint = 6;
|
||||
pub const APP_CMD_LOST_FOCUS: ::std::os::raw::c_uint = 7;
|
||||
pub const APP_CMD_CONFIG_CHANGED: ::std::os::raw::c_uint = 8;
|
||||
pub const APP_CMD_LOW_MEMORY: ::std::os::raw::c_uint = 9;
|
||||
pub const APP_CMD_START: ::std::os::raw::c_uint = 10;
|
||||
pub const APP_CMD_RESUME: ::std::os::raw::c_uint = 11;
|
||||
pub const APP_CMD_SAVE_STATE: ::std::os::raw::c_uint = 12;
|
||||
pub const APP_CMD_PAUSE: ::std::os::raw::c_uint = 13;
|
||||
pub const APP_CMD_STOP: ::std::os::raw::c_uint = 14;
|
||||
pub const APP_CMD_DESTROY: ::std::os::raw::c_uint = 15;
|
||||
|
||||
pub struct android_poll_source {
|
||||
pub id: i32,
|
||||
pub app: *mut android_app,
|
||||
pub process: ::std::option::Option<
|
||||
unsafe extern "C" fn(app: *mut android_app, source: *mut android_poll_source),
|
||||
>,
|
||||
}
|
||||
|
||||
pub struct android_app {
|
||||
pub userData: *mut ::std::os::raw::c_void,
|
||||
pub onAppCmd: ::std::option::Option<unsafe extern "C" fn(app: *mut android_app, cmd: i32)>,
|
||||
pub onInputEvent: ::std::option::Option<
|
||||
unsafe extern "C" fn(app: *mut android_app, event: *mut ndk_sys::AInputEvent) -> i32,
|
||||
>,
|
||||
pub activity: *mut ndk_sys::ANativeActivity,
|
||||
pub config: *mut ndk_sys::AConfiguration,
|
||||
pub savedState: *mut ::std::os::raw::c_void,
|
||||
pub savedStateSize: libc::size_t,
|
||||
pub looper: *mut ndk_sys::ALooper,
|
||||
pub inputQueue: *mut ndk_sys::AInputQueue,
|
||||
pub window: *mut ndk_sys::ANativeWindow,
|
||||
pub contentRect: ndk_sys::ARect,
|
||||
pub activityState: ::std::os::raw::c_int,
|
||||
pub destroyRequested: ::std::os::raw::c_int,
|
||||
pub mutex: libc::pthread_mutex_t,
|
||||
pub cond: libc::pthread_cond_t,
|
||||
pub msgread: ::std::os::raw::c_int,
|
||||
pub msgwrite: ::std::os::raw::c_int,
|
||||
pub thread: libc::pthread_t,
|
||||
pub cmdPollSource: android_poll_source,
|
||||
pub inputPollSource: android_poll_source,
|
||||
pub running: ::std::os::raw::c_int,
|
||||
pub stateSaved: ::std::os::raw::c_int,
|
||||
pub destroyed: ::std::os::raw::c_int,
|
||||
pub redrawNeeded: ::std::os::raw::c_int,
|
||||
pub pendingInputQueue: *mut ndk_sys::AInputQueue,
|
||||
pub pendingWindow: *mut ndk_sys::ANativeWindow,
|
||||
pub pendingContentRect: ndk_sys::ARect,
|
||||
}
|
||||
}
|
||||
|
||||
impl AndroidApp {
|
||||
pub(crate) unsafe fn from_ptr(ptr: NonNull<ffi::android_app>) -> AndroidApp {
|
||||
// Note: we don't use from_ptr since we don't own the android_app.config
|
||||
@@ -205,20 +270,20 @@ impl AndroidAppInner {
|
||||
);
|
||||
info!("pollAll id = {id}");
|
||||
match id {
|
||||
ffi::ALOOPER_POLL_WAKE => {
|
||||
ndk_sys::ALOOPER_POLL_WAKE => {
|
||||
trace!("ALooper_pollAll returned POLL_WAKE");
|
||||
callback(PollEvent::Wake);
|
||||
}
|
||||
ffi::ALOOPER_POLL_CALLBACK => {
|
||||
ndk_sys::ALOOPER_POLL_CALLBACK => {
|
||||
// ALooper_pollAll is documented to handle all callback sources internally so it should
|
||||
// never return a _CALLBACK source id...
|
||||
error!("Spurious ALOOPER_POLL_CALLBACK from ALopper_pollAll() (ignored)");
|
||||
}
|
||||
ffi::ALOOPER_POLL_TIMEOUT => {
|
||||
ndk_sys::ALOOPER_POLL_TIMEOUT => {
|
||||
trace!("ALooper_pollAll returned POLL_TIMEOUT");
|
||||
callback(PollEvent::Timeout);
|
||||
}
|
||||
ffi::ALOOPER_POLL_ERROR => {
|
||||
ndk_sys::ALOOPER_POLL_ERROR => {
|
||||
// If we have an IO error with our pipe to the main Java thread that's surely
|
||||
// not something we can recover from
|
||||
panic!("ALooper_pollAll returned POLL_ERROR");
|
||||
@@ -229,81 +294,81 @@ impl AndroidAppInner {
|
||||
trace!("ALooper_pollAll returned ID_MAIN");
|
||||
let source: *mut ffi::android_poll_source = source.cast();
|
||||
if source != ptr::null_mut() {
|
||||
let cmd_i = ffi::android_app_read_cmd(native_app.as_ptr());
|
||||
if let Some(cmd_i) = android_app_read_cmd(native_app.as_ptr()) {
|
||||
let cmd = match cmd_i as libc::c_uint {
|
||||
// We don't forward info about the AInputQueue to apps since it's
|
||||
// an implementation details that's also not compatible with
|
||||
// GameActivity
|
||||
ffi::APP_CMD_INPUT_CHANGED => None,
|
||||
|
||||
let cmd = match cmd_i as u32 {
|
||||
// We don't forward info about the AInputQueue to apps since it's
|
||||
// an implementation details that's also not compatible with
|
||||
// GameActivity
|
||||
ffi::APP_CMD_INPUT_CHANGED => None,
|
||||
|
||||
ffi::APP_CMD_INIT_WINDOW => Some(MainEvent::InitWindow {}),
|
||||
ffi::APP_CMD_TERM_WINDOW => Some(MainEvent::TerminateWindow {}),
|
||||
ffi::APP_CMD_WINDOW_RESIZED => {
|
||||
Some(MainEvent::WindowResized {})
|
||||
}
|
||||
ffi::APP_CMD_WINDOW_REDRAW_NEEDED => {
|
||||
Some(MainEvent::RedrawNeeded {})
|
||||
}
|
||||
ffi::APP_CMD_CONTENT_RECT_CHANGED => {
|
||||
Some(MainEvent::ContentRectChanged {})
|
||||
}
|
||||
ffi::APP_CMD_GAINED_FOCUS => Some(MainEvent::GainedFocus),
|
||||
ffi::APP_CMD_LOST_FOCUS => Some(MainEvent::LostFocus),
|
||||
ffi::APP_CMD_CONFIG_CHANGED => {
|
||||
Some(MainEvent::ConfigChanged {})
|
||||
}
|
||||
ffi::APP_CMD_LOW_MEMORY => Some(MainEvent::LowMemory),
|
||||
ffi::APP_CMD_START => Some(MainEvent::Start),
|
||||
ffi::APP_CMD_RESUME => Some(MainEvent::Resume {
|
||||
loader: StateLoader { app: &self },
|
||||
}),
|
||||
ffi::APP_CMD_SAVE_STATE => Some(MainEvent::SaveState {
|
||||
saver: StateSaver { app: &self },
|
||||
}),
|
||||
ffi::APP_CMD_PAUSE => Some(MainEvent::Pause),
|
||||
ffi::APP_CMD_STOP => Some(MainEvent::Stop),
|
||||
ffi::APP_CMD_DESTROY => Some(MainEvent::Destroy),
|
||||
|
||||
//ffi::NativeAppGlueAppCmd_APP_CMD_WINDOW_INSETS_CHANGED => MainEvent::InsetsChanged {},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
trace!("Calling android_app_pre_exec_cmd({cmd_i})");
|
||||
ffi::android_app_pre_exec_cmd(native_app.as_ptr(), cmd_i);
|
||||
|
||||
if let Some(cmd) = cmd {
|
||||
trace!("Read ID_MAIN command {cmd_i} = {cmd:?}");
|
||||
match cmd {
|
||||
MainEvent::ConfigChanged { .. } => {
|
||||
self.config.replace(Configuration::clone_from_ptr(
|
||||
NonNull::new_unchecked(
|
||||
(*native_app.as_ptr()).config,
|
||||
),
|
||||
));
|
||||
ffi::APP_CMD_INIT_WINDOW => Some(MainEvent::InitWindow {}),
|
||||
ffi::APP_CMD_TERM_WINDOW => Some(MainEvent::TerminateWindow {}),
|
||||
ffi::APP_CMD_WINDOW_RESIZED => {
|
||||
Some(MainEvent::WindowResized {})
|
||||
}
|
||||
MainEvent::InitWindow { .. } => {
|
||||
let win_ptr = (*native_app.as_ptr()).window;
|
||||
// It's important that we use ::clone_from_ptr() here
|
||||
// because NativeWindow has a Drop implementation that
|
||||
// will unconditionally _release() the native window
|
||||
*self.native_window.write().unwrap() =
|
||||
Some(NativeWindow::clone_from_ptr(
|
||||
NonNull::new(win_ptr).unwrap(),
|
||||
ffi::APP_CMD_WINDOW_REDRAW_NEEDED => {
|
||||
Some(MainEvent::RedrawNeeded {})
|
||||
}
|
||||
ffi::APP_CMD_CONTENT_RECT_CHANGED => {
|
||||
Some(MainEvent::ContentRectChanged {})
|
||||
}
|
||||
ffi::APP_CMD_GAINED_FOCUS => Some(MainEvent::GainedFocus),
|
||||
ffi::APP_CMD_LOST_FOCUS => Some(MainEvent::LostFocus),
|
||||
ffi::APP_CMD_CONFIG_CHANGED => {
|
||||
Some(MainEvent::ConfigChanged {})
|
||||
}
|
||||
ffi::APP_CMD_LOW_MEMORY => Some(MainEvent::LowMemory),
|
||||
ffi::APP_CMD_START => Some(MainEvent::Start),
|
||||
ffi::APP_CMD_RESUME => Some(MainEvent::Resume {
|
||||
loader: StateLoader { app: &self },
|
||||
}),
|
||||
ffi::APP_CMD_SAVE_STATE => Some(MainEvent::SaveState {
|
||||
saver: StateSaver { app: &self },
|
||||
}),
|
||||
ffi::APP_CMD_PAUSE => Some(MainEvent::Pause),
|
||||
ffi::APP_CMD_STOP => Some(MainEvent::Stop),
|
||||
ffi::APP_CMD_DESTROY => Some(MainEvent::Destroy),
|
||||
|
||||
//ffi::NativeAppGlueAppCmd_APP_CMD_WINDOW_INSETS_CHANGED => MainEvent::InsetsChanged {},
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
trace!("Calling android_app_pre_exec_cmd({cmd_i})");
|
||||
android_app_pre_exec_cmd(native_app.as_ptr(), cmd_i);
|
||||
|
||||
if let Some(cmd) = cmd {
|
||||
trace!("Read ID_MAIN command {cmd_i} = {cmd:?}");
|
||||
match cmd {
|
||||
MainEvent::ConfigChanged { .. } => {
|
||||
self.config.replace(Configuration::clone_from_ptr(
|
||||
NonNull::new_unchecked(
|
||||
(*native_app.as_ptr()).config,
|
||||
),
|
||||
));
|
||||
}
|
||||
MainEvent::InitWindow { .. } => {
|
||||
let win_ptr = (*native_app.as_ptr()).window;
|
||||
// It's important that we use ::clone_from_ptr() here
|
||||
// because NativeWindow has a Drop implementation that
|
||||
// will unconditionally _release() the native window
|
||||
*self.native_window.write().unwrap() =
|
||||
Some(NativeWindow::clone_from_ptr(
|
||||
NonNull::new(win_ptr).unwrap(),
|
||||
));
|
||||
}
|
||||
MainEvent::TerminateWindow { .. } => {
|
||||
*self.native_window.write().unwrap() = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
MainEvent::TerminateWindow { .. } => {
|
||||
*self.native_window.write().unwrap() = None;
|
||||
}
|
||||
_ => {}
|
||||
|
||||
trace!("Invoking callback for ID_MAIN command = {:?}", cmd);
|
||||
callback(PollEvent::Main(cmd));
|
||||
}
|
||||
|
||||
trace!("Invoking callback for ID_MAIN command = {:?}", cmd);
|
||||
callback(PollEvent::Main(cmd));
|
||||
trace!("Calling android_app_post_exec_cmd({cmd_i})");
|
||||
android_app_post_exec_cmd(native_app.as_ptr(), cmd_i);
|
||||
}
|
||||
|
||||
trace!("Calling android_app_post_exec_cmd({cmd_i})");
|
||||
ffi::android_app_post_exec_cmd(native_app.as_ptr(), cmd_i);
|
||||
} else {
|
||||
panic!("ALooper_pollAll returned ID_MAIN event with NULL android_poll_source!");
|
||||
}
|
||||
@@ -315,7 +380,7 @@ impl AndroidAppInner {
|
||||
// input events then we only send one `InputAvailable` per iteration of input
|
||||
// handling. We re-attach the looper when the application calls
|
||||
// `AndroidApp::input_events()`
|
||||
ffi::android_app_detach_input_queue_looper(native_app.as_ptr());
|
||||
android_app_detach_input_queue_looper(native_app.as_ptr());
|
||||
callback(PollEvent::Main(MainEvent::InputAvailable))
|
||||
}
|
||||
_ => {
|
||||
@@ -373,7 +438,7 @@ impl AndroidAppInner {
|
||||
let na = self.native_activity();
|
||||
let na_mut = na as *mut ndk_sys::ANativeActivity;
|
||||
unsafe {
|
||||
ffi::ANativeActivity_setWindowFlags(
|
||||
ndk_sys::ANativeActivity_setWindowFlags(
|
||||
na_mut.cast(),
|
||||
add_flags.bits(),
|
||||
remove_flags.bits(),
|
||||
@@ -386,11 +451,11 @@ impl AndroidAppInner {
|
||||
let na = self.native_activity();
|
||||
unsafe {
|
||||
let flags = if show_implicit {
|
||||
ffi::ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT
|
||||
ndk_sys::ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT
|
||||
} else {
|
||||
0
|
||||
};
|
||||
ffi::ANativeActivity_showSoftInput(na as *mut _, flags);
|
||||
ndk_sys::ANativeActivity_showSoftInput(na as *mut _, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,11 +464,11 @@ impl AndroidAppInner {
|
||||
let na = self.native_activity();
|
||||
unsafe {
|
||||
let flags = if hide_implicit_only {
|
||||
ffi::ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY
|
||||
ndk_sys::ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY
|
||||
} else {
|
||||
0
|
||||
};
|
||||
ffi::ANativeActivity_hideSoftInput(na as *mut _, flags);
|
||||
ndk_sys::ANativeActivity_hideSoftInput(na as *mut _, flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,7 +492,7 @@ impl AndroidAppInner {
|
||||
|
||||
// Reattach the input queue to the looper so future input will again deliver an
|
||||
// `InputAvailable` event.
|
||||
ffi::android_app_attach_input_queue_looper(app_ptr);
|
||||
android_app_attach_input_queue_looper(app_ptr);
|
||||
|
||||
let queue = NonNull::new_unchecked((*app_ptr).inputQueue);
|
||||
InputQueue::from_ptr(queue)
|
||||
@@ -484,6 +549,175 @@ impl AndroidAppInner {
|
||||
// Rust-side event loop
|
||||
////////////////////////////
|
||||
|
||||
unsafe fn free_saved_state(android_app: *mut ffi::android_app) {
|
||||
libc::pthread_mutex_lock(&mut (*android_app).mutex as *mut _);
|
||||
if (*android_app).savedState != ptr::null_mut() {
|
||||
libc::free((*android_app).savedState);
|
||||
(*android_app).savedState = ptr::null_mut();
|
||||
(*android_app).savedStateSize = 0;
|
||||
}
|
||||
libc::pthread_mutex_unlock(&mut (*android_app).mutex as *mut _);
|
||||
}
|
||||
|
||||
unsafe fn android_app_read_cmd(android_app: *mut ffi::android_app) -> Option<i8> {
|
||||
let mut cmd: i8 = 0;
|
||||
loop {
|
||||
match libc::read((*android_app).msgread, &mut cmd as *mut _ as *mut _, 1) {
|
||||
1 => {
|
||||
match cmd as libc::c_uint {
|
||||
ffi::APP_CMD_SAVE_STATE => {
|
||||
free_saved_state(android_app);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
return Some(cmd);
|
||||
}
|
||||
-1 => {
|
||||
let err = std::io::Error::last_os_error();
|
||||
if err.kind() != std::io::ErrorKind::Interrupted {
|
||||
log::error!("Failure reading android_app cmd: {}", err);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
count => {
|
||||
log::error!("Spurious read of {count} bytes while reading android_app cmd");
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn print_cur_config(android_app: *mut ffi::android_app) {
|
||||
let mut lang = [0u8; 2];
|
||||
ndk_sys::AConfiguration_getLanguage((*android_app).config, lang[..].as_mut_ptr());
|
||||
let lang = if lang[0] == 0 {
|
||||
" ".to_owned()
|
||||
} else {
|
||||
std::str::from_utf8(&lang[..]).unwrap().to_owned()
|
||||
};
|
||||
let mut country = " ".to_owned();
|
||||
ndk_sys::AConfiguration_getCountry((*android_app).config, country.as_mut_ptr() as *mut _);
|
||||
|
||||
ndk_sys::AConfiguration_getCountry((*android_app).config, country[..].as_mut_ptr());
|
||||
|
||||
log::debug!("Config: mcc={} mnc={} lang={} cnt={} orien={} touch={} dens={} keys={} nav={} keysHid={} navHid={} sdk={} size={} long={} modetype={} modenight={}",
|
||||
ndk_sys::AConfiguration_getMcc((*android_app).config),
|
||||
ndk_sys::AConfiguration_getMnc((*android_app).config),
|
||||
lang,
|
||||
country,
|
||||
ndk_sys::AConfiguration_getOrientation((*android_app).config),
|
||||
ndk_sys::AConfiguration_getTouchscreen((*android_app).config),
|
||||
ndk_sys::AConfiguration_getDensity((*android_app).config),
|
||||
ndk_sys::AConfiguration_getKeyboard((*android_app).config),
|
||||
ndk_sys::AConfiguration_getNavigation((*android_app).config),
|
||||
ndk_sys::AConfiguration_getKeysHidden((*android_app).config),
|
||||
ndk_sys::AConfiguration_getNavHidden((*android_app).config),
|
||||
ndk_sys::AConfiguration_getSdkVersion((*android_app).config),
|
||||
ndk_sys::AConfiguration_getScreenSize((*android_app).config),
|
||||
ndk_sys::AConfiguration_getScreenLong((*android_app).config),
|
||||
ndk_sys::AConfiguration_getUiModeType((*android_app).config),
|
||||
ndk_sys::AConfiguration_getUiModeNight((*android_app).config));
|
||||
}
|
||||
|
||||
unsafe fn android_app_attach_input_queue_looper(android_app: *mut ffi::android_app) {
|
||||
if (*android_app).inputQueue != ptr::null_mut() {
|
||||
log::debug!("Attaching input queue to looper");
|
||||
ndk_sys::AInputQueue_attachLooper((*android_app).inputQueue,
|
||||
(*android_app).looper, ffi::LOOPER_ID_INPUT as libc::c_int, None,
|
||||
&mut (*android_app).inputPollSource as *mut _ as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn android_app_detach_input_queue_looper(android_app: *mut ffi::android_app) {
|
||||
if (*android_app).inputQueue != ptr::null_mut() {
|
||||
log::debug!("Detaching input queue from looper");
|
||||
ndk_sys::AInputQueue_detachLooper((*android_app).inputQueue);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn android_app_pre_exec_cmd(android_app: *mut ffi::android_app, cmd: i8) {
|
||||
match cmd as libc::c_uint {
|
||||
ffi::APP_CMD_INPUT_CHANGED => {
|
||||
log::debug!("APP_CMD_INPUT_CHANGED\n");
|
||||
libc::pthread_mutex_lock(&mut (*android_app).mutex as *mut _);
|
||||
if (*android_app).inputQueue != ptr::null_mut() {
|
||||
android_app_detach_input_queue_looper(android_app);
|
||||
}
|
||||
(*android_app).inputQueue = (*android_app).pendingInputQueue;
|
||||
if (*android_app).inputQueue != ptr::null_mut() {
|
||||
android_app_attach_input_queue_looper(android_app);
|
||||
}
|
||||
libc::pthread_cond_broadcast(&mut (*android_app).cond as *mut _);
|
||||
libc::pthread_mutex_unlock(&mut (*android_app).mutex as *mut _);
|
||||
}
|
||||
ffi::APP_CMD_INIT_WINDOW => {
|
||||
log::debug!("APP_CMD_INIT_WINDOW");
|
||||
libc::pthread_mutex_lock(&mut (*android_app).mutex as *mut _);
|
||||
(*android_app).window = (*android_app).pendingWindow;
|
||||
libc::pthread_cond_broadcast(&mut (*android_app).cond as *mut _);
|
||||
libc::pthread_mutex_unlock(&mut (*android_app).mutex as *mut _);
|
||||
}
|
||||
ffi::APP_CMD_TERM_WINDOW => {
|
||||
log::debug!("APP_CMD_TERM_WINDOW");
|
||||
libc::pthread_cond_broadcast(&mut (*android_app).cond as *mut _);
|
||||
}
|
||||
ffi::APP_CMD_RESUME | ffi::APP_CMD_START | ffi::APP_CMD_PAUSE | ffi::APP_CMD_STOP => {
|
||||
log::debug!("activityState={}", cmd);
|
||||
libc::pthread_mutex_lock(&mut (*android_app).mutex as *mut _);
|
||||
(*android_app).activityState = cmd as i32;
|
||||
libc::pthread_cond_broadcast(&mut (*android_app).cond as *mut _);
|
||||
libc::pthread_mutex_unlock(&mut (*android_app).mutex as *mut _);
|
||||
}
|
||||
ffi::APP_CMD_CONFIG_CHANGED => {
|
||||
log::debug!("APP_CMD_CONFIG_CHANGED");
|
||||
ndk_sys::AConfiguration_fromAssetManager((*android_app).config,
|
||||
(*(*android_app).activity).assetManager);
|
||||
print_cur_config(android_app);
|
||||
}
|
||||
ffi::APP_CMD_DESTROY => {
|
||||
log::debug!("APP_CMD_DESTROY");
|
||||
(*android_app).destroyRequested = 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn android_app_post_exec_cmd(android_app: *mut ffi::android_app, cmd: i8) {
|
||||
match cmd as libc::c_uint {
|
||||
ffi::APP_CMD_TERM_WINDOW => {
|
||||
log::debug!("APP_CMD_TERM_WINDOW");
|
||||
libc::pthread_mutex_lock(&mut (*android_app).mutex as *mut _);
|
||||
(*android_app).window = ptr::null_mut();
|
||||
libc::pthread_cond_broadcast(&mut (*android_app).cond as *mut _);
|
||||
libc::pthread_mutex_unlock(&mut (*android_app).mutex as *mut _);
|
||||
}
|
||||
ffi::APP_CMD_SAVE_STATE => {
|
||||
log::debug!("APP_CMD_SAVE_STATE");
|
||||
libc::pthread_mutex_lock(&mut (*android_app).mutex as *mut _);
|
||||
(*android_app).stateSaved = 1;
|
||||
libc::pthread_cond_broadcast(&mut (*android_app).cond as *mut _);
|
||||
libc::pthread_mutex_unlock(&mut (*android_app).mutex as *mut _);
|
||||
}
|
||||
ffi::APP_CMD_RESUME => {
|
||||
free_saved_state(android_app);
|
||||
}
|
||||
_ => { }
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn android_app_destroy(android_app: *mut ffi::android_app) {
|
||||
log::debug!("android_app_destroy!");
|
||||
free_saved_state(android_app);
|
||||
libc::pthread_mutex_lock(&mut (*android_app).mutex as *mut _);
|
||||
if (*android_app).inputQueue != ptr::null_mut() {
|
||||
ndk_sys::AInputQueue_detachLooper((*android_app).inputQueue);
|
||||
}
|
||||
ndk_sys::AConfiguration_delete((*android_app).config);
|
||||
(*android_app).destroyed = 1;
|
||||
libc::pthread_cond_broadcast(&mut (*android_app).cond as *mut _);
|
||||
libc::pthread_mutex_unlock(&mut (*android_app).mutex as *mut _);
|
||||
// Can't touch android_app object after this.
|
||||
}
|
||||
|
||||
extern "C" fn android_app_main(arg: *mut libc::c_void) -> *mut libc::c_void {
|
||||
unsafe {
|
||||
@@ -492,11 +726,11 @@ extern "C" fn android_app_main(arg: *mut libc::c_void) -> *mut libc::c_void {
|
||||
(*android_app).config = ndk_sys::AConfiguration_new();
|
||||
ndk_sys::AConfiguration_fromAssetManager((*android_app).config, (*(*android_app).activity).assetManager);
|
||||
|
||||
ffi::print_cur_config(android_app);
|
||||
print_cur_config(android_app);
|
||||
|
||||
(*android_app).cmdPollSource.id = ffi::LOOPER_ID_MAIN as i32;
|
||||
(*android_app).cmdPollSource.app = android_app;
|
||||
(*android_app).cmdPollSource.process = Some(ffi::process_cmd);
|
||||
(*android_app).cmdPollSource.process = None;
|
||||
|
||||
let looper = ndk_sys::ALooper_prepare(ndk_sys::ALOOPER_PREPARE_ALLOW_NON_CALLBACKS as libc::c_int);
|
||||
ndk_sys::ALooper_addFd(looper, (*android_app).msgread, ffi::LOOPER_ID_MAIN as libc::c_int, ndk_sys::ALOOPER_EVENT_INPUT as libc::c_int, None,
|
||||
@@ -510,7 +744,7 @@ extern "C" fn android_app_main(arg: *mut libc::c_void) -> *mut libc::c_void {
|
||||
|
||||
_rust_glue_entry(android_app);
|
||||
|
||||
ffi::android_app_destroy(android_app);
|
||||
android_app_destroy(android_app);
|
||||
|
||||
ptr::null_mut()
|
||||
}
|
||||
@@ -522,7 +756,7 @@ extern "C" fn android_app_main(arg: *mut libc::c_void) -> *mut libc::c_void {
|
||||
///////////////////////////////
|
||||
|
||||
|
||||
unsafe fn android_app_create(activity: *mut ffi::ANativeActivity,
|
||||
unsafe fn android_app_create(activity: *mut ndk_sys::ANativeActivity,
|
||||
saved_state_in: *const libc::c_void, saved_state_size: libc::size_t) -> *mut ffi::android_app
|
||||
{
|
||||
let mut msgpipe: [libc::c_int; 2] = [ -1, -1 ];
|
||||
@@ -660,7 +894,7 @@ unsafe fn android_app_set_activity_state(android_app: *mut ffi::android_app, cmd
|
||||
libc::pthread_mutex_unlock(&mut (*android_app).mutex as *mut _);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_destroy(activity: *mut ffi::ANativeActivity) {
|
||||
unsafe extern "C" fn on_destroy(activity: *mut ndk_sys::ANativeActivity) {
|
||||
log::debug!("Destroy: {:p}\n", activity);
|
||||
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
@@ -668,20 +902,20 @@ unsafe extern "C" fn on_destroy(activity: *mut ffi::ANativeActivity) {
|
||||
android_app_drop(android_app);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_start(activity: *mut ffi::ANativeActivity) {
|
||||
unsafe extern "C" fn on_start(activity: *mut ndk_sys::ANativeActivity) {
|
||||
log::debug!("Start: {:p}\n", activity);
|
||||
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
android_app_set_activity_state(android_app, ffi::APP_CMD_START as i8);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_resume(activity: *mut ffi::ANativeActivity) {
|
||||
unsafe extern "C" fn on_resume(activity: *mut ndk_sys::ANativeActivity) {
|
||||
log::debug!("Resume: {:p}\n", activity);
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
android_app_set_activity_state(android_app, ffi::APP_CMD_RESUME as i8);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_save_instance_state(activity: *mut ffi::ANativeActivity, out_len: *mut libc::size_t) -> *mut libc::c_void {
|
||||
unsafe extern "C" fn on_save_instance_state(activity: *mut ndk_sys::ANativeActivity, out_len: *mut ndk_sys::size_t) -> *mut libc::c_void {
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
let mut saved_state: *mut libc::c_void = ptr::null_mut();
|
||||
|
||||
@@ -695,7 +929,7 @@ unsafe extern "C" fn on_save_instance_state(activity: *mut ffi::ANativeActivity,
|
||||
|
||||
if (*android_app).savedState != ptr::null_mut() {
|
||||
saved_state = (*android_app).savedState;
|
||||
*out_len = (*android_app).savedStateSize;
|
||||
*out_len = (*android_app).savedStateSize as _;
|
||||
(*android_app).savedState = ptr::null_mut();
|
||||
(*android_app).savedStateSize = 0;
|
||||
}
|
||||
@@ -705,56 +939,56 @@ unsafe extern "C" fn on_save_instance_state(activity: *mut ffi::ANativeActivity,
|
||||
return saved_state;
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_pause(activity: *mut ffi::ANativeActivity) {
|
||||
unsafe extern "C" fn on_pause(activity: *mut ndk_sys::ANativeActivity) {
|
||||
log::debug!("Pause: {:p}\n", activity);
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
android_app_set_activity_state(android_app, ffi::APP_CMD_PAUSE as i8);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_stop(activity: *mut ffi::ANativeActivity) {
|
||||
unsafe extern "C" fn on_stop(activity: *mut ndk_sys::ANativeActivity) {
|
||||
log::debug!("Stop: {:p}\n", activity);
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
android_app_set_activity_state(android_app, ffi::APP_CMD_STOP as i8);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_configuration_changed(activity: *mut ffi::ANativeActivity) {
|
||||
unsafe extern "C" fn on_configuration_changed(activity: *mut ndk_sys::ANativeActivity) {
|
||||
log::debug!("ConfigurationChanged: {:p}\n", activity);
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
android_app_write_cmd(android_app, ffi::APP_CMD_CONFIG_CHANGED as i8);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_low_memory(activity: *mut ffi::ANativeActivity) {
|
||||
unsafe extern "C" fn on_low_memory(activity: *mut ndk_sys::ANativeActivity) {
|
||||
log::debug!("LowMemory: {:p}\n", activity);
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
android_app_write_cmd(android_app, ffi::APP_CMD_LOW_MEMORY as i8);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_window_focus_changed(activity: *mut ffi::ANativeActivity, focused: libc::c_int) {
|
||||
unsafe extern "C" fn on_window_focus_changed(activity: *mut ndk_sys::ANativeActivity, focused: libc::c_int) {
|
||||
log::debug!("WindowFocusChanged: {:p} -- {}\n", activity, focused);
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
android_app_write_cmd(android_app,
|
||||
if focused != 0 { ffi::APP_CMD_GAINED_FOCUS as i8 } else { ffi::APP_CMD_LOST_FOCUS as i8});
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_native_window_created(activity: *mut ffi::ANativeActivity, window: *mut ndk_sys::ANativeWindow) {
|
||||
unsafe extern "C" fn on_native_window_created(activity: *mut ndk_sys::ANativeActivity, window: *mut ndk_sys::ANativeWindow) {
|
||||
log::debug!("NativeWindowCreated: {:p} -- {:p}\n", activity, window);
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
android_app_set_window(android_app, window);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_native_window_destroyed(activity: *mut ffi::ANativeActivity, window: *mut ndk_sys::ANativeWindow) {
|
||||
unsafe extern "C" fn on_native_window_destroyed(activity: *mut ndk_sys::ANativeActivity, window: *mut ndk_sys::ANativeWindow) {
|
||||
log::debug!("NativeWindowDestroyed: {:p} -- {:p}\n", activity, window);
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
android_app_set_window(android_app, ptr::null_mut());
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_input_queue_created(activity: *mut ffi::ANativeActivity, queue: *mut ndk_sys::AInputQueue) {
|
||||
unsafe extern "C" fn on_input_queue_created(activity: *mut ndk_sys::ANativeActivity, queue: *mut ndk_sys::AInputQueue) {
|
||||
log::debug!("InputQueueCreated: {:p} -- {:p}\n", activity, queue);
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
android_app_set_input(android_app, queue);
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_input_queue_destroyed(activity: *mut ffi::ANativeActivity, queue: *mut ndk_sys::AInputQueue) {
|
||||
unsafe extern "C" fn on_input_queue_destroyed(activity: *mut ndk_sys::ANativeActivity, queue: *mut ndk_sys::AInputQueue) {
|
||||
log::debug!("InputQueueDestroyed: {:p} -- {:p}\n", activity, queue);
|
||||
let android_app: *mut ffi::android_app = (*activity).instance.cast();
|
||||
android_app_set_input(android_app, ptr::null_mut());
|
||||
@@ -762,7 +996,7 @@ unsafe extern "C" fn on_input_queue_destroyed(activity: *mut ffi::ANativeActivit
|
||||
|
||||
#[no_mangle]
|
||||
unsafe extern "C" fn ANativeActivity_onCreate(
|
||||
activity: *mut ffi::ANativeActivity,
|
||||
activity: *mut ndk_sys::ANativeActivity,
|
||||
saved_state: *const libc::c_void,
|
||||
saved_state_size: libc::size_t,
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user