Display: add safeArea property

display.safeArea is a subset of display.bounds that accounts for notches, holes, or other display cutouts.
This commit is contained in:
Josh Tynjala
2025-01-30 15:55:47 -08:00
parent 70efb2a4ec
commit af2299670f
8 changed files with 144 additions and 15 deletions

View File

@@ -315,6 +315,8 @@
<file name="src/system/CFFI.cpp" /> <file name="src/system/CFFI.cpp" />
<file name="src/system/CFFIPointer.cpp" /> <file name="src/system/CFFIPointer.cpp" />
<file name="src/system/ClipboardEvent.cpp" /> <file name="src/system/ClipboardEvent.cpp" />
<file name="src/system/Display.mm" if="mac || ios" />
<file name="src/system/Display.cpp" unless="mac || ios" />
<file name="src/system/DisplayMode.cpp" /> <file name="src/system/DisplayMode.cpp" />
<file name="src/system/JNI.cpp" if="android" /> <file name="src/system/JNI.cpp" if="android" />
<file name="src/system/Locale.cpp" unless="mac || ios" /> <file name="src/system/Locale.cpp" unless="mac || ios" />

View File

@@ -0,0 +1,21 @@
#ifndef LIME_SYSTEM_DISPLAY_H
#define LIME_SYSTEM_DISPLAY_H
#include <math/Rectangle.h>
namespace lime {
class Display {
public:
static void GetSafeAreaInsets (int displayIndex, Rectangle * rect);
};
}
#endif

View File

@@ -1,6 +1,7 @@
#include <graphics/PixelFormat.h> #include <graphics/PixelFormat.h>
#include <math/Rectangle.h> #include <math/Rectangle.h>
#include <system/Clipboard.h> #include <system/Clipboard.h>
#include <system/Display.h>
#include <system/DisplayMode.h> #include <system/DisplayMode.h>
#include <system/JNI.h> #include <system/JNI.h>
#include <system/System.h> #include <system/System.h>
@@ -57,6 +58,7 @@ namespace lime {
static int id_refreshRate; static int id_refreshRate;
static int id_supportedModes; static int id_supportedModes;
static int id_width; static int id_width;
static int id_safeArea;
static bool init = false; static bool init = false;
@@ -312,6 +314,7 @@ namespace lime {
id_refreshRate = val_id ("refreshRate"); id_refreshRate = val_id ("refreshRate");
id_supportedModes = val_id ("supportedModes"); id_supportedModes = val_id ("supportedModes");
id_width = val_id ("width"); id_width = val_id ("width");
id_safeArea = val_id ("safeArea");
init = true; init = true;
} }
@@ -331,6 +334,14 @@ namespace lime {
SDL_GetDisplayBounds (id, &bounds); SDL_GetDisplayBounds (id, &bounds);
alloc_field (display, id_bounds, Rectangle (bounds.x, bounds.y, bounds.w, bounds.h).Value ()); alloc_field (display, id_bounds, Rectangle (bounds.x, bounds.y, bounds.w, bounds.h).Value ());
Rectangle safeAreaInsets;
Display::GetSafeAreaInsets(id, &safeAreaInsets);
alloc_field (display, id_safeArea,
Rectangle (bounds.x + safeAreaInsets.x,
bounds.y + safeAreaInsets.y,
bounds.w - safeAreaInsets.x - safeAreaInsets.width,
bounds.h - safeAreaInsets.y - safeAreaInsets.height).Value ());
float dpi = 72.0; float dpi = 72.0;
#ifndef EMSCRIPTEN #ifndef EMSCRIPTEN
SDL_GetDisplayDPI (id, &dpi, NULL, NULL); SDL_GetDisplayDPI (id, &dpi, NULL, NULL);
@@ -421,6 +432,7 @@ namespace lime {
const int id_refreshRate = hl_hash_utf8 ("refreshRate"); const int id_refreshRate = hl_hash_utf8 ("refreshRate");
const int id_supportedModes = hl_hash_utf8 ("supportedModes"); const int id_supportedModes = hl_hash_utf8 ("supportedModes");
const int id_width = hl_hash_utf8 ("width"); const int id_width = hl_hash_utf8 ("width");
const int id_safeArea = hl_hash_utf8 ("safeArea");
const int id_x = hl_hash_utf8 ("x"); const int id_x = hl_hash_utf8 ("x");
const int id_y = hl_hash_utf8 ("y"); const int id_y = hl_hash_utf8 ("y");
@@ -450,6 +462,16 @@ namespace lime {
hl_dyn_setp (display, id_bounds, &hlt_dynobj, _bounds); hl_dyn_setp (display, id_bounds, &hlt_dynobj, _bounds);
Rectangle safeAreaInsets;
Display::GetSafeAreaInsets(id, &safeAreaInsets);
vdynamic* _safeArea = (vdynamic*)hl_alloc_dynobj ();
hl_dyn_seti (_safeArea, id_x, &hlt_i32, bounds.x + safeAreaInsets.x);
hl_dyn_seti (_safeArea, id_y, &hlt_i32, bounds.y + safeAreaInsets.y);
hl_dyn_seti (_safeArea, id_width, &hlt_i32, bounds.w - safeAreaInsets.x - safeAreaInsets.width);
hl_dyn_seti (_safeArea, id_height, &hlt_i32, bounds.h - safeAreaInsets.y - safeAreaInsets.height);
hl_dyn_setp (display, id_safeArea, &hlt_dynobj, _safeArea);
float dpi = 72.0; float dpi = 72.0;
#ifndef EMSCRIPTEN #ifndef EMSCRIPTEN
SDL_GetDisplayDPI (id, &dpi, NULL, NULL); SDL_GetDisplayDPI (id, &dpi, NULL, NULL);

View File

@@ -0,0 +1,13 @@
#include <system/Display.h>
#include <math/Rectangle.h>
namespace lime {
void Display::GetSafeAreaInsets (int displayIndex, Rectangle * rect) {
rect->SetTo(0.0, 0.0, 0.0, 0.0);
}
}

View File

@@ -4,35 +4,49 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#endif #endif
#include <system/Locale.h> #include <system/Display.h>
#include <math/Rectangle.h>
namespace lime { namespace lime {
float Display::GetDPI () { void Display::GetSafeAreaInsets (int displayIndex, Rectangle * rect) {
#ifndef OBJC_ARC #ifdef HX_MACOS
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
#endif
NSString* locale = [[NSLocale currentLocale] localeIdentifier]; if (@available(macOS 12, *)) {
std::string* result = 0;
if (locale) { NSScreen * screen = [[NSScreen screens] objectAtIndex: displayIndex];
NSEdgeInsets safeAreaInsets = [screen safeAreaInsets];
const char* ptr = [locale UTF8String]; rect->SetTo(safeAreaInsets.left,
result = new std::string (ptr); safeAreaInsets.top,
safeAreaInsets.right,
safeAreaInsets.bottom);
return;
} }
#ifndef OBJC_ARC
[pool drain];
#endif #endif
return result; #ifdef IPHONE
if (@available(iOS 11, *)) {
UIWindow * window = [[UIApplication sharedApplication] keyWindow];
UIEdgeInsets safeAreaInsets = [window safeAreaInsets];
rect->SetTo(safeAreaInsets.left,
safeAreaInsets.top,
safeAreaInsets.right,
safeAreaInsets.bottom);
return;
}
#endif
rect->SetTo(0.0, 0.0, 0.0, 0.0);
} }
} }

View File

@@ -40,5 +40,12 @@ class Display
**/ **/
public var supportedModes(default, null):Array<DisplayMode>; public var supportedModes(default, null):Array<DisplayMode>;
/**
The area within the display's `bounds` where it is safe to render
content without being obscured by notches, holes, or other display
cutouts.
**/
public var safeArea(default, null):Rectangle;
@:noCompletion private function new() {} @:noCompletion private function new() {}
} }

View File

@@ -248,6 +248,22 @@ class System
display.bounds = new Rectangle(displayInfo.bounds.x, displayInfo.bounds.y, displayInfo.bounds.width, displayInfo.bounds.height); display.bounds = new Rectangle(displayInfo.bounds.x, displayInfo.bounds.y, displayInfo.bounds.width, displayInfo.bounds.height);
display.orientation = displayInfo.orientation; display.orientation = displayInfo.orientation;
#if android
var getDisplaySafeArea = JNI.createStaticMethod("org/haxe/lime/GameActivity", "getDisplaySafeAreaInsets", "()[I");
var result = getDisplaySafeArea();
display.safeArea = new Rectangle(
display.bounds.x + result[0],
display.bounds.y + result[1],
display.bounds.width - result[0] - result[2],
display.bounds.height - result[1] - result[3]);
#else
display.safeArea = new Rectangle(
displayInfo.safeArea.x,
displayInfo.safeArea.y,
displayInfo.safeArea.width,
displayInfo.safeArea.height);
#end
#if ios #if ios
var tablet = NativeCFFI.lime_system_get_ios_tablet(); var tablet = NativeCFFI.lime_system_get_ios_tablet();
var scale = Application.current.window.scale; var scale = Application.current.window.scale;

View File

@@ -11,12 +11,14 @@ import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.VibrationEffect; import android.os.VibrationEffect;
import android.os.Vibrator; import android.os.Vibrator;
import android.view.DisplayCutout;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.view.KeyCharacterMap; import android.view.KeyCharacterMap;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.OrientationEventListener; import android.view.OrientationEventListener;
import android.view.View; import android.view.View;
import android.view.WindowInsets;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import android.Manifest; import android.Manifest;
import org.haxe.extension.Extension; import org.haxe.extension.Extension;
@@ -33,6 +35,7 @@ public class GameActivity extends SDLActivity {
private static AssetManager assetManager; private static AssetManager assetManager;
private static List<Extension> extensions; private static List<Extension> extensions;
private static DisplayMetrics metrics; private static DisplayMetrics metrics;
private static DisplayCutout displayCutout;
private static Vibrator vibrator; private static Vibrator vibrator;
private static OrientationEventListener orientationListener; private static OrientationEventListener orientationListener;
private static HaxeObject deviceOrientationListener; private static HaxeObject deviceOrientationListener;
@@ -62,6 +65,37 @@ public class GameActivity extends SDLActivity {
} }
public static int[] getDisplaySafeAreaInsets () {
if (displayCutout == null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
WindowInsets windowInsets = ((GameActivity)Extension.mainContext).getWindow().getDecorView().getRootWindowInsets();
if (windowInsets != null) {
displayCutout = windowInsets.getDisplayCutout();
}
}
}
int[] result = {0, 0, 0, 0};
if (displayCutout != null) {
result[0] = displayCutout.getSafeInsetLeft();
result[1] = displayCutout.getSafeInsetTop();
result[2] = displayCutout.getSafeInsetRight();
result[3] = displayCutout.getSafeInsetBottom();
}
return result;
}
protected String[] getLibraries () { protected String[] getLibraries () {