Added SDL_GetWindowSafeArea()

Fixes https://github.com/libsdl-org/SDL/issues/3243
This commit is contained in:
Sam Lantinga 2024-07-22 18:59:53 -07:00
parent 1c4cc2b024
commit 457ca3995c
16 changed files with 147 additions and 1 deletions

View File

@ -918,6 +918,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
SDLActivity.mFullscreenModeActive = false;
}
window.getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
}
} else {
Log.e(TAG, "error handling message, getContext() returned no Activity");
@ -1057,6 +1058,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
public static native void nativeSetenv(String name, String value);
public static native void nativeSetNaturalOrientation(int orientation);
public static native void onNativeRotationChanged(int rotation);
public static native void onNativeInsetsChanged(int left, int right, int top, int bottom);
public static native void nativeAddTouch(int touchId, String name);
public static native void nativePermissionResult(int requestCode, boolean result);
public static native void onNativeLocaleChanged();

View File

@ -3,6 +3,7 @@ package org.libsdl.app;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.graphics.Insets;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@ -18,6 +19,7 @@ import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
@ -28,7 +30,7 @@ import android.view.WindowManager;
Because of this, that's where we set up the SDL thread
*/
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
View.OnApplyWindowInsetsListener, View.OnKeyListener, View.OnTouchListener, SensorEventListener {
// Sensors
protected SensorManager mSensorManager;
@ -48,6 +50,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnApplyWindowInsetsListener(this);
setOnKeyListener(this);
setOnTouchListener(this);
@ -71,6 +74,7 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnApplyWindowInsetsListener(this);
setOnKeyListener(this);
setOnTouchListener(this);
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
@ -187,6 +191,24 @@ public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
SDLActivity.handleNativeState();
}
// Window inset
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
Insets combined = insets.getInsets(WindowInsets.Type.statusBars() |
WindowInsets.Type.navigationBars() |
WindowInsets.Type.captionBar() |
WindowInsets.Type.systemGestures() |
WindowInsets.Type.mandatorySystemGestures() |
WindowInsets.Type.tappableElement() |
WindowInsets.Type.displayCutout() |
WindowInsets.Type.systemOverlays());
SDLActivity.onNativeInsetsChanged(combined.left, combined.right, combined.top, combined.bottom);
// Pass these to any child views in case they need them
return insets;
}
// Key events
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {

View File

@ -142,6 +142,7 @@ typedef enum SDL_EventType
SDL_EVENT_WINDOW_ICCPROF_CHANGED, /**< The ICC profile of the window's display has changed */
SDL_EVENT_WINDOW_DISPLAY_CHANGED, /**< Window has been moved to display data1 */
SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED, /**< Window display scale has been changed */
SDL_EVENT_WINDOW_SAFE_AREA_CHANGED, /**< The window safe area has been changed */
SDL_EVENT_WINDOW_OCCLUDED, /**< The window has been occluded */
SDL_EVENT_WINDOW_ENTER_FULLSCREEN, /**< The window has entered fullscreen mode */
SDL_EVENT_WINDOW_LEAVE_FULLSCREEN, /**< The window has left fullscreen mode */

View File

@ -1480,6 +1480,20 @@ extern SDL_DECLSPEC int SDLCALL SDL_SetWindowSize(SDL_Window *window, int w, int
*/
extern SDL_DECLSPEC int SDLCALL SDL_GetWindowSize(SDL_Window *window, int *w, int *h);
/**
* Get the safe area for this window.
*
* Some devices have portions of the screen which are partially obscured or not interactive, possibly due to on-screen controls, curved edges, camera notches, TV overscan, etc. This function provides the area of the window which is safe to have interactible content. You should continue rendering into the rest of the window, but it should not contain visually important or interactible content.
*
* \param window the window to query.
* \param rect a pointer filled in with the client area that is safe for interactive content.
* \returns 0 on success or a negative error code on failure; call
* SDL_GetError() for more information.
*
* \since This function is available since SDL 3.0.0.
*/
extern SDL_DECLSPEC int SDLCALL SDL_GetWindowSafeArea(SDL_Window *window, SDL_Rect *rect);
/**
* Request that the aspect ratio of a window's client area be set.
*

View File

@ -163,6 +163,10 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeRotationChanged)(
JNIEnv *env, jclass cls,
jint rotation);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeInsetsChanged)(
JNIEnv *env, jclass cls,
jint left, jint right, jint top, jint bottom);
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)(
JNIEnv *env, jclass cls,
jint touchId, jstring name);
@ -212,6 +216,7 @@ static JNINativeMethod SDLActivity_tab[] = {
{ "nativeSetenv", "(Ljava/lang/String;Ljava/lang/String;)V", SDL_JAVA_INTERFACE(nativeSetenv) },
{ "nativeSetNaturalOrientation", "(I)V", SDL_JAVA_INTERFACE(nativeSetNaturalOrientation) },
{ "onNativeRotationChanged", "(I)V", SDL_JAVA_INTERFACE(onNativeRotationChanged) },
{ "onNativeInsetsChanged", "(IIII)V", SDL_JAVA_INTERFACE(onNativeInsetsChanged) },
{ "nativeAddTouch", "(ILjava/lang/String;)V", SDL_JAVA_INTERFACE(nativeAddTouch) },
{ "nativePermissionResult", "(IZ)V", SDL_JAVA_INTERFACE(nativePermissionResult) },
{ "nativeAllowRecreateActivity", "()Z", SDL_JAVA_INTERFACE(nativeAllowRecreateActivity) },
@ -997,6 +1002,19 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeRotationChanged)(
SDL_UnlockMutex(Android_ActivityMutex);
}
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(onNativeInsetsChanged)(
JNIEnv *env, jclass jcls,
jint left, jint right, jint top, jint bottom)
{
SDL_LockMutex(Android_ActivityMutex);
if (Android_Window) {
SDL_SetWindowSafeAreaInsets(Android_Window, left, right, top, bottom);
}
SDL_UnlockMutex(Android_ActivityMutex);
}
JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeAddTouch)(
JNIEnv *env, jclass cls,
jint touchId, jstring name)

View File

@ -484,6 +484,7 @@ SDL3_0.0.0 {
SDL_GetWindowPixelFormat;
SDL_GetWindowPosition;
SDL_GetWindowProperties;
SDL_GetWindowSafeArea;
SDL_GetWindowSize;
SDL_GetWindowSizeInPixels;
SDL_GetWindowSurface;

View File

@ -509,6 +509,7 @@
#define SDL_GetWindowPixelFormat SDL_GetWindowPixelFormat_REAL
#define SDL_GetWindowPosition SDL_GetWindowPosition_REAL
#define SDL_GetWindowProperties SDL_GetWindowProperties_REAL
#define SDL_GetWindowSafeArea SDL_GetWindowSafeArea_REAL
#define SDL_GetWindowSize SDL_GetWindowSize_REAL
#define SDL_GetWindowSizeInPixels SDL_GetWindowSizeInPixels_REAL
#define SDL_GetWindowSurface SDL_GetWindowSurface_REAL

View File

@ -529,6 +529,7 @@ SDL_DYNAPI_PROC(float,SDL_GetWindowPixelDensity,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(SDL_PixelFormat,SDL_GetWindowPixelFormat,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetWindowPosition,(SDL_Window *a, int *b, int *c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_PropertiesID,SDL_GetWindowProperties,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(int,SDL_GetWindowSafeArea,(SDL_Window *a, SDL_Rect *b),(a,b),return)
SDL_DYNAPI_PROC(int,SDL_GetWindowSize,(SDL_Window *a, int *b, int *c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_GetWindowSizeInPixels,(SDL_Window *a, int *b, int *c),(a,b,c),return)
SDL_DYNAPI_PROC(SDL_Surface*,SDL_GetWindowSurface,(SDL_Window *a),(a),return)

View File

@ -497,6 +497,7 @@ static void SDL_LogEvent(const SDL_Event *event)
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MOVED);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_RESIZED);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_SAFE_AREA_CHANGED);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MINIMIZED);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_MAXIMIZED);
SDL_WINDOWEVENT_CASE(SDL_EVENT_WINDOW_RESTORED);

View File

@ -193,6 +193,7 @@ int SDL_SendWindowEvent(SDL_Window *window, SDL_EventType windowevent,
if (windowevent == SDL_EVENT_WINDOW_MOVED ||
windowevent == SDL_EVENT_WINDOW_RESIZED ||
windowevent == SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED ||
windowevent == SDL_EVENT_WINDOW_SAFE_AREA_CHANGED ||
windowevent == SDL_EVENT_WINDOW_EXPOSED ||
windowevent == SDL_EVENT_WINDOW_OCCLUDED) {
SDL_FilterEvents(RemoveSupercededWindowEvents, &event);

View File

@ -1614,6 +1614,14 @@ static void SDLTest_PrintEvent(const SDL_Event *event)
SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " changed pixel size to %" SDL_PRIs32 "x%" SDL_PRIs32,
event->window.windowID, event->window.data1, event->window.data2);
break;
case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED: {
SDL_Rect rect;
SDL_GetWindowSafeArea(SDL_GetWindowFromID(event->window.windowID), &rect);
SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " changed safe area to: %d,%d %dx%d\n",
event->window.windowID, rect.x, rect.y, rect.w, rect.h);
break;
}
case SDL_EVENT_WINDOW_MINIMIZED:
SDL_Log("SDL EVENT: Window %" SDL_PRIu32 " minimized", event->window.windowID);
break;

View File

@ -100,6 +100,12 @@ struct SDL_Window
SDL_bool is_destroying;
SDL_bool is_dropping; /* drag/drop in progress, expecting SDL_SendDropComplete(). */
int safe_inset_left;
int safe_inset_right;
int safe_inset_top;
int safe_inset_bottom;
SDL_Rect safe_rect;
SDL_bool text_input_active;
SDL_Rect text_input_rect;
int text_input_cursor;
@ -526,6 +532,7 @@ extern SDL_DisplayData *SDL_GetDisplayDriverData(SDL_DisplayID display);
extern SDL_DisplayData *SDL_GetDisplayDriverDataForWindow(SDL_Window *window);
extern int SDL_GetMessageBoxCount(void);
extern void SDL_SetWindowHDRProperties(SDL_Window *window, const SDL_HDROutputProperties *HDR, SDL_bool send_event);
extern void SDL_SetWindowSafeAreaInsets(SDL_Window *window, int left, int right, int top, int bottom);
extern void SDL_GL_DeduceMaxSupportedESProfile(int *major, int *minor);

View File

@ -165,6 +165,7 @@ extern SDL_bool Cocoa_SetWindowFullscreenSpace(SDL_Window *window, SDL_bool stat
static void SDL_CheckWindowDisplayChanged(SDL_Window *window);
static void SDL_CheckWindowDisplayScaleChanged(SDL_Window *window);
static void SDL_CheckWindowSafeAreaChanged(SDL_Window *window);
/* Convenience functions for reading driver flags */
static SDL_bool SDL_ModeSwitchingEmulated(SDL_VideoDevice *_this)
@ -3789,6 +3790,7 @@ void SDL_OnWindowResized(SDL_Window *window)
{
SDL_CheckWindowDisplayChanged(window);
SDL_CheckWindowPixelSizeChanged(window);
SDL_CheckWindowSafeAreaChanged(window);
if ((window->flags & SDL_WINDOW_TRANSPARENT) && _this->UpdateWindowShape) {
SDL_Surface *surface = (SDL_Surface *)SDL_GetPointerProperty(window->props, SDL_PROP_WINDOW_SHAPE_POINTER, NULL);
@ -3813,6 +3815,43 @@ void SDL_OnWindowPixelSizeChanged(SDL_Window *window)
window->surface_valid = SDL_FALSE;
}
static void SDL_CheckWindowSafeAreaChanged(SDL_Window *window)
{
SDL_Rect rect;
rect.x = window->safe_inset_left;
rect.y = window->safe_inset_top;
rect.w = window->w - (window->safe_inset_right + window->safe_inset_left);
rect.h = window->h - (window->safe_inset_top + window->safe_inset_bottom);
if (SDL_memcmp(&rect, &window->safe_rect, sizeof(rect)) != 0) {
SDL_copyp(&window->safe_rect, &rect);
SDL_SendWindowEvent(window, SDL_EVENT_WINDOW_SAFE_AREA_CHANGED, 0, 0);
}
}
void SDL_SetWindowSafeAreaInsets(SDL_Window *window, int left, int right, int top, int bottom)
{
window->safe_inset_left = left;
window->safe_inset_right = right;
window->safe_inset_top = top;
window->safe_inset_bottom = bottom;
SDL_CheckWindowSafeAreaChanged(window);
}
int SDL_GetWindowSafeArea(SDL_Window *window, SDL_Rect *rect)
{
if (rect) {
SDL_zerop(rect);
}
CHECK_WINDOW_MAGIC(window, -1);
if (rect) {
SDL_copyp(rect, &window->safe_rect);
}
return 0;
}
void SDL_OnWindowMinimized(SDL_Window *window)
{
if (window->flags & SDL_WINDOW_FULLSCREEN) {

View File

@ -2785,6 +2785,22 @@ int Cocoa_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_Vi
[data.listener resumeVisibleObservation];
}
// Update the safe area insets
// The view never seems to reflect the safe area, so we'll use the screen instead
if (@available(macOS 12.0, *)) {
if (fullscreen) {
NSScreen *screen = [nswindow screen];
SDL_SetWindowSafeAreaInsets(data.window,
(int)SDL_ceilf(screen.safeAreaInsets.left),
(int)SDL_ceilf(screen.safeAreaInsets.right),
(int)SDL_ceilf(screen.safeAreaInsets.top),
(int)SDL_ceilf(screen.safeAreaInsets.bottom));
} else {
SDL_SetWindowSafeAreaInsets(data.window, 0, 0, 0, 0);
}
}
ScheduleContextUpdates(data);
}

View File

@ -43,4 +43,6 @@
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)safeAreaInsetsDidChange;
@end

View File

@ -368,6 +368,18 @@ extern int SDL_AppleTVRemoteOpenedAsJoystick;
}
}
- (void)safeAreaInsetsDidChange
{
// Update the safe area insets
if (@available(iOS 11.0, tvOS 11.0, *)) {
SDL_SetWindowSafeAreaInsets(sdlwindow,
(int)SDL_ceilf(self.safeAreaInsets.left),
(int)SDL_ceilf(self.safeAreaInsets.right),
(int)SDL_ceilf(self.safeAreaInsets.top),
(int)SDL_ceilf(self.safeAreaInsets.bottom));
}
}
#if defined(SDL_PLATFORM_TVOS) || defined(__IPHONE_9_1)
- (SDL_Scancode)scancodeFromPress:(UIPress *)press
{