Skip to content

Detect device form factor #12584

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions android-project/app/src/main/java/org/libsdl/app/SDLActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,25 @@ public static boolean isVRHeadset() {
return false;
}

/**
* This method is called by SDL using JNI.
*/
static String getDeviceFormFactor()
{
// TODO: WearOS
if (isAndroidTV()) {
return "tv";
} else if (isVRHeadset()) {
return "vr";
} else if (isTablet()) {
return "tablet";
//} else if (isAndroidAutomotive()) {
// return "car";
} else {
return "phone";
}
}

public static double getDiagonal()
{
DisplayMetrics metrics = new DisplayMetrics();
Expand Down
40 changes: 40 additions & 0 deletions include/SDL3/SDL_system.h
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,46 @@ 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
*/
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_VR,
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
*/
extern SDL_DECLSPEC SDL_FormFactor SDLCALL SDL_GetDeviceFormFactor(void);

/**
* Application sandbox environment.
*
Expand Down
35 changes: 23 additions & 12 deletions src/SDL.c
Original file line number Diff line number Diff line change
Expand Up @@ -769,25 +769,36 @@ const char *SDL_GetPlatform(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)
{
return SDL_GetDeviceFormFactor() == SDL_FORMFACTOR_TV;
}

SDL_FormFactor SDL_GetDeviceFormFactor(void)
{
/* TODO: SDL private platforms? */
#ifdef SDL_PLATFORM_ANDROID
return SDL_IsAndroidTV();
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;
#elif defined(SDL_PLATFORM_HAIKU) || defined(SDL_PLATFORM_BSDI) || defined(SDL_PLATFORM_HPUX) || defined(SDL_PLATFORM_IRIX) || defined(SDL_PLATFORM_EMSCRIPTEN) || defined(SDL_PLATFORM_OS2) || defined(SDL_PLATFORM_OSF) || defined(SDL_PLATFORM_RISCOS) || defined(SDL_PLATFORM_SOLARIS) || defined(SDL_PLATFORM_WIN32)
return SDL_FORMFACTOR_DESKTOP;
#else
return false;
return SDL_FORMFACTOR_UNKNOWN;
#endif
}

Expand Down
36 changes: 36 additions & 0 deletions src/core/android/SDL_android.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,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;
Expand Down Expand Up @@ -635,6 +636,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/content/Context;");
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");
Expand Down Expand Up @@ -667,6 +669,7 @@ JNIEXPORT void JNICALL SDL_JAVA_INTERFACE(nativeSetupJNI)(JNIEnv *env, jclass cl
!midCreateCustomCursor ||
!midDestroyCustomCursor ||
!midGetContext ||
!midGetDeviceFormFactor ||
!midGetManifestEnvironmentVariables ||
!midGetNativeSurface ||
!midInitTouch ||
Expand Down Expand Up @@ -2821,4 +2824,37 @@ bool Android_JNI_OpenFileDialog(
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, "vr") == 0) {
form_factor = SDL_FORMFACTOR_VR;
} 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
1 change: 1 addition & 0 deletions src/core/android/SDL_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ int SDL_GetAndroidSDKVersion(void);

bool SDL_IsAndroidTablet(void);
bool SDL_IsAndroidTV(void);
SDL_FormFactor SDL_GetAndroidDeviceFormFactor(void);

// File Dialogs
bool Android_JNI_OpenFileDialog(SDL_DialogFileCallback callback, void* userdata,
Expand Down
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi.sym
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,7 @@ SDL3_0.0.0 {
SDL_SetWindowProgressValue;
SDL_GetWindowProgressState;
SDL_GetWindowProgressValue;
SDL_GetDeviceFormFactor;
# extra symbols go here (don't modify this line)
local: *;
};
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi_overrides.h
Original file line number Diff line number Diff line change
Expand Up @@ -1271,3 +1271,4 @@
#define SDL_SetWindowProgressValue SDL_SetWindowProgressValue_REAL
#define SDL_GetWindowProgressState SDL_GetWindowProgressState_REAL
#define SDL_GetWindowProgressValue SDL_GetWindowProgressValue_REAL
#define SDL_GetDeviceFormFactor SDL_GetDeviceFormFactor_REAL
1 change: 1 addition & 0 deletions src/dynapi/SDL_dynapi_procs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1279,3 +1279,4 @@ SDL_DYNAPI_PROC(bool,SDL_SetWindowProgressState,(SDL_Window *a,SDL_ProgressState
SDL_DYNAPI_PROC(bool,SDL_SetWindowProgressValue,(SDL_Window *a,float b),(a,b),return)
SDL_DYNAPI_PROC(SDL_ProgressState,SDL_GetWindowProgressState,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(float,SDL_GetWindowProgressValue,(SDL_Window *a),(a),return)
SDL_DYNAPI_PROC(SDL_FormFactor,SDL_GetDeviceFormFactor,(void),(),return)
24 changes: 18 additions & 6 deletions src/video/uikit/SDL_uikitvideo.m
Original file line number Diff line number Diff line change
Expand Up @@ -301,14 +301,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_VR;
default:
return SDL_FORMFACTOR_UNKNOWN;
}
}

#endif // SDL_VIDEO_DRIVER_UIKIT
Loading