2025-08-27 15:09:05 +09:00
|
|
|
#include <jni.h>
|
2025-08-26 13:32:53 +09:00
|
|
|
#include <android/native_window.h>
|
2025-08-26 18:24:06 +09:00
|
|
|
#include <android/native_window_jni.h>
|
2025-08-26 13:32:53 +09:00
|
|
|
#include <android/log.h>
|
2025-08-26 18:24:06 +09:00
|
|
|
#include <thread>
|
2025-08-27 15:09:05 +09:00
|
|
|
#include "Renderer.h"
|
|
|
|
|
#include "Preloader.h"
|
2025-08-26 18:24:06 +09:00
|
|
|
|
2025-08-26 13:32:53 +09:00
|
|
|
#define LOG_TAG "NativeRenderer"
|
|
|
|
|
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
|
|
|
|
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
|
|
|
|
|
2025-08-28 15:13:40 +09:00
|
|
|
// --- ⬇️ 전역 렌더러, 프리로더 포인터 삭제! ⬇️ ---
|
|
|
|
|
// Renderer* renderer = nullptr;
|
|
|
|
|
// Preloader* preloader = nullptr;
|
|
|
|
|
|
|
|
|
|
// JavaVM과 콜백 관련 전역 변수는 앱 프로세스 전체에서 유일하므로 유지합니다.
|
2025-08-27 15:09:05 +09:00
|
|
|
JavaVM* g_vm = nullptr;
|
|
|
|
|
jobject g_callback_obj = nullptr;
|
|
|
|
|
jmethodID g_callback_method_id = nullptr;
|
2025-08-26 18:24:06 +09:00
|
|
|
|
2025-08-28 15:13:40 +09:00
|
|
|
// JNI_OnLoad, callNextMediaCallback 함수는 기존과 동일
|
2025-08-27 15:09:05 +09:00
|
|
|
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|
|
|
|
g_vm = vm;
|
|
|
|
|
return JNI_VERSION_1_6;
|
2025-08-26 18:24:06 +09:00
|
|
|
}
|
2025-08-27 15:09:05 +09:00
|
|
|
void callNextMediaCallback() {
|
|
|
|
|
JNIEnv* env;
|
|
|
|
|
bool isAttached = false;
|
|
|
|
|
// 현재 스레드가 JVM에 연결되어 있지 않다면 연결
|
|
|
|
|
if (g_vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
|
2025-08-28 15:13:40 +09:00
|
|
|
if (g_vm->AttachCurrentThread(&env, nullptr) != JNI_OK) {
|
|
|
|
|
LOGE("Failed to attach current thread to JVM");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-08-27 15:09:05 +09:00
|
|
|
isAttached = true;
|
2025-08-26 18:24:06 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-27 15:09:05 +09:00
|
|
|
if (g_callback_obj && g_callback_method_id) {
|
|
|
|
|
env->CallVoidMethod(g_callback_obj, g_callback_method_id);
|
2025-08-26 18:24:06 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-27 15:09:05 +09:00
|
|
|
// 이전에 연결되지 않았다면 연결 해제
|
|
|
|
|
if (isAttached) {
|
|
|
|
|
g_vm->DetachCurrentThread();
|
2025-08-26 18:24:06 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 15:13:40 +09:00
|
|
|
// C++ 객체 포인터를 jlong으로, jlong을 C++ 객체 포인터로 안전하게 변환하는 헬퍼 함수
|
|
|
|
|
template<typename T>
|
|
|
|
|
T* toNative(jlong handle) {
|
|
|
|
|
return reinterpret_cast<T*>(handle);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-27 15:09:05 +09:00
|
|
|
extern "C" {
|
2025-08-26 18:24:06 +09:00
|
|
|
|
2025-08-28 15:13:40 +09:00
|
|
|
// [수정] jboolean -> jlong. Renderer 객체를 생성하고 그 주소(핸들)를 반환
|
|
|
|
|
JNIEXPORT jlong JNICALL
|
2025-08-27 15:09:05 +09:00
|
|
|
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeInit(JNIEnv* env, jobject) {
|
2025-08-28 15:13:40 +09:00
|
|
|
// Preloader는 Renderer가 내부적으로 소유하도록 변경하는 것이 더 좋습니다.
|
|
|
|
|
// 여기서는 간단하게 Renderer만 생성하여 핸들링합니다.
|
|
|
|
|
Renderer* renderer = new Renderer();
|
|
|
|
|
LOGI("Native renderer instance created at address: %p", renderer);
|
|
|
|
|
return reinterpret_cast<jlong>(renderer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// [수정] 핸들을 파라미터로 받아 해당 객체를 파괴
|
|
|
|
|
JNIEXPORT void JNICALL
|
|
|
|
|
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeDestroy(JNIEnv* env, jobject, jlong nativeHandle) {
|
|
|
|
|
Renderer* renderer = toNative<Renderer>(nativeHandle);
|
|
|
|
|
if (renderer) {
|
|
|
|
|
LOGI("Destroying native renderer instance at address: %p", renderer);
|
|
|
|
|
delete renderer;
|
2025-08-26 18:24:06 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-28 15:13:40 +09:00
|
|
|
// [수정] 모든 함수가 nativeHandle을 첫 파라미터로 받도록 변경
|
2025-08-27 15:09:05 +09:00
|
|
|
JNIEXPORT void JNICALL
|
2025-08-28 15:13:40 +09:00
|
|
|
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeRender(JNIEnv* env, jobject, jlong nativeHandle, jobject surface) {
|
|
|
|
|
Renderer* renderer = toNative<Renderer>(nativeHandle);
|
2025-08-27 15:09:05 +09:00
|
|
|
if (!renderer || !surface) return;
|
2025-08-26 18:24:06 +09:00
|
|
|
|
2025-08-27 15:09:05 +09:00
|
|
|
ANativeWindow* window = ANativeWindow_fromSurface(env, surface);
|
|
|
|
|
if (!window) {
|
|
|
|
|
LOGE("Could not get ANativeWindow from Surface.");
|
2025-08-26 13:32:53 +09:00
|
|
|
return;
|
|
|
|
|
}
|
2025-08-27 15:09:05 +09:00
|
|
|
renderer->renderFrame(window);
|
|
|
|
|
ANativeWindow_release(window);
|
2025-08-26 18:24:06 +09:00
|
|
|
}
|
2025-08-26 13:32:53 +09:00
|
|
|
|
2025-08-26 18:24:06 +09:00
|
|
|
JNIEXPORT void JNICALL
|
2025-08-28 15:13:40 +09:00
|
|
|
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeStartNextPreload(JNIEnv* env, jobject, jlong nativeHandle, jint fd) {
|
|
|
|
|
Renderer* renderer = toNative<Renderer>(nativeHandle);
|
2025-08-27 15:09:05 +09:00
|
|
|
if (renderer) {
|
2025-08-28 15:13:40 +09:00
|
|
|
// Preloader가 Renderer의 일부가 되었다고 가정하고 호출 (추후 Renderer 수정 필요)
|
|
|
|
|
// 여기서는 간단하게 setNextMedia를 호출하는 것으로 변경합니다.
|
|
|
|
|
renderer->setNextMedia(fd);
|
2025-08-26 18:24:06 +09:00
|
|
|
}
|
2025-08-28 15:13:40 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JNIEXPORT void JNICALL
|
|
|
|
|
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeSetAnimationSpeed(JNIEnv* env, jobject, jlong nativeHandle, jfloat speed) {
|
|
|
|
|
// ...
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- ⬇️ 새로 추가할 JNI 함수들 ⬇️ ---
|
|
|
|
|
JNIEXPORT void JNICALL
|
|
|
|
|
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeSetAnimationMode(JNIEnv* env, jobject, jlong nativeHandle, jint mode) {
|
|
|
|
|
Renderer* renderer = toNative<Renderer>(nativeHandle);
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer->setAnimationMode(mode);
|
2025-08-27 15:09:05 +09:00
|
|
|
}
|
2025-08-26 18:24:06 +09:00
|
|
|
}
|
2025-08-28 15:13:40 +09:00
|
|
|
|
2025-08-26 18:24:06 +09:00
|
|
|
JNIEXPORT void JNICALL
|
2025-08-28 15:13:40 +09:00
|
|
|
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeSetFadeDuration(JNIEnv* env, jobject, jlong nativeHandle, jint duration) {
|
|
|
|
|
Renderer* renderer = toNative<Renderer>(nativeHandle);
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer->setFadeDuration(duration);
|
|
|
|
|
}
|
2025-08-26 13:32:53 +09:00
|
|
|
}
|
|
|
|
|
|
2025-08-27 15:09:05 +09:00
|
|
|
JNIEXPORT void JNICALL
|
2025-08-28 15:13:40 +09:00
|
|
|
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeSetPageTurnDelay(JNIEnv* env, jobject, jlong nativeHandle, jint duration) {
|
|
|
|
|
Renderer* renderer = toNative<Renderer>(nativeHandle);
|
|
|
|
|
if (renderer) {
|
|
|
|
|
renderer->setPageTurnDelay(duration);
|
|
|
|
|
}
|
2025-08-26 13:32:53 +09:00
|
|
|
}
|
2025-08-26 18:24:06 +09:00
|
|
|
|
2025-08-28 15:13:40 +09:00
|
|
|
JNIEXPORT jstring JNICALL
|
|
|
|
|
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeGetDebugInfo(JNIEnv* env, jobject, jlong nativeHandle) {
|
|
|
|
|
Renderer* renderer = toNative<Renderer>(nativeHandle);
|
|
|
|
|
if (renderer) {
|
|
|
|
|
std::string debugInfo = renderer->getDebugInfo();
|
|
|
|
|
return env->NewStringUTF(debugInfo.c_str());
|
|
|
|
|
}
|
|
|
|
|
return env->NewStringUTF("Renderer handle is invalid or null.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- 참고: nativeSetNextMediaCallback은 전역이라 일단 그대로 둡니다 ---
|
2025-08-26 18:24:06 +09:00
|
|
|
JNIEXPORT void JNICALL
|
2025-08-27 15:09:05 +09:00
|
|
|
Java_bums_lunatic_launcher_wall_NativeRenderer_nativeSetNextMediaCallback(JNIEnv* env, jobject, jobject callback) {
|
2025-08-28 15:13:40 +09:00
|
|
|
if (g_callback_obj) {
|
|
|
|
|
env->DeleteGlobalRef(g_callback_obj);
|
|
|
|
|
}
|
|
|
|
|
g_callback_obj = env->NewGlobalRef(callback);
|
2025-08-27 15:09:05 +09:00
|
|
|
jclass clazz = env->GetObjectClass(callback);
|
|
|
|
|
g_callback_method_id = env->GetMethodID(clazz, "onNextMediaRequested", "()V");
|
2025-08-26 18:24:06 +09:00
|
|
|
|
2025-08-27 15:09:05 +09:00
|
|
|
if (!g_callback_method_id) {
|
|
|
|
|
LOGE("Could not find onNextMediaRequested method ID.");
|
2025-08-26 18:24:06 +09:00
|
|
|
}
|
|
|
|
|
}
|
2025-08-28 15:13:40 +09:00
|
|
|
|
|
|
|
|
} // extern "C"
|