mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
parent
4a10702f6c
commit
9a8d6e7b41
@ -18,15 +18,19 @@ package ui
|
||||
#include <jni.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Basically same as:
|
||||
// The following JNI code works as this pseudo Java code:
|
||||
//
|
||||
// WindowService windowService = context.getSystemService(Context.WINDOW_SERVICE);
|
||||
// Display display = windowManager.getDefaultDisplay();
|
||||
// DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
// display.getRealMetrics(displayMetrics);
|
||||
// this.deviceScale = displayMetrics.density;
|
||||
// return displayMetrics.widthPixels, displayMetrics.heightPixels, displayMetrics.density;
|
||||
//
|
||||
static float deviceScale(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx) {
|
||||
static void displayInfo(int* width, int* height, float* scale, uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx) {
|
||||
*width = 0;
|
||||
*height = 0;
|
||||
*scale = 1;
|
||||
|
||||
JavaVM* vm = (JavaVM*)java_vm;
|
||||
JNIEnv* env = (JNIEnv*)jni_env;
|
||||
jobject context = (jobject)ctx;
|
||||
@ -64,7 +68,15 @@ static float deviceScale(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx) {
|
||||
env, display,
|
||||
(*env)->GetMethodID(env, android_view_Display, "getRealMetrics", "(Landroid/util/DisplayMetrics;)V"),
|
||||
displayMetrics);
|
||||
const float density =
|
||||
*width =
|
||||
(*env)->GetIntField(
|
||||
env, displayMetrics,
|
||||
(*env)->GetFieldID(env, android_util_DisplayMetrics, "widthPixels", "I"));
|
||||
*height =
|
||||
(*env)->GetIntField(
|
||||
env, displayMetrics,
|
||||
(*env)->GetFieldID(env, android_util_DisplayMetrics, "heightPixels", "I"));
|
||||
*scale =
|
||||
(*env)->GetFloatField(
|
||||
env, displayMetrics,
|
||||
(*env)->GetFieldID(env, android_util_DisplayMetrics, "density", "F"));
|
||||
@ -78,15 +90,12 @@ static float deviceScale(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx) {
|
||||
(*env)->DeleteLocalRef(env, windowManager);
|
||||
(*env)->DeleteLocalRef(env, display);
|
||||
(*env)->DeleteLocalRef(env, displayMetrics);
|
||||
|
||||
return density;
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ebitengine/gomobile/app"
|
||||
|
||||
@ -119,18 +128,27 @@ func (*graphicsDriverCreatorImpl) newPlayStation5() (graphicsdriver.Graphics, er
|
||||
return nil, errors.New("ui: PlayStation 5 is not supported in this environment")
|
||||
}
|
||||
|
||||
func (u *UserInterface) deviceScaleFactor() float64 {
|
||||
var s float64
|
||||
if err := app.RunOnJVM(func(vm, env, ctx uintptr) error {
|
||||
// TODO: This might be crash when this is called from init(). How can we detect this?
|
||||
s = float64(C.deviceScale(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx)))
|
||||
return nil
|
||||
}); err != nil {
|
||||
panic(fmt.Sprintf("devicescale: error %v", err))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func dipToNativePixels(x float64, scale float64) float64 {
|
||||
return x * scale
|
||||
}
|
||||
|
||||
func dipFromNativePixels(x float64, scale float64) float64 {
|
||||
return x / scale
|
||||
}
|
||||
|
||||
func (u *UserInterface) displayInfo() (int, int, float64, bool) {
|
||||
var cWidth, cHeight C.int
|
||||
var cScale C.float
|
||||
if err := app.RunOnJVM(func(vm, env, ctx uintptr) error {
|
||||
C.displayInfo(&cWidth, &cHeight, &cScale, C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx))
|
||||
return nil
|
||||
}); err != nil {
|
||||
// JVM is not ready yet.
|
||||
// TODO: Fix gomobile to detect the error type for this case.
|
||||
return 0, 0, 1, false
|
||||
}
|
||||
scale := float64(cScale)
|
||||
width := int(dipFromNativePixels(float64(cWidth), scale))
|
||||
height := int(dipFromNativePixels(float64(cHeight), scale))
|
||||
return width, height, scale, true
|
||||
}
|
||||
|
@ -19,31 +19,43 @@ package ui
|
||||
//
|
||||
// #import <UIKit/UIKit.h>
|
||||
//
|
||||
// static double devicePixelRatioOnMainThread(UIView* view) {
|
||||
// static void displayInfoOnMainThread(float* width, float* height, float* scale, UIView* view) {
|
||||
// *width = 0;
|
||||
// *height = 0;
|
||||
// *scale = 1;
|
||||
// UIWindow* window = view.window;
|
||||
// if (!window) {
|
||||
// return 1;
|
||||
// return;
|
||||
// }
|
||||
// UIWindowScene* scene = window.windowScene;
|
||||
// if (!scene) {
|
||||
// return 1;
|
||||
// return;
|
||||
// }
|
||||
// return scene.screen.nativeScale;
|
||||
// CGRect bounds = scene.screen.bounds;
|
||||
// *width = bounds.size.width;
|
||||
// *height = bounds.size.height;
|
||||
// *scale = scene.screen.nativeScale;
|
||||
// }
|
||||
//
|
||||
// static double devicePixelRatio(uintptr_t viewPtr) {
|
||||
// static void displayInfo(float* width, float* height, float* scale, uintptr_t viewPtr) {
|
||||
// *width = 0;
|
||||
// *height = 0;
|
||||
// *scale = 1;
|
||||
// if (!viewPtr) {
|
||||
// return 1;
|
||||
// return;
|
||||
// }
|
||||
// UIView* view = (__bridge UIView*)(void*)viewPtr;
|
||||
// if ([NSThread isMainThread]) {
|
||||
// return devicePixelRatioOnMainThread(view);
|
||||
// displayInfoOnMainThread(width, height, scale, view);
|
||||
// return;
|
||||
// }
|
||||
// __block double scale;
|
||||
// __block float w, h, s;
|
||||
// dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
// scale = devicePixelRatioOnMainThread(view);
|
||||
// displayInfoOnMainThread(&w, &h, &s, view);
|
||||
// });
|
||||
// return scale;
|
||||
// *width = w;
|
||||
// *height = h;
|
||||
// *scale = s;
|
||||
// }
|
||||
import "C"
|
||||
|
||||
@ -113,10 +125,24 @@ func (u *UserInterface) IsGL() (bool, error) {
|
||||
return u.GraphicsLibrary() == GraphicsLibraryOpenGL, nil
|
||||
}
|
||||
|
||||
func (u *UserInterface) deviceScaleFactor() float64 {
|
||||
return float64(C.devicePixelRatio(C.uintptr_t(u.uiView.Load())))
|
||||
}
|
||||
|
||||
func dipToNativePixels(x float64, scale float64) float64 {
|
||||
return x
|
||||
}
|
||||
|
||||
func dipFromNativePixels(x float64, scale float64) float64 {
|
||||
return x
|
||||
}
|
||||
|
||||
func (u *UserInterface) displayInfo() (int, int, float64, bool) {
|
||||
view := u.uiView.Load()
|
||||
if view == 0 {
|
||||
return 0, 0, 1, false
|
||||
}
|
||||
|
||||
var cWidth, cHeight, cScale C.float
|
||||
C.displayInfo(&cWidth, &cHeight, &cScale, C.uintptr_t(view))
|
||||
scale := float64(cScale)
|
||||
width := int(dipFromNativePixels(float64(cWidth), scale))
|
||||
height := int(dipFromNativePixels(float64(cHeight), scale))
|
||||
return width, height, scale, true
|
||||
}
|
||||
|
@ -268,8 +268,10 @@ func (u *UserInterface) Window() Window {
|
||||
}
|
||||
|
||||
type Monitor struct {
|
||||
width int
|
||||
height int
|
||||
deviceScaleFactor float64
|
||||
deviceScaleFactorOnce sync.Once
|
||||
inited atomic.Bool
|
||||
|
||||
m sync.Mutex
|
||||
}
|
||||
@ -280,22 +282,35 @@ func (m *Monitor) Name() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Monitor) DeviceScaleFactor() float64 {
|
||||
func (m *Monitor) ensureInit() {
|
||||
if m.inited.Load() {
|
||||
return
|
||||
}
|
||||
|
||||
m.m.Lock()
|
||||
defer m.m.Unlock()
|
||||
// Re-check the state since the state might be changed while locking.
|
||||
if m.inited.Load() {
|
||||
return
|
||||
}
|
||||
width, height, scale, ok := theUI.displayInfo()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
m.width = width
|
||||
m.height = height
|
||||
m.deviceScaleFactor = scale
|
||||
m.inited.Store(true)
|
||||
}
|
||||
|
||||
// The device scale factor can be obtained after the main function starts, especially on Android.
|
||||
// Initialize this lazily.
|
||||
m.deviceScaleFactorOnce.Do(func() {
|
||||
// Assume that the device scale factor never changes on mobiles.
|
||||
m.deviceScaleFactor = theUI.deviceScaleFactor()
|
||||
})
|
||||
func (m *Monitor) DeviceScaleFactor() float64 {
|
||||
m.ensureInit()
|
||||
return m.deviceScaleFactor
|
||||
}
|
||||
|
||||
func (m *Monitor) Size() (int, int) {
|
||||
// TODO: Return a valid value.
|
||||
return 0, 0
|
||||
m.ensureInit()
|
||||
return m.width, m.height
|
||||
}
|
||||
|
||||
func (u *UserInterface) AppendMonitors(mons []*Monitor) []*Monitor {
|
||||
|
@ -32,8 +32,7 @@ func (m *MonitorType) Name() string {
|
||||
// DeviceScaleFactor returns a meaningful value on high-DPI display environment,
|
||||
// otherwise DeviceScaleFactor returns 1.
|
||||
//
|
||||
// DeviceScaleFactor might panic on init function on some devices like Android.
|
||||
// Then, it is not recommended to call DeviceScaleFactor from init functions.
|
||||
// On mobiles, DeviceScaleFactor returns 1 before the game starts e.g. in init functions.
|
||||
func (m *MonitorType) DeviceScaleFactor() float64 {
|
||||
return (*ui.Monitor)(m).DeviceScaleFactor()
|
||||
}
|
||||
@ -42,7 +41,7 @@ func (m *MonitorType) DeviceScaleFactor() float64 {
|
||||
// This is the same as the screen size in fullscreen mode.
|
||||
// The returned value can be given to SetSize function if the perfectly fit fullscreen is needed.
|
||||
//
|
||||
// On mobiles, Size returns (0, 0) so far.
|
||||
// On mobiles, Size returns (0, 0) before the game starts e.g. in init functions.
|
||||
//
|
||||
// Size's use cases are limited. If you are making a fullscreen application, you can use RunGame and
|
||||
// the Game interface's Layout function instead. If you are making a not-fullscreen application but the application's
|
||||
|
Loading…
Reference in New Issue
Block a user