Detect device form factor (#12584)

This commit is contained in:
Semphriss
2026-06-21 11:33:13 -04:00
committed by GitHub
parent 5317703567
commit d474769878
10 changed files with 184 additions and 19 deletions

View File

@@ -1345,6 +1345,25 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
return false;
}
/**
* This method is called by SDL using JNI.
*/
static String getDeviceFormFactor()
{
// TODO: WearOS
if (isAndroidTV()) {
return "tv";
} else if (isVRHeadset()) {
return "headset";
} else if (isTablet()) {
return "tablet";
//} else if (isAndroidAutomotive()) {
// return "car";
} else {
return "phone";
}
}
public static double getDiagonal()
{
DisplayMetrics metrics = new DisplayMetrics();

View File

@@ -655,6 +655,64 @@ extern SDL_DECLSPEC bool SDLCALL SDL_IsTablet(void);
*/
extern SDL_DECLSPEC bool SDLCALL SDL_IsTV(void);
/**
* The possible form factors for a device.
*
* \since This enum is available since SDL 3.4.0.
*
* \sa SDL_GetDeviceFormFactor
* \sa SDL_GetDeviceFormFactorName
*/
typedef enum SDL_FormFactor {
SDL_FORMFACTOR_UNKNOWN = 0,
SDL_FORMFACTOR_DESKTOP,
SDL_FORMFACTOR_LAPTOP,
SDL_FORMFACTOR_PHONE,
SDL_FORMFACTOR_TABLET,
SDL_FORMFACTOR_CONSOLE,
SDL_FORMFACTOR_HANDHELD,
SDL_FORMFACTOR_WATCH,
SDL_FORMFACTOR_TV,
SDL_FORMFACTOR_HEADSET,
SDL_FORMFACTOR_CAR
} SDL_FormFactor;
/**
* Get the form factor of the current device.
*
* This function guesses what the device may be, but may report inaccurate or
* outright wrong results. For example, it may report a laptop as a desktop, or
* a car device as a phone.
*
* Depending on the usage, there may be different functions better suited for
* each purpose. For example, activating touch controls can be done by detecting
* the presence of a touchscreen rather than restricting to phones and tablets.
*
* \returns the best guess for the form factor of the current device.
*
* \since This function is available since SDL 3.4.0.
*
* \sa SDL_FormFactor
* \sa SDL_GetDeviceFormFactorName
*/
extern SDL_DECLSPEC SDL_FormFactor SDLCALL SDL_GetDeviceFormFactor(void);
/**
* Get a short name for the current device.
*
* The name will be in English.
*
* \param form_factor the form factor to query.
* \returns a human-readable name for the given form factor, or
* "SDL_FORMFACTOR_UNKNOWN" if the form factor isn't recognized.
*
* \since This function is available since SDL 3.4.0.
*
* \sa SDL_FormFactor
* \sa SDL_GetDeviceFormFactor
*/
extern SDL_DECLSPEC const char* SDLCALL SDL_GetDeviceFormFactorName(SDL_FormFactor form_factor);
/**
* Application sandbox environment.
*

View File

@@ -849,28 +849,59 @@ bool SDL_IsPhone(void)
bool SDL_IsTablet(void)
{
#ifdef SDL_PLATFORM_ANDROID
return SDL_IsAndroidTablet();
#elif defined(SDL_PLATFORM_IOS)
extern bool SDL_IsIPad(void);
return SDL_IsIPad();
#else
return false;
#endif
return SDL_GetDeviceFormFactor() == SDL_FORMFACTOR_TABLET;
}
bool SDL_IsTV(void)
{
#ifdef SDL_PLATFORM_ANDROID
return SDL_IsAndroidTV();
return SDL_GetDeviceFormFactor() == SDL_FORMFACTOR_TV;
}
SDL_FormFactor SDL_GetDeviceFormFactor(void)
{
#ifdef SDL_FORMFACTOR_PRIVATE
return SDL_FORMFACTOR_PRIVATE;
#elif defined(SDL_PLATFORM_ANDROID)
return SDL_GetAndroidDeviceFormFactor();
#elif defined(SDL_PLATFORM_IOS)
extern bool SDL_IsAppleTV(void);
return SDL_IsAppleTV();
extern bool SDL_GetUIKitDeviceFormFactor(void);
return SDL_GetUIKitDeviceFormFactor();
#elif defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES) || defined(SDL_PLATFORM_PS2)
return SDL_FORMFACTOR_CONSOLE;
#elif defined(SDL_PLATFORM_PSP) || defined(SDL_PLATFORM_VITA) || defined(SDL_PLATFORM_3DS)
return SDL_FORMFACTOR_HANDHELD;
#elif defined(SDL_PLATFORM_QNXNTO)
/* TODO: QNX is used in BlackBerry phones and tablets, and in many embedded devices */
return SDL_FORMFACTOR_UNKNOWN;
#elif defined(SDL_PLATFORM_WINGDK)
/* TODO: GDK can be either desktop Windows or XBox */
return SDL_FORMFACTOR_UNKNOWN;
#else
return false;
return SDL_FORMFACTOR_DESKTOP;
#endif
}
const char* SDL_GetDeviceFormFactorName(SDL_FormFactor form_factor)
{
switch (form_factor)
{
#define CASE(x) case x: return #x;
default:
CASE(SDL_FORMFACTOR_UNKNOWN)
CASE(SDL_FORMFACTOR_DESKTOP)
CASE(SDL_FORMFACTOR_LAPTOP)
CASE(SDL_FORMFACTOR_PHONE)
CASE(SDL_FORMFACTOR_TABLET)
CASE(SDL_FORMFACTOR_CONSOLE)
CASE(SDL_FORMFACTOR_HANDHELD)
CASE(SDL_FORMFACTOR_WATCH)
CASE(SDL_FORMFACTOR_TV)
CASE(SDL_FORMFACTOR_HEADSET)
CASE(SDL_FORMFACTOR_CAR)
#undef CASE
}
}
static SDL_Sandbox SDL_DetectSandbox(void)
{
#if defined(SDL_PLATFORM_LINUX)

View File

@@ -369,6 +369,7 @@ static jmethodID midClipboardSetText;
static jmethodID midCreateCustomCursor;
static jmethodID midDestroyCustomCursor;
static jmethodID midGetContext;
static jmethodID midGetDeviceFormFactor;
static jmethodID midGetManifestEnvironmentVariables;
static jmethodID midGetNativeSurface;
static jmethodID midInitTouch;
@@ -659,6 +660,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
midCreateCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "createCustomCursor", "([IIIII)I");
midDestroyCustomCursor = (*env)->GetStaticMethodID(env, mActivityClass, "destroyCustomCursor", "(I)V");
midGetContext = (*env)->GetStaticMethodID(env, mActivityClass, "getContext", "()Landroid/app/Activity;");
midGetDeviceFormFactor = (*env)->GetStaticMethodID(env, mActivityClass, "getDeviceFormFactor", "()Ljava/lang/String;");
midGetManifestEnvironmentVariables = (*env)->GetStaticMethodID(env, mActivityClass, "getManifestEnvironmentVariables", "()Z");
midGetNativeSurface = (*env)->GetStaticMethodID(env, mActivityClass, "getNativeSurface", "()Landroid/view/Surface;");
midInitTouch = (*env)->GetStaticMethodID(env, mActivityClass, "initTouch", "()V");
@@ -691,6 +693,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
!midCreateCustomCursor ||
!midDestroyCustomCursor ||
!midGetContext ||
!midGetDeviceFormFactor ||
!midGetManifestEnvironmentVariables ||
!midGetNativeSurface ||
!midInitTouch ||
@@ -3505,4 +3508,37 @@ bool Android_JNI_ShowFileDialog(
return true;
}
SDL_FormFactor SDL_GetAndroidDeviceFormFactor(void)
{
JNIEnv *env = Android_JNI_GetEnv();
SDL_FormFactor form_factor = SDL_FORMFACTOR_UNKNOWN;
jstring string;
string = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetDeviceFormFactor);
if (string) {
const char *utf = (*env)->GetStringUTFChars(env, string, 0);
if (utf) {
if (SDL_strcmp(utf, "tv") == 0) {
form_factor = SDL_FORMFACTOR_TV;
} else if (SDL_strcmp(utf, "tablet") == 0) {
form_factor = SDL_FORMFACTOR_TABLET;
} else if (SDL_strcmp(utf, "phone") == 0) {
form_factor = SDL_FORMFACTOR_PHONE;
} else if (SDL_strcmp(utf, "car") == 0) {
form_factor = SDL_FORMFACTOR_CAR;
} else if (SDL_strcmp(utf, "headset") == 0) {
form_factor = SDL_FORMFACTOR_HEADSET;
} else if (SDL_strcmp(utf, "watch") == 0) {
form_factor = SDL_FORMFACTOR_WATCH;
} else {
form_factor = SDL_FORMFACTOR_UNKNOWN;
}
(*env)->ReleaseStringUTFChars(env, string, utf);
}
(*env)->DeleteLocalRef(env, string);
}
return form_factor;
}
#endif // SDL_PLATFORM_ANDROID

View File

@@ -151,6 +151,7 @@ int SDL_GetAndroidSDKVersion(void);
bool SDL_IsAndroidTablet(void);
bool SDL_IsAndroidTV(void);
SDL_FormFactor SDL_GetAndroidDeviceFormFactor(void);
char *SDL_GetAndroidPackageName(void); // this is a SDL_malloc'd string the caller will own.

View File

@@ -1298,3 +1298,5 @@ _SDL_RequestNotificationPermission
_SDL_ShowNotificationWithProperties
_SDL_ShowNotification
_SDL_RemoveNotification
_SDL_GetDeviceFormFactor
_SDL_GetDeviceFormFactorName

View File

@@ -1299,6 +1299,8 @@ SDL3_0.0.0 {
SDL_ShowNotificationWithProperties;
SDL_ShowNotification;
SDL_RemoveNotification;
SDL_GetDeviceFormFactor;
SDL_GetDeviceFormFactorName;
# extra symbols go here (don't modify this line)
local: *;
};

View File

@@ -1325,3 +1325,5 @@
#define SDL_ShowNotificationWithProperties SDL_ShowNotificationWithProperties_REAL
#define SDL_ShowNotification SDL_ShowNotification_REAL
#define SDL_RemoveNotification SDL_RemoveNotification_REAL
#define SDL_GetDeviceFormFactor SDL_GetDeviceFormFactor_REAL
#define SDL_GetDeviceFormFactorName SDL_GetDeviceFormFactorName_REAL

View File

@@ -1333,3 +1333,5 @@ SDL_DYNAPI_PROC(bool,SDL_RequestNotificationPermission,(void),(),return)
SDL_DYNAPI_PROC(SDL_NotificationID,SDL_ShowNotificationWithProperties,(SDL_PropertiesID a),(a),return)
SDL_DYNAPI_PROC(SDL_NotificationID,SDL_ShowNotification,(const char *a,const char *b,SDL_Surface *c,SDL_NotificationAction *d,int e),(a,b,c,d,e),return)
SDL_DYNAPI_PROC(bool,SDL_RemoveNotification,(SDL_NotificationID a),(a),return)
SDL_DYNAPI_PROC(SDL_FormFactor,SDL_GetDeviceFormFactor,(void),(),return)
SDL_DYNAPI_PROC(const char*,SDL_GetDeviceFormFactorName,(SDL_FormFactor a),(a),return)

View File

@@ -384,14 +384,26 @@ void SDL_NSLog(const char *prefix, const char *text)
* This doesn't really have anything to do with the interfaces of the SDL video
* subsystem, but we need to stuff this into an Objective-C source code file.
*/
bool SDL_IsIPad(void)
{
return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad);
}
bool SDL_IsAppleTV(void)
bool SDL_GetUIKitDeviceFormFactor(void)
{
return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomTV);
// TODO: Apple Watch
switch ([UIDevice currentDevice].userInterfaceIdiom) {
case UIUserInterfaceIdiomPhone:
return SDL_FORMFACTOR_PHONE;
case UIUserInterfaceIdiomPad:
return SDL_FORMFACTOR_TABLET;
case UIUserInterfaceIdiomTV:
return SDL_FORMFACTOR_TV;
case UIUserInterfaceIdiomCarPlay:
return SDL_FORMFACTOR_CAR;
case UIUserInterfaceIdiomMac:
return SDL_FORMFACTOR_DESKTOP;
case UIUserInterfaceIdiomVision:
return SDL_FORMFACTOR_HEADSET;
default:
return SDL_FORMFACTOR_UNKNOWN;
}
}
#endif // SDL_VIDEO_DRIVER_UIKIT