[UI] Android surface [skip appveyor]
This commit is contained in:
parent
c6fc8f706a
commit
413d7ded49
|
@ -81,4 +81,8 @@ android {
|
|||
path file('../../../build/xenia.wks.Android.mk')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.jetbrains:annotations:15.0'
|
||||
}
|
|
@ -29,7 +29,9 @@
|
|||
android:supportsRtl="true"
|
||||
android:theme="@android:style/Theme.Material.Light">
|
||||
|
||||
<activity android:name="jp.xenia.emulator.WindowDemoActivity">
|
||||
<activity
|
||||
android:name="jp.xenia.emulator.WindowDemoActivity"
|
||||
android:label="@string/activity_label_window_demo">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
|
|
@ -7,15 +7,15 @@ public class XeniaRuntimeException extends RuntimeException {
|
|||
public XeniaRuntimeException() {
|
||||
}
|
||||
|
||||
public XeniaRuntimeException(String name) {
|
||||
public XeniaRuntimeException(final String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
public XeniaRuntimeException(String name, Throwable cause) {
|
||||
public XeniaRuntimeException(final String name, final Throwable cause) {
|
||||
super(name, cause);
|
||||
}
|
||||
|
||||
public XeniaRuntimeException(Exception cause) {
|
||||
public XeniaRuntimeException(final Exception cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
package jp.xenia.emulator;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
public class WindowDemoActivity extends WindowedAppActivity {
|
||||
@Override
|
||||
protected String getWindowedAppIdentifier() {
|
||||
return "xenia_ui_window_vulkan_demo";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_window_demo);
|
||||
setWindowSurfaceView(findViewById(R.id.window_demo_surface_view));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package jp.xenia.emulator;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
public class WindowSurfaceView extends SurfaceView {
|
||||
public WindowSurfaceView(final Context context) {
|
||||
super(context);
|
||||
// Native drawing is invoked from onDraw.
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
public WindowSurfaceView(final Context context, final AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
public WindowSurfaceView(
|
||||
final Context context, final AttributeSet attrs, final int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
public WindowSurfaceView(
|
||||
final Context context, final AttributeSet attrs, final int defStyleAttr,
|
||||
final int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(final Canvas canvas) {
|
||||
final Context context = getContext();
|
||||
if (!(context instanceof WindowedAppActivity)) {
|
||||
return;
|
||||
}
|
||||
final WindowedAppActivity activity = (WindowedAppActivity) context;
|
||||
activity.onWindowSurfaceDraw(false);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,11 @@ package jp.xenia.emulator;
|
|||
import android.app.Activity;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Bundle;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jp.xenia.XeniaRuntimeException;
|
||||
|
||||
|
@ -12,21 +17,87 @@ public abstract class WindowedAppActivity extends Activity {
|
|||
System.loadLibrary("xenia-ui-window-vulkan-demo");
|
||||
}
|
||||
|
||||
private long mAppContext;
|
||||
private final WindowSurfaceOnLayoutChangeListener mWindowSurfaceOnLayoutChangeListener =
|
||||
new WindowSurfaceOnLayoutChangeListener();
|
||||
private final WindowSurfaceHolderCallback mWindowSurfaceHolderCallback =
|
||||
new WindowSurfaceHolderCallback();
|
||||
|
||||
private native long initializeWindowedAppOnCreateNative(
|
||||
// May be 0 while destroying (mainly while the superclass is).
|
||||
private long mAppContext = 0;
|
||||
|
||||
@Nullable
|
||||
private WindowSurfaceView mWindowSurfaceView = null;
|
||||
|
||||
private native long initializeWindowedAppOnCreate(
|
||||
String windowedAppIdentifier, AssetManager assetManager);
|
||||
|
||||
private native void onDestroyNative(long appContext);
|
||||
|
||||
private native void onWindowSurfaceLayoutChange(
|
||||
long appContext, int left, int top, int right, int bottom);
|
||||
|
||||
private native void onWindowSurfaceChanged(long appContext, Surface windowSurface);
|
||||
|
||||
private native void paintWindow(long appContext, boolean forcePaint);
|
||||
|
||||
protected abstract String getWindowedAppIdentifier();
|
||||
|
||||
protected void setWindowSurfaceView(@Nullable final WindowSurfaceView windowSurfaceView) {
|
||||
if (mWindowSurfaceView == windowSurfaceView) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Detach from the old surface.
|
||||
if (mWindowSurfaceView != null) {
|
||||
mWindowSurfaceView.getHolder().removeCallback(mWindowSurfaceHolderCallback);
|
||||
mWindowSurfaceView.removeOnLayoutChangeListener(mWindowSurfaceOnLayoutChangeListener);
|
||||
mWindowSurfaceView = null;
|
||||
if (mAppContext != 0) {
|
||||
onWindowSurfaceChanged(mAppContext, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (windowSurfaceView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWindowSurfaceView = windowSurfaceView;
|
||||
// The native window code assumes that, when the surface exists, it covers the entire
|
||||
// window.
|
||||
// FIXME(Triang3l): This doesn't work if the layout has already been performed.
|
||||
mWindowSurfaceView.addOnLayoutChangeListener(mWindowSurfaceOnLayoutChangeListener);
|
||||
final SurfaceHolder windowSurfaceHolder = mWindowSurfaceView.getHolder();
|
||||
windowSurfaceHolder.addCallback(mWindowSurfaceHolderCallback);
|
||||
// If setting after the creation of the surface.
|
||||
if (mAppContext != 0) {
|
||||
final Surface windowSurface = windowSurfaceHolder.getSurface();
|
||||
if (windowSurface != null) {
|
||||
onWindowSurfaceChanged(mAppContext, windowSurface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onWindowSurfaceDraw(final boolean forcePaint) {
|
||||
if (mAppContext == 0) {
|
||||
return;
|
||||
}
|
||||
paintWindow(mAppContext, forcePaint);
|
||||
}
|
||||
|
||||
// Used from the native WindowedAppContext. May be called from non-UI threads.
|
||||
protected void postInvalidateWindowSurface() {
|
||||
if (mWindowSurfaceView == null) {
|
||||
return;
|
||||
}
|
||||
mWindowSurfaceView.postInvalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final String windowedAppIdentifier = getWindowedAppIdentifier();
|
||||
mAppContext = initializeWindowedAppOnCreateNative(windowedAppIdentifier, getAssets());
|
||||
mAppContext = initializeWindowedAppOnCreate(windowedAppIdentifier, getAssets());
|
||||
if (mAppContext == 0) {
|
||||
finish();
|
||||
throw new XeniaRuntimeException(
|
||||
|
@ -36,10 +107,54 @@ public abstract class WindowedAppActivity extends Activity {
|
|||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
setWindowSurfaceView(null);
|
||||
if (mAppContext != 0) {
|
||||
onDestroyNative(mAppContext);
|
||||
}
|
||||
mAppContext = 0;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private class WindowSurfaceOnLayoutChangeListener implements View.OnLayoutChangeListener {
|
||||
@Override
|
||||
public void onLayoutChange(
|
||||
final View v, final int left, final int top, final int right, final int bottom,
|
||||
final int oldLeft, final int oldTop, final int oldRight, final int oldBottom) {
|
||||
if (mAppContext != 0) {
|
||||
onWindowSurfaceLayoutChange(mAppContext, left, top, right, bottom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class WindowSurfaceHolderCallback implements SurfaceHolder.Callback2 {
|
||||
@Override
|
||||
public void surfaceCreated(final SurfaceHolder holder) {
|
||||
if (mAppContext == 0) {
|
||||
return;
|
||||
}
|
||||
onWindowSurfaceChanged(mAppContext, holder.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(
|
||||
final SurfaceHolder holder, final int format, final int width, final int height) {
|
||||
if (mAppContext == 0) {
|
||||
return;
|
||||
}
|
||||
onWindowSurfaceChanged(mAppContext, holder.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(final SurfaceHolder holder) {
|
||||
if (mAppContext == 0) {
|
||||
return;
|
||||
}
|
||||
onWindowSurfaceChanged(mAppContext, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceRedrawNeeded(final SurfaceHolder holder) {
|
||||
onWindowSurfaceDraw(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<jp.xenia.emulator.WindowSurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/window_demo_surface_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="jp.xenia.emulator.WindowDemoActivity">
|
||||
|
||||
</RelativeLayout>
|
||||
tools:context="jp.xenia.emulator.WindowDemoActivity" />
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
<resources>
|
||||
<string name="app_name">Xenia</string>
|
||||
<string name="activity_label_window_demo">Xenia Window Demo</string>
|
||||
</resources>
|
|
@ -26,13 +26,13 @@
|
|||
// presenting from the thread refreshing the guest output is not absolutely
|
||||
// necessary, but still may be nice for bypassing the scheduling and the
|
||||
// message queue.
|
||||
// On GTK, the frame rate of draw signals is limited to the display refresh rate
|
||||
// internally, so for the lowest latency especially in case the refresh rates
|
||||
// differ significantly on the guest and the host (like 30/60 Hz presented to
|
||||
// 144 Hz), drawing from the guest output refreshing thread is highly desirable.
|
||||
// Presenting directly from the GPU emulation thread also makes debugging GPU
|
||||
// emulation easier with external tools, as presenting in most cases happens
|
||||
// exactly between emulation frames.
|
||||
// On Android and GTK, the frame rate of draw events is limited to the display
|
||||
// refresh rate internally, so for the lowest latency especially in case the
|
||||
// refresh rates differ significantly on the guest and the host (like 30/60 Hz
|
||||
// presented to 144 Hz), drawing from the guest output refreshing thread is
|
||||
// highly desirable. Presenting directly from the GPU emulation thread also
|
||||
// makes debugging GPU emulation easier with external tools, as presenting in
|
||||
// most cases happens exactly between emulation frames.
|
||||
DEFINE_bool(
|
||||
host_present_from_non_ui_thread, true,
|
||||
"Allow the GPU emulation thread to present the guest output to the host "
|
||||
|
|
|
@ -974,8 +974,8 @@ class Presenter {
|
|||
// frame rates wasting the CPU and the GPU resources and starving everything
|
||||
// else. The waits performed here must be interruptible by guest output
|
||||
// presentation requests to prevent adding arbitrary amounts of latency to it.
|
||||
// On GTK, this is not needed, the frame rate of draw signals is limited to
|
||||
// the display refresh rate internally.
|
||||
// On Android and GTK, this is not needed, the frame rate of draw events is
|
||||
// limited to the display refresh rate internally.
|
||||
#if XE_PLATFORM_WIN32
|
||||
static Microsoft::WRL::ComPtr<IDXGIOutput> GetDXGIOutputForMonitor(
|
||||
IDXGIFactory1* factory, HMONITOR monitor);
|
||||
|
|
|
@ -29,17 +29,42 @@ std::unique_ptr<Window> Window::Create(WindowedAppContext& app_context,
|
|||
|
||||
AndroidWindow::~AndroidWindow() {
|
||||
EnterDestructor();
|
||||
AndroidWindowedAppContext& android_app_context =
|
||||
auto& android_app_context =
|
||||
static_cast<AndroidWindowedAppContext&>(app_context());
|
||||
if (android_app_context.GetActivityWindow() == this) {
|
||||
android_app_context.SetActivityWindow(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidWindow::OnActivitySurfaceLayoutChange() {
|
||||
auto& android_app_context =
|
||||
static_cast<const AndroidWindowedAppContext&>(app_context());
|
||||
assert_true(android_app_context.GetActivityWindow() == this);
|
||||
uint32_t physical_width =
|
||||
uint32_t(android_app_context.window_surface_layout_right() -
|
||||
android_app_context.window_surface_layout_left());
|
||||
uint32_t physical_height =
|
||||
uint32_t(android_app_context.window_surface_layout_bottom() -
|
||||
android_app_context.window_surface_layout_top());
|
||||
OnDesiredLogicalSizeUpdate(SizeToLogical(physical_width),
|
||||
SizeToLogical(physical_height));
|
||||
WindowDestructionReceiver destruction_receiver(this);
|
||||
OnActualSizeUpdate(physical_width, physical_height, destruction_receiver);
|
||||
if (destruction_receiver.IsWindowDestroyedOrClosed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t AndroidWindow::GetLatestDpiImpl() const {
|
||||
auto& android_app_context =
|
||||
static_cast<const AndroidWindowedAppContext&>(app_context());
|
||||
return android_app_context.GetPixelDensity();
|
||||
}
|
||||
|
||||
bool AndroidWindow::OpenImpl() {
|
||||
// The window is a proxy between the main activity and Xenia, so there can be
|
||||
// only one for an activity.
|
||||
AndroidWindowedAppContext& android_app_context =
|
||||
// only one open window for an activity.
|
||||
auto& android_app_context =
|
||||
static_cast<AndroidWindowedAppContext&>(app_context());
|
||||
AndroidWindow* previous_activity_window =
|
||||
android_app_context.GetActivityWindow();
|
||||
|
@ -50,6 +75,10 @@ bool AndroidWindow::OpenImpl() {
|
|||
return false;
|
||||
}
|
||||
android_app_context.SetActivityWindow(this);
|
||||
|
||||
// Report the initial layout.
|
||||
OnActivitySurfaceLayoutChange();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -68,7 +97,7 @@ void AndroidWindow::RequestCloseImpl() {
|
|||
}
|
||||
OnAfterClose();
|
||||
|
||||
AndroidWindowedAppContext& android_app_context =
|
||||
auto& android_app_context =
|
||||
static_cast<AndroidWindowedAppContext&>(app_context());
|
||||
if (android_app_context.GetActivityWindow() == this) {
|
||||
android_app_context.SetActivityWindow(nullptr);
|
||||
|
@ -78,13 +107,24 @@ void AndroidWindow::RequestCloseImpl() {
|
|||
std::unique_ptr<Surface> AndroidWindow::CreateSurfaceImpl(
|
||||
Surface::TypeFlags allowed_types) {
|
||||
if (allowed_types & Surface::kTypeFlag_AndroidNativeWindow) {
|
||||
// TODO(Triang3l): AndroidNativeWindowSurface for the ANativeWindow.
|
||||
auto& android_app_context =
|
||||
static_cast<const AndroidWindowedAppContext&>(app_context());
|
||||
assert_true(android_app_context.GetActivityWindow() == this);
|
||||
ANativeWindow* activity_window_surface =
|
||||
android_app_context.GetWindowSurface();
|
||||
if (activity_window_surface) {
|
||||
return std::make_unique<AndroidNativeWindowSurface>(
|
||||
activity_window_surface);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AndroidWindow::RequestPaintImpl() {
|
||||
// TODO(Triang3l): postInvalidate.
|
||||
auto& android_app_context =
|
||||
static_cast<AndroidWindowedAppContext&>(app_context());
|
||||
assert_true(android_app_context.GetActivityWindow() == this);
|
||||
android_app_context.PostInvalidateWindowSurface();
|
||||
}
|
||||
|
||||
std::unique_ptr<ui::MenuItem> MenuItem::Create(Type type,
|
||||
|
|
|
@ -29,7 +29,13 @@ class AndroidWindow : public Window {
|
|||
|
||||
uint32_t GetMediumDpi() const override { return 160; }
|
||||
|
||||
void OnActivitySurfaceLayoutChange();
|
||||
void OnActivitySurfaceChanged() { OnSurfaceChanged(true); }
|
||||
void PaintActivitySurface(bool force_paint) { OnPaint(force_paint); }
|
||||
|
||||
protected:
|
||||
uint32_t GetLatestDpiImpl() const override;
|
||||
|
||||
bool OpenImpl() override;
|
||||
void RequestCloseImpl() override;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2021 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -13,6 +13,8 @@
|
|||
#include <android/configuration.h>
|
||||
#include <android/log.h>
|
||||
#include <android/looper.h>
|
||||
#include <android/native_window.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include <fcntl.h>
|
||||
#include <jni.h>
|
||||
#include <unistd.h>
|
||||
|
@ -22,6 +24,7 @@
|
|||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/main_android.h"
|
||||
#include "xenia/ui/window_android.h"
|
||||
#include "xenia/ui/windowed_app.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -52,6 +55,16 @@ void AndroidWindowedAppContext::PlatformQuitFromUIThread() {
|
|||
}
|
||||
}
|
||||
|
||||
void AndroidWindowedAppContext::PostInvalidateWindowSurface() {
|
||||
// May be called from non-UI threads.
|
||||
JNIEnv* jni_env = GetAndroidThreadJniEnv();
|
||||
if (!jni_env) {
|
||||
return;
|
||||
}
|
||||
jni_env->CallVoidMethod(activity_,
|
||||
activity_method_post_invalidate_window_surface_);
|
||||
}
|
||||
|
||||
AndroidWindowedAppContext*
|
||||
AndroidWindowedAppContext::JniActivityInitializeWindowedAppOnCreate(
|
||||
JNIEnv* jni_env, jobject activity, jstring windowed_app_identifier,
|
||||
|
@ -100,9 +113,54 @@ void AndroidWindowedAppContext::JniActivityOnDestroy() {
|
|||
app_->InvokeOnDestroy();
|
||||
app_.reset();
|
||||
}
|
||||
// Expecting that the destruction of the app will destroy the window as well,
|
||||
// no need to notify it explicitly.
|
||||
assert_null(activity_window_);
|
||||
RequestDestruction();
|
||||
}
|
||||
|
||||
void AndroidWindowedAppContext::JniActivityOnWindowSurfaceLayoutChange(
|
||||
jint left, jint top, jint right, jint bottom) {
|
||||
window_surface_layout_left_ = left;
|
||||
window_surface_layout_top_ = top;
|
||||
window_surface_layout_right_ = right;
|
||||
window_surface_layout_bottom_ = bottom;
|
||||
if (activity_window_) {
|
||||
activity_window_->OnActivitySurfaceLayoutChange();
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidWindowedAppContext::JniActivityOnWindowSurfaceChanged(
|
||||
jobject window_surface_object) {
|
||||
// Detach from the old surface.
|
||||
if (window_surface_) {
|
||||
ANativeWindow* old_window_surface = window_surface_;
|
||||
window_surface_ = nullptr;
|
||||
if (activity_window_) {
|
||||
activity_window_->OnActivitySurfaceChanged();
|
||||
}
|
||||
ANativeWindow_release(old_window_surface);
|
||||
}
|
||||
if (!window_surface_object) {
|
||||
return;
|
||||
}
|
||||
window_surface_ =
|
||||
ANativeWindow_fromSurface(ui_thread_jni_env_, window_surface_object);
|
||||
if (!window_surface_) {
|
||||
return;
|
||||
}
|
||||
if (activity_window_) {
|
||||
activity_window_->OnActivitySurfaceChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidWindowedAppContext::JniActivityPaintWindow(bool force_paint) {
|
||||
if (!activity_window_) {
|
||||
return;
|
||||
}
|
||||
activity_window_->PaintActivitySurface(force_paint);
|
||||
}
|
||||
|
||||
AndroidWindowedAppContext::~AndroidWindowedAppContext() { Shutdown(); }
|
||||
|
||||
bool AndroidWindowedAppContext::Initialize(JNIEnv* ui_thread_jni_env,
|
||||
|
@ -205,6 +263,11 @@ bool AndroidWindowedAppContext::Initialize(JNIEnv* ui_thread_jni_env,
|
|||
activity_ids_obtained &=
|
||||
(activity_method_finish_ = ui_thread_jni_env_->GetMethodID(
|
||||
activity_class_, "finish", "()V")) != nullptr;
|
||||
activity_ids_obtained &=
|
||||
(activity_method_post_invalidate_window_surface_ =
|
||||
ui_thread_jni_env_->GetMethodID(
|
||||
activity_class_, "postInvalidateWindowSurface", "()V")) !=
|
||||
nullptr;
|
||||
if (!activity_ids_obtained) {
|
||||
XELOGE("AndroidWindowedAppContext: Failed to get the activity class IDs");
|
||||
Shutdown();
|
||||
|
@ -407,7 +470,7 @@ bool AndroidWindowedAppContext::InitializeApp(std::unique_ptr<WindowedApp> (
|
|||
extern "C" {
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_jp_xenia_emulator_WindowedAppActivity_initializeWindowedAppOnCreateNative(
|
||||
Java_jp_xenia_emulator_WindowedAppActivity_initializeWindowedAppOnCreate(
|
||||
JNIEnv* jni_env, jobject activity, jstring windowed_app_identifier,
|
||||
jobject asset_manager) {
|
||||
return reinterpret_cast<jlong>(
|
||||
|
@ -423,4 +486,27 @@ Java_jp_xenia_emulator_WindowedAppActivity_onDestroyNative(
|
|||
->JniActivityOnDestroy();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_jp_xenia_emulator_WindowedAppActivity_onWindowSurfaceLayoutChange(
|
||||
JNIEnv* jni_env, jobject activity, jlong app_context_ptr, jint left,
|
||||
jint top, jint right, jint bottom) {
|
||||
reinterpret_cast<xe::ui::AndroidWindowedAppContext*>(app_context_ptr)
|
||||
->JniActivityOnWindowSurfaceLayoutChange(left, top, right, bottom);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_jp_xenia_emulator_WindowedAppActivity_onWindowSurfaceChanged(
|
||||
JNIEnv* jni_env, jobject activity, jlong app_context_ptr,
|
||||
jobject window_surface_object) {
|
||||
reinterpret_cast<xe::ui::AndroidWindowedAppContext*>(app_context_ptr)
|
||||
->JniActivityOnWindowSurfaceChanged(window_surface_object);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_jp_xenia_emulator_WindowedAppActivity_paintWindow(
|
||||
JNIEnv* jni_env, jobject activity, jlong app_context_ptr,
|
||||
jboolean force_paint) {
|
||||
reinterpret_cast<xe::ui::AndroidWindowedAppContext*>(app_context_ptr)
|
||||
->JniActivityPaintWindow(bool(force_paint));
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2021 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
@ -13,6 +13,7 @@
|
|||
#include <android/asset_manager.h>
|
||||
#include <android/configuration.h>
|
||||
#include <android/looper.h>
|
||||
#include <android/native_window.h>
|
||||
#include <jni.h>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
@ -33,6 +34,27 @@ class AndroidWindowedAppContext final : public WindowedAppContext {
|
|||
|
||||
void PlatformQuitFromUIThread() override;
|
||||
|
||||
uint32_t GetPixelDensity() const {
|
||||
return configuration_ ? uint32_t(AConfiguration_getDensity(configuration_))
|
||||
: 160;
|
||||
}
|
||||
|
||||
int32_t window_surface_layout_left() const {
|
||||
return window_surface_layout_left_;
|
||||
}
|
||||
int32_t window_surface_layout_top() const {
|
||||
return window_surface_layout_top_;
|
||||
}
|
||||
int32_t window_surface_layout_right() const {
|
||||
return window_surface_layout_right_;
|
||||
}
|
||||
int32_t window_surface_layout_bottom() const {
|
||||
return window_surface_layout_bottom_;
|
||||
}
|
||||
|
||||
ANativeWindow* GetWindowSurface() const { return window_surface_; }
|
||||
void PostInvalidateWindowSurface();
|
||||
|
||||
// The single Window instance that will be receiving window callbacks.
|
||||
// Multiple windows cannot be created as one activity or fragment can have
|
||||
// only one layout. This window acts purely as a proxy between the activity
|
||||
|
@ -46,6 +68,10 @@ class AndroidWindowedAppContext final : public WindowedAppContext {
|
|||
JNIEnv* jni_env, jobject activity, jstring windowed_app_identifier,
|
||||
jobject asset_manager);
|
||||
void JniActivityOnDestroy();
|
||||
void JniActivityOnWindowSurfaceLayoutChange(jint left, jint top, jint right,
|
||||
jint bottom);
|
||||
void JniActivityOnWindowSurfaceChanged(jobject window_surface_object);
|
||||
void JniActivityPaintWindow(bool force_paint);
|
||||
|
||||
private:
|
||||
enum class UIThreadLooperCallbackCommand : uint8_t {
|
||||
|
@ -93,6 +119,7 @@ class AndroidWindowedAppContext final : public WindowedAppContext {
|
|||
|
||||
jobject activity_ = nullptr;
|
||||
jmethodID activity_method_finish_ = nullptr;
|
||||
jmethodID activity_method_post_invalidate_window_surface_ = nullptr;
|
||||
|
||||
// May be read by non-UI threads in NotifyUILoopOfPendingFunctions.
|
||||
ALooper* ui_thread_looper_ = nullptr;
|
||||
|
@ -101,6 +128,13 @@ class AndroidWindowedAppContext final : public WindowedAppContext {
|
|||
std::array<int, 2> ui_thread_looper_callback_pipe_{-1, -1};
|
||||
bool ui_thread_looper_callback_registered_ = false;
|
||||
|
||||
int32_t window_surface_layout_left_ = 0;
|
||||
int32_t window_surface_layout_top_ = 0;
|
||||
int32_t window_surface_layout_right_ = 0;
|
||||
int32_t window_surface_layout_bottom_ = 0;
|
||||
|
||||
ANativeWindow* window_surface_ = nullptr;
|
||||
|
||||
AndroidWindow* activity_window_ = nullptr;
|
||||
|
||||
std::unique_ptr<WindowedApp> app_;
|
||||
|
|
Loading…
Reference in New Issue