From af2299670ff219885d16a5c2f8eed9d1bd5d9c03 Mon Sep 17 00:00:00 2001 From: Josh Tynjala Date: Thu, 30 Jan 2025 15:55:47 -0800 Subject: [PATCH] Display: add safeArea property display.safeArea is a subset of display.bounds that accounts for notches, holes, or other display cutouts. --- project/Build.xml | 2 + project/include/system/Display.h | 21 +++++++++ project/src/backend/sdl/SDLSystem.cpp | 22 ++++++++++ project/src/system/Display.cpp | 13 ++++++ project/src/system/Display.mm | 44 ++++++++++++------- src/lime/system/Display.hx | 7 +++ src/lime/system/System.hx | 16 +++++++ .../main/java/org/haxe/lime/GameActivity.java | 34 ++++++++++++++ 8 files changed, 144 insertions(+), 15 deletions(-) create mode 100644 project/include/system/Display.h create mode 100644 project/src/system/Display.cpp diff --git a/project/Build.xml b/project/Build.xml index 72301b0fa..be06ee766 100755 --- a/project/Build.xml +++ b/project/Build.xml @@ -315,6 +315,8 @@ + + diff --git a/project/include/system/Display.h b/project/include/system/Display.h new file mode 100644 index 000000000..8d04580f0 --- /dev/null +++ b/project/include/system/Display.h @@ -0,0 +1,21 @@ +#ifndef LIME_SYSTEM_DISPLAY_H +#define LIME_SYSTEM_DISPLAY_H + +#include + +namespace lime { + + + class Display { + + + public: + + static void GetSafeAreaInsets (int displayIndex, Rectangle * rect); + + + }; + +} + +#endif \ No newline at end of file diff --git a/project/src/backend/sdl/SDLSystem.cpp b/project/src/backend/sdl/SDLSystem.cpp index 4c5fe09a1..77031cf8f 100644 --- a/project/src/backend/sdl/SDLSystem.cpp +++ b/project/src/backend/sdl/SDLSystem.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ namespace lime { static int id_refreshRate; static int id_supportedModes; static int id_width; + static int id_safeArea; static bool init = false; @@ -312,6 +314,7 @@ namespace lime { id_refreshRate = val_id ("refreshRate"); id_supportedModes = val_id ("supportedModes"); id_width = val_id ("width"); + id_safeArea = val_id ("safeArea"); init = true; } @@ -331,6 +334,14 @@ namespace lime { SDL_GetDisplayBounds (id, &bounds); 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; #ifndef EMSCRIPTEN SDL_GetDisplayDPI (id, &dpi, NULL, NULL); @@ -421,6 +432,7 @@ namespace lime { const int id_refreshRate = hl_hash_utf8 ("refreshRate"); const int id_supportedModes = hl_hash_utf8 ("supportedModes"); 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_y = hl_hash_utf8 ("y"); @@ -450,6 +462,16 @@ namespace lime { 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; #ifndef EMSCRIPTEN SDL_GetDisplayDPI (id, &dpi, NULL, NULL); diff --git a/project/src/system/Display.cpp b/project/src/system/Display.cpp new file mode 100644 index 000000000..81c9fcd4c --- /dev/null +++ b/project/src/system/Display.cpp @@ -0,0 +1,13 @@ +#include +#include + + +namespace lime { + + + void Display::GetSafeAreaInsets (int displayIndex, Rectangle * rect) { + + rect->SetTo(0.0, 0.0, 0.0, 0.0); + } + +} \ No newline at end of file diff --git a/project/src/system/Display.mm b/project/src/system/Display.mm index 0a6bfc335..21a1660f2 100644 --- a/project/src/system/Display.mm +++ b/project/src/system/Display.mm @@ -4,35 +4,49 @@ #import #endif -#include +#include +#include namespace lime { - float Display::GetDPI () { + void Display::GetSafeAreaInsets (int displayIndex, Rectangle * rect) { - #ifndef OBJC_ARC - NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - #endif + #ifdef HX_MACOS - NSString* locale = [[NSLocale currentLocale] localeIdentifier]; - std::string* result = 0; + if (@available(macOS 12, *)) { - if (locale) { - - const char* ptr = [locale UTF8String]; - result = new std::string (ptr); + NSScreen * screen = [[NSScreen screens] objectAtIndex: displayIndex]; + NSEdgeInsets safeAreaInsets = [screen safeAreaInsets]; + rect->SetTo(safeAreaInsets.left, + safeAreaInsets.top, + safeAreaInsets.right, + safeAreaInsets.bottom); + return; } - #ifndef OBJC_ARC - [pool drain]; #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); } - } \ No newline at end of file diff --git a/src/lime/system/Display.hx b/src/lime/system/Display.hx index 660f0eb89..79942c389 100644 --- a/src/lime/system/Display.hx +++ b/src/lime/system/Display.hx @@ -40,5 +40,12 @@ class Display **/ public var supportedModes(default, null):Array; + /** + 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() {} } diff --git a/src/lime/system/System.hx b/src/lime/system/System.hx index 8737e1f40..ab3a1042d 100644 --- a/src/lime/system/System.hx +++ b/src/lime/system/System.hx @@ -248,6 +248,22 @@ class System display.bounds = new Rectangle(displayInfo.bounds.x, displayInfo.bounds.y, displayInfo.bounds.width, displayInfo.bounds.height); 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 var tablet = NativeCFFI.lime_system_get_ios_tablet(); var scale = Application.current.window.scale; diff --git a/templates/android/template/app/src/main/java/org/haxe/lime/GameActivity.java b/templates/android/template/app/src/main/java/org/haxe/lime/GameActivity.java index 6f0d3bf11..7f7939832 100644 --- a/templates/android/template/app/src/main/java/org/haxe/lime/GameActivity.java +++ b/templates/android/template/app/src/main/java/org/haxe/lime/GameActivity.java @@ -11,12 +11,14 @@ import android.os.Bundle; import android.os.Handler; import android.os.VibrationEffect; import android.os.Vibrator; +import android.view.DisplayCutout; import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.OrientationEventListener; import android.view.View; +import android.view.WindowInsets; import android.webkit.MimeTypeMap; import android.Manifest; import org.haxe.extension.Extension; @@ -33,6 +35,7 @@ public class GameActivity extends SDLActivity { private static AssetManager assetManager; private static List extensions; private static DisplayMetrics metrics; + private static DisplayCutout displayCutout; private static Vibrator vibrator; private static OrientationEventListener orientationListener; 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 () {