From 92f6290dbb89adadc51f13b98237c657ca3815a8 Mon Sep 17 00:00:00 2001 From: Vadim Boev <33985574+Kronka@users.noreply.github.com> Date: Mon, 27 Sep 2021 18:57:06 +0300 Subject: [PATCH] Build Android App with Visual Studio 2019 (#2013) * Add support Android build for Visual Studio * Delete projects/VS2019-Android/raylib_android/ARM64/Debug directory * Delete projects/VS2019-Android/raylib_android/raylib_android/raylib_android.NativeActivity/ARM64/Debug directory * Delete projects/VS2019-Android/raylib_android/raylib_android/raylib_android.Packaging/ARM64/Debug directory * Delete projects/VS2019-Android/raylib_android/raylib_android/raylib_android.Packaging/ARM/Debug directory * Delete projects/VS2019-Android directory * Build Android App with Visual Studio 2019 --- projects/VS2019-Android/raylib_android.sln | 75 +++ .../android_native_app_glue.c | 437 ++++++++++++++++++ .../android_native_app_glue.h | 344 ++++++++++++++ .../raylib_android.NativeActivity/main.c | 64 +++ .../raylib_android.NativeActivity.vcxproj | 226 +++++++++ ...lib_android.NativeActivity.vcxproj.filters | 10 + ...raylib_android.NativeActivity.vcxproj.user | 4 + .../AndroidManifest.xml | 22 + .../raylib_android.Packaging/build.xml | 90 ++++ .../project.properties | 3 + .../raylib_android.Packaging.androidproj | 134 ++++++ .../res/values/strings.xml | 4 + 12 files changed, 1413 insertions(+) create mode 100644 projects/VS2019-Android/raylib_android.sln create mode 100644 projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/android_native_app_glue.c create mode 100644 projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/android_native_app_glue.h create mode 100644 projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/main.c create mode 100644 projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj create mode 100644 projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj.filters create mode 100644 projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj.user create mode 100644 projects/VS2019-Android/raylib_android/raylib_android.Packaging/AndroidManifest.xml create mode 100644 projects/VS2019-Android/raylib_android/raylib_android.Packaging/build.xml create mode 100644 projects/VS2019-Android/raylib_android/raylib_android.Packaging/project.properties create mode 100644 projects/VS2019-Android/raylib_android/raylib_android.Packaging/raylib_android.Packaging.androidproj create mode 100644 projects/VS2019-Android/raylib_android/raylib_android.Packaging/res/values/strings.xml diff --git a/projects/VS2019-Android/raylib_android.sln b/projects/VS2019-Android/raylib_android.sln new file mode 100644 index 00000000..ec1880c0 --- /dev/null +++ b/projects/VS2019-Android/raylib_android.sln @@ -0,0 +1,75 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31702.278 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "raylib_android", "raylib_android", "{5C24BC76-8848-40EA-9BBB-A544648D8D5B}" +EndProject +Project("{39E2626F-3545-4960-A6E8-258AD8476CE5}") = "raylib_android.Packaging", "raylib_android\raylib_android.Packaging\raylib_android.Packaging.androidproj", "{B2231F0D-52DA-4C55-8998-5886F766553C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "raylib_android.NativeActivity", "raylib_android\raylib_android.NativeActivity\raylib_android.NativeActivity.vcxproj", "{BFB31759-4FCA-4503-BC7C-A97F705EFDB2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM = Debug|ARM + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM = Release|ARM + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|ARM.ActiveCfg = Debug|ARM + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|ARM.Build.0 = Debug|ARM + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|ARM.Deploy.0 = Debug|ARM + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|ARM64.Build.0 = Debug|ARM64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|ARM64.Deploy.0 = Debug|ARM64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|x64.ActiveCfg = Debug|x64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|x64.Build.0 = Debug|x64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|x64.Deploy.0 = Debug|x64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|x86.ActiveCfg = Debug|x86 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|x86.Build.0 = Debug|x86 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Debug|x86.Deploy.0 = Debug|x86 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|ARM.ActiveCfg = Release|ARM + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|ARM.Build.0 = Release|ARM + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|ARM.Deploy.0 = Release|ARM + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|ARM64.ActiveCfg = Release|ARM64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|ARM64.Build.0 = Release|ARM64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|ARM64.Deploy.0 = Release|ARM64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|x64.ActiveCfg = Release|x64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|x64.Build.0 = Release|x64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|x64.Deploy.0 = Release|x64 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|x86.ActiveCfg = Release|x86 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|x86.Build.0 = Release|x86 + {B2231F0D-52DA-4C55-8998-5886F766553C}.Release|x86.Deploy.0 = Release|x86 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Debug|ARM.ActiveCfg = Debug|ARM + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Debug|ARM.Build.0 = Debug|ARM + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Debug|ARM64.Build.0 = Debug|ARM64 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Debug|x64.ActiveCfg = Debug|x64 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Debug|x64.Build.0 = Debug|x64 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Debug|x86.ActiveCfg = Debug|x86 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Debug|x86.Build.0 = Debug|x86 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Release|ARM.ActiveCfg = Release|ARM + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Release|ARM.Build.0 = Release|ARM + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Release|ARM64.ActiveCfg = Release|ARM64 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Release|ARM64.Build.0 = Release|ARM64 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Release|x64.ActiveCfg = Release|x64 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Release|x64.Build.0 = Release|x64 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Release|x86.ActiveCfg = Release|x86 + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B2231F0D-52DA-4C55-8998-5886F766553C} = {5C24BC76-8848-40EA-9BBB-A544648D8D5B} + {BFB31759-4FCA-4503-BC7C-A97F705EFDB2} = {5C24BC76-8848-40EA-9BBB-A544648D8D5B} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B7ED0AA6-6B00-40AC-BF71-526D5BEEFC78} + EndGlobalSection +EndGlobal diff --git a/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/android_native_app_glue.c b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/android_native_app_glue.c new file mode 100644 index 00000000..0b837942 --- /dev/null +++ b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/android_native_app_glue.c @@ -0,0 +1,437 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * лицензировано по лицензии Apache License, версия 2.0 ( "Лицензия"); + *этот файл можно использовать только в соответствии с лицензией. + *Копию лицензии можно получить на веб-сайте + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + *Если только не требуется в соответствии с применимым законодательством или согласовано в письменном виде, программное обеспечение + * распространяется в рамках лицензии на УСЛОВИЯХ "КАК ЕСТЬ", + * БЕЗ ГАРАНТИЙ И УСЛОВИЙ ЛЮБОГО РОДА, явно выраженных и подразумеваемых. + * См. лицензию для получения информации об определенных разрешениях по использованию языка и + * ограничениях в рамках лицензии. + * + */ + +#include + +#include +#include +#include +#include +#include + +#include "android_native_app_glue.h" +#include + +#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__)) + +/* Для отладочных построений необходимо всегда включать трассировку отладки в этой библиотеке*/ +#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; +} + +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); + + 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_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) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->inputQueue = android_app->pendingInputQueue; + 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); + } + 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; + } +} + +static 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); + // После этого нельзя изменять объект android_app. +} + +static void process_input(struct android_app* app, struct android_poll_source* source) { + AInputEvent* event = NULL; + while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { + LOGV("New input event: type=%d\n", AInputEvent_getType(event)); + if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { + continue; + } + int32_t handled = 0; + if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); + AInputQueue_finishEvent(app->inputQueue, event, handled); + } +} + +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; + + 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; + + ALooper* 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Аctivity (вызванное из основного потока) +// -------------------------------------------------------------------- + +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)); + 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); + } + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGE("could not create pipe: %s", strerror(errno)); + return NULL; + } + android_app->msgread = msgpipe[0]; + android_app->msgwrite = msgpipe[1]; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&android_app->thread, &attr, android_app_entry, android_app); + + // Дождитесь запуска потока. + 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)) { + LOGE("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) { + LOGV("Destroy: %p\n", activity); + android_app_free((struct android_app*)activity->instance); +} + +static void onStart(ANativeActivity* activity) { + LOGV("Start: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); +} + +static void onResume(ANativeActivity* activity) { + LOGV("Resume: %p\n", 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; + + LOGV("SaveInstanceState: %p\n", 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) { + LOGV("Pause: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); +} + +static void onStop(ANativeActivity* activity) { + LOGV("Stop: %p\n", 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; + LOGV("ConfigurationChanged: %p\n", 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; + LOGV("LowMemory: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); +} + +static void onWindowFocusChanged(ANativeActivity* activity, int focused) { + LOGV("WindowFocusChanged: %p -- %d\n", 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) { + LOGV("NativeWindowCreated: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, window); +} + +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, NULL); +} + +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueCreated: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, queue); +} + +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, NULL); +} + +void ANativeActivity_onCreate(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + LOGV("Creating: %p\n", 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); +} diff --git a/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/android_native_app_glue.h b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/android_native_app_glue.h new file mode 100644 index 00000000..c7269a44 --- /dev/null +++ b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/android_native_app_glue.h @@ -0,0 +1,344 @@ +/* + * © 2010 The Android Open Source Project + * + * Лицензировано по лицензии Apache License, версия 2.0 ( "Лицензия"); + *этот файл можно использовать только в соответствии с лицензией. + *Копию лицензии можно получить на веб-сайте + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + *Если только не требуется в соответствии с применимым законодательством или согласовано в письменном виде, программное обеспечение + * распространяется в рамках лицензии на УСЛОВИЯХ "КАК ЕСТЬ", + * БЕЗ ГАРАНТИЙ И УСЛОВИЙ ЛЮБОГО РОДА, явно выраженных и подразумеваемых. + * См. лицензию для получения информации об определенных разрешениях по использованию языка и + * ограничениях в рамках лицензии. + * + */ + +#ifndef _ANDROID_NATIVE_APP_GLUE_H +#define _ANDROID_NATIVE_APP_GLUE_H + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Интерфейс NativeActivity, предоставленный , + * основан на наборе предоставленных приложением обратных вызовов, которые вызываются + *основным потоком действия при возникновении определенных событий. + * + * Это означает, что ни один из данных обратных вызовов _не_ _должен_ блокироваться, иначе + * существует риск принудительного закрытия приложения системой. Эта модель программирования + * прямая, простая, но имеет ограничения. + * + * Статическая библиотека threaded_native_app используется для обеспечения другой + * модели выполнения, в которой приложение может реализовать свой собственный цикл главного события + * в другом потоке вместо этого. Это работает так: + * + * 1/ Приложение должно предоставить функцию с именем android_main(), которая + * будет вызываться при создании действия в новом потоке, + * отличающемся от основного потока действия. + * + * 2/ android_main() получает указатель на допустимую структуру android_app, + * которая содержит ссылки на другие важные объекты, например экземпляр объекта + * ANativeActivity, где выполняется приложение. + * + * 3/ Объект android_app содержит экземпляр ALooper, который уже + * ожидает две важных вещи: + * + * - событий жизненного цикла действия (например, "pause", "resume"). См. объявления APP_CMD_XXX + * ниже. + * + * - входных событий, поступающих из очереди AInputQueue, присоединенной к действию. + * + * Каждое из этих событий соответствует идентификатору ALooper, возвращенному + * ALooper_pollOnce со значениями LOOPER_ID_MAIN и LOOPER_ID_INPUT, + *, соответственно. + * + * Ваше приложение может использовать тот же ALooper для прослушивания дополнительных + * дескрипторов файла. Они могут быть основаны либо на обратных вызовах, либо поступают с идентификаторами возврата, + * начинающимися с LOOPER_ID_USER. + * + * 4/ При получении события LOOPER_ID_MAIN или LOOPER_ID_INPUT + * возвращенные данные будут указывать на структуру android_poll_source. Для нее + * можно вызвать функцию process() и заполнить android_app->onAppCmd + * и android_app->onInputEvent, для того чтобы они вызывались для вашей собственной обработки + * события. + * + * Вместо этого можно вызвать функции нижнего уровня для чтения и обработки + * данных непосредственно... посмотрите на реализации process_cmd() и process_input() + * в приклеивании, чтобы выяснить, как это делается. + * + * См. пример "native-activity" в NDK с + * полной демонстрацией использования. Также посмотрите JavaDoc в NativeActivity. + */ + +struct android_app; + +/** + * Данные, связанные с ALooper fd, которые будут возвращаться как outData + * при готовности данных в этом источнике. + */ +struct android_poll_source { + // Идентификатор данного источника. Может быть LOOPER_ID_MAIN или + // LOOPER_ID_INPUT. + int32_t id; + + // android_app, с которым связан данный идентификатор. + struct android_app* app; + + // Функция, вызываемая для стандартной обработки данных из + // этого источника. + void (*process)(struct android_app* app, struct android_poll_source* source); +}; + +/** + * Это интерфейс стандартного кода приклеивания поточного + * приложения. В этой модели код приложения выполняется + * в своем собственном потоке, отдельном от основного потока процесса. + * Не требуется связь данного потока с ВМ Java + *, хотя это необходимо для выполнения вызовов JNI любых + * объектов Java. + */ +struct android_app { + // Приложение может поместить указатель на свой собственный объект состояния + // здесь, если нужно. + void* userData; + + // Введите здесь код функции для обработки основных команд приложения (APP_CMD_*) + void (*onAppCmd)(struct android_app* app, int32_t cmd); + + // Введите здесь код функции для обработки входных событий. Сейчас + // событие уже было предварительно отправлено и будет завершено при + // возврате. Верните 1, если событие обработано, 0 — для любой диспетчеризации + // по умолчанию. + int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); + + // Экземпляр объекта ANativeActivity, в котором выполняется это приложение. + ANativeActivity* activity; + + // Текущая конфигурация, в которой выполняется это приложение. + AConfiguration* config; + + // Это последнее сохраненное состояние экземпляра, предоставленное во время создания. + // Значение равно NULL, если состояния не было. Можно использовать это по мере необходимости; + // память останется доступной до вызова android_app_exec_cmd() для + // APP_CMD_RESUME, после чего она будет освобождена, а savedState получит значение NULL. + // Эти переменные необходимо изменять только при обработке APP_CMD_SAVE_STATE, + // когда их значения будут инициализироваться в NULL и можно будет выполнить malloc для + // состояния и поместить здесь информацию. В этом случае память будет + // освобождена позднее. + void* savedState; + size_t savedStateSize; + + // ALooper, связанный с потоком приложения. + ALooper* looper; + + // Если значение не равно NULL, то это входная очередь, из которой приложение будет + // получать входные события пользователя. + AInputQueue* inputQueue; + + // Если значение не равно NULL, то это поверхность окна, в котором приложение может рисовать. + ANativeWindow* window; + + // Текущий прямоугольник содержимого окна. Это область, в которой + // должно помещаться содержимое окна, чтобы его видел пользователь. + ARect contentRect; + + // Текущее состояние действия приложения. Может быть APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE или APP_CMD_STOP; см. ниже. + int activityState; + + // Значение не равно нулю, когда NativeActivity приложения + // разрушается и ожидает завершения потока приложения. + int destroyRequested; + + // ------------------------------------------------- + // Ниже показан "частная" реализация кода прилипания. + + 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 команд, поступающих из основного потока приложения, который + * возвращается как идентификатор от ALooper_pollOnce(). Данные для этого идентификатора + * являются указателем на структуру android_poll_source. + * Их можно извлечь и обработать с помощью android_app_read_cmd() + * и android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Идентификатор данных Looper событий, поступающий из AInputQueue окна + * приложения, который возвращается как идентификатор из + * ALooper_pollOnce(). Данные этого идентификатора являются указателем на структуру + * android_poll_source. Их можно прочитать через объект inputQueue + * приложения android_app. + */ + LOOPER_ID_INPUT = 2, + + /** + * Запуск определяемых пользователем идентификаторов ALooper. + */ + LOOPER_ID_USER = 3, +}; + +enum { + /** + * Команда из основного потока: AInputQueue изменена. После обработки + * этой команды android_app->inputQueue будет обновлена в новую очередь + * (или NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Команда из основного потока: новое окно ANativeWindow готово к использованию. После + * получения этой команды окно android_app-> будет содержать новую поверхность + *окна. + */ + APP_CMD_INIT_WINDOW, + + /** + * Команда из основного потока: существующее окно ANativeWindow необходимо + * прекратить. После получения этой команды окно android_app->по-прежнему + * содержит существующее окно; после вызова android_app_exec_cmd + * оно получит значение NULL. + */ + APP_CMD_TERM_WINDOW, + + /** + * Команда из основного потока: текущее окно ANativeWindow изменило размер. + * Перерисуйте согласно новом размеру. + */ + APP_CMD_WINDOW_RESIZED, + + /** + * Команда из основного потока: системе необходимо, чтобы текущее окно ANativeWindow + * было перерисовано. Необходимо перерисовать окно перед ее передачей в + * android_app_exec_cmd(), чтобы избежать переходных сбоев рисования. + */ + APP_CMD_WINDOW_REDRAW_NEEDED, + + /** + * Команда из основного потока: область содержимого окна изменена + * таким образом, что из функционального ввода окно показывается или скрывается. Можно + * найти новый прямоугольник содержимого в android_app::contentRect. + */ + APP_CMD_CONTENT_RECT_CHANGED, + + /** + * Команда из основного потока: окно действия приложения получило + * фокус ввода. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Команда из основного потока: окно действия приложения потеряло + * фокус ввода. + */ + APP_CMD_LOST_FOCUS, + + /** + * Команда из основного потока: изменена текущая конфигурация устройства. + */ + APP_CMD_CONFIG_CHANGED, + + /** + * Команда из основного потока: системе не хватает памяти. + * Попробуйте уменьшить использование памяти. + */ + APP_CMD_LOW_MEMORY, + + /** + * Команда из основного потока: действие приложения было запущено. + */ + APP_CMD_START, + + /** + * Команда из основного потока: действие приложения было возобновлено. + */ + APP_CMD_RESUME, + + /** + * Команда из основного потока: приложение должно создать новое сохраненное состояние + * для себя, чтобы восстанавливаться из него позднее в случае необходимости. Если вы сохранили состояние, + * выделите его с использованием malloc и поместите в android_app.savedState с + * размером android_app.savedStateSize. Память будет освобождена + * позднее. + */ + APP_CMD_SAVE_STATE, + + /** + * Команда из основного потока: пауза в действии приложения. + */ + APP_CMD_PAUSE, + + /** + * Команда из основного потока: действие приложения было остановлено. + */ + APP_CMD_STOP, + + /** + * Команда из основного потока: действие приложения уничтожается, + * и ожидает очистки потока приложения и выхода перед обработкой. + */ + APP_CMD_DESTROY, +}; + +/** + * Вызовите, когда ALooper_pollAll() возвращает LOOPER_ID_MAIN, при чтении следующего сообщения команды + *приложения. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Вызовите с помощью команды, возвращенной android_app_read_cmd() для выполнения + * начальной предварительной обработки данной команды. Можно выполнить собственные + * действия для команды после вызова этой функции. + */ +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Вызовите с помощью команды, возвращенной android_app_read_cmd(), для + * окончательной предварительной обработки данной команды. Необходимо завершить собственные + * действия с командой до вызова этой функции. + */ +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Это функция, которую должен реализовать код приложения, представляет собой + * главный вход в приложение. + */ +extern void android_main(struct android_app* app); + +#ifdef __cplusplus +} +#endif + +#endif /* _ANDROID_NATIVE_APP_GLUE_H */ diff --git a/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/main.c b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/main.c new file mode 100644 index 00000000..dce3f503 --- /dev/null +++ b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/main.c @@ -0,0 +1,64 @@ +/******************************************************************************************* +* +* raylib [core] example - Basic window +* +* This example has been created using raylib 3.8 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Example contributed by (@) and reviewed by Ramon Santamaria (@raysan5) +* +* Copyright (c) 2021 (@) + +* Adapt for Visual Studio: Vadim Boev (Kronka Dev) +* +********************************************************************************************/ +#include "android_native_app_glue.h" + +#include "../../../../src/raylib.h" + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window"); + + // TODO: Load resources / Initialize variables at this point + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // TODO: Update variables / Implement example logic at this point + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + // TODO: Draw everything that requires to be drawn at this point: + + DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); // Example + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + + // TODO: Unload all loaded resources at this point + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj new file mode 100644 index 00000000..6b7b5a08 --- /dev/null +++ b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj @@ -0,0 +1,226 @@ + + + + + Debug + ARM + + + Release + ARM + + + Debug + ARM64 + + + Release + ARM64 + + + Debug + x64 + + + Release + x64 + + + Debug + x86 + + + Release + x86 + + + + {bfb31759-4fca-4503-bc7c-a97f705efdb2} + Android + raylib_android + en-US + 14.0 + Android + 3.0 + + + + DynamicLibrary + true + Clang_5_0 + + + DynamicLibrary + false + Clang_5_0 + + + DynamicLibrary + true + Clang_5_0 + + + DynamicLibrary + false + Clang_5_0 + + + DynamicLibrary + true + Clang_5_0 + android-29 + + + DynamicLibrary + false + Clang_5_0 + + + DynamicLibrary + true + Clang_5_0 + + + DynamicLibrary + false + Clang_5_0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + libmain + + + + + + + + + + + NotUsing + pch.h + CompileAsC + c99 + Default + CompileAsC + + + %(LibraryDependencies);EGL;GLESv2;log;android;c;m;raylib + ../../../../src/;%(AdditionalLibraryDirectories) + + + + + NotUsing + pch.h + CompileAsC + c99 + Default + CompileAsC + + + %(LibraryDependencies);GLESv1_CM;EGL; + .;%(AdditionalLibraryDirectories) + + + + + Use + pch.h + CompileAsCpp + + + %(LibraryDependencies);GLESv1_CM;EGL; + + + + + Use + pch.h + CompileAsCpp + + + %(LibraryDependencies);GLESv1_CM;EGL; + + + + + Use + pch.h + CompileAsCpp + + + %(LibraryDependencies);GLESv1_CM;EGL; + + + + + Use + pch.h + CompileAsCpp + + + %(LibraryDependencies);GLESv1_CM;EGL; + + + + + Use + pch.h + CompileAsCpp + + + %(LibraryDependencies);GLESv1_CM;EGL; + + + + + Use + pch.h + CompileAsCpp + + + %(LibraryDependencies);GLESv1_CM;EGL; + + + + + + + + + + + + + \ No newline at end of file diff --git a/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj.filters b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj.filters new file mode 100644 index 00000000..e9a26e2d --- /dev/null +++ b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj.filters @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj.user b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj.user new file mode 100644 index 00000000..0f14913f --- /dev/null +++ b/projects/VS2019-Android/raylib_android/raylib_android.NativeActivity/raylib_android.NativeActivity.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/projects/VS2019-Android/raylib_android/raylib_android.Packaging/AndroidManifest.xml b/projects/VS2019-Android/raylib_android/raylib_android.Packaging/AndroidManifest.xml new file mode 100644 index 00000000..8d58d375 --- /dev/null +++ b/projects/VS2019-Android/raylib_android/raylib_android.Packaging/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/VS2019-Android/raylib_android/raylib_android.Packaging/build.xml b/projects/VS2019-Android/raylib_android/raylib_android.Packaging/build.xml new file mode 100644 index 00000000..4dd78480 --- /dev/null +++ b/projects/VS2019-Android/raylib_android/raylib_android.Packaging/build.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/VS2019-Android/raylib_android/raylib_android.Packaging/project.properties b/projects/VS2019-Android/raylib_android/raylib_android.Packaging/project.properties new file mode 100644 index 00000000..3c100376 --- /dev/null +++ b/projects/VS2019-Android/raylib_android/raylib_android.Packaging/project.properties @@ -0,0 +1,3 @@ +# Project target +target=$(androidapilevel) +# Provide path to the directory where prebuilt external jar files are by setting jar.libs.dir= diff --git a/projects/VS2019-Android/raylib_android/raylib_android.Packaging/raylib_android.Packaging.androidproj b/projects/VS2019-Android/raylib_android/raylib_android.Packaging/raylib_android.Packaging.androidproj new file mode 100644 index 00000000..030d7174 --- /dev/null +++ b/projects/VS2019-Android/raylib_android/raylib_android.Packaging/raylib_android.Packaging.androidproj @@ -0,0 +1,134 @@ + + + + + Debug + ARM + + + Release + ARM + + + Debug + ARM64 + + + Release + ARM64 + + + Debug + x64 + + + Release + x64 + + + Debug + x86 + + + Release + x86 + + + + raylib_android + 14.0 + 1.0 + {b2231f0d-52da-4c55-8998-5886f766553c} + + + + true + Application + + + false + Application + + + true + Application + + + false + Application + + + true + Application + + + false + Application + + + true + Application + + + false + Application + + + + + + + + + $(RootNamespace) + + + + + $(RootNamespace) + + + + + $(RootNamespace) + + + + + $(RootNamespace) + + + + + $(RootNamespace) + + + + + $(RootNamespace) + + + + + $(RootNamespace) + + + + + $(RootNamespace) + + + + + + + + + + + {bfb31759-4fca-4503-bc7c-a97f705efdb2} + + + + + \ No newline at end of file diff --git a/projects/VS2019-Android/raylib_android/raylib_android.Packaging/res/values/strings.xml b/projects/VS2019-Android/raylib_android/raylib_android.Packaging/res/values/strings.xml new file mode 100644 index 00000000..98afc467 --- /dev/null +++ b/projects/VS2019-Android/raylib_android/raylib_android.Packaging/res/values/strings.xml @@ -0,0 +1,4 @@ + + + raylib_android.Packaging +