diff --git a/driver.go b/driver.go index 2cc7cecd2f..b66691dd31 100644 --- a/driver.go +++ b/driver.go @@ -37,4 +37,9 @@ type Driver interface { // // Since: 2.5 DoubleTapDelay() time.Duration + + // SetDisableScreenBlanking allows an app to ask the device not to sleep/lock/blank displays + // + // Since: 2.5 + SetDisableScreenBlanking(bool) } diff --git a/internal/driver/glfw/driver.go b/internal/driver/glfw/driver.go index 26755572cf..0e31bc26c2 100644 --- a/internal/driver/glfw/driver.go +++ b/internal/driver/glfw/driver.go @@ -169,6 +169,10 @@ func (d *gLDriver) DoubleTapDelay() time.Duration { return doubleTapDelay } +func (d *gLDriver) SetDisableScreenBlanking(bool) { + // TODO implement for Windows, macOS, X11 and Wayland +} + // NewGLDriver sets up a new Driver instance implemented using the GLFW Go library and OpenGL bindings. func NewGLDriver() *gLDriver { repository.Register("file", intRepo.NewFileRepository()) diff --git a/internal/driver/mobile/android.c b/internal/driver/mobile/android.c index afc898dbe0..970174039c 100644 --- a/internal/driver/mobile/android.c +++ b/internal/driver/mobile/android.c @@ -480,3 +480,25 @@ char* listURI(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) { LOG_FATAL("Unrecognized scheme: %s", uriCstr); return ""; } + +void keepScreenOn(uintptr_t jni_env, uintptr_t ctx, bool disabled) { + JNIEnv *env = (JNIEnv*)jni_env; + jclass activityClass = find_class(env, "android/app/Activity"); + jmethodID getWindow = find_method(env, activityClass, "getWindow", "()Landroid/view/Window;"); + + jobject win = (*env)->CallObjectMethod(env, (jobject)ctx, getWindow); + jclass windowClass = find_class(env, "android/view/Window"); + + jmethodID action = NULL; + if (disabled) { + action = find_method(env, windowClass, "addFlags", "(I)V"); + } else { + action = find_method(env, windowClass, "clearFlags", "(I)V"); + } + + jclass paramsClass = find_class(env, "android/view/WindowManager$LayoutParams" ); + jfieldID screenFlagField = (*env)->GetStaticFieldID(env, paramsClass, "FLAG_KEEP_SCREEN_ON", "I" ); + int screenFlag = (*env)->GetStaticIntField(env, paramsClass, screenFlagField); + + (*env)->CallVoidMethod(env, win, action, screenFlag); +} diff --git a/internal/driver/mobile/device_desktop.go b/internal/driver/mobile/device_desktop.go index 006e71cfec..2b51209bd1 100644 --- a/internal/driver/mobile/device_desktop.go +++ b/internal/driver/mobile/device_desktop.go @@ -9,3 +9,7 @@ const tapYOffset = 0 // no finger compensation on desktop (simulation) func (*device) SystemScaleForWindow(_ fyne.Window) float32 { return 2 // this is simply due to the high number of pixels on a mobile device - just an approximation } + +func setDisableScreenBlank(_ bool) { + // ignore in mobile simulation mode +} diff --git a/internal/driver/mobile/device_wayland.go b/internal/driver/mobile/device_wayland.go index 30f3ee8fd9..8ad3e3aff5 100644 --- a/internal/driver/mobile/device_wayland.go +++ b/internal/driver/mobile/device_wayland.go @@ -9,3 +9,7 @@ const tapYOffset = -4.0 // to compensate for how we hold our fingers on the devi func (*device) SystemScaleForWindow(_ fyne.Window) float32 { return 1 // PinePhone simplification, our only wayland mobile currently } + +func setDisableScreenBlank(_ bool) { + // ignore in mobile simulation mode +} diff --git a/internal/driver/mobile/driver.go b/internal/driver/mobile/driver.go index 242a82e29a..493395c5d8 100644 --- a/internal/driver/mobile/driver.go +++ b/internal/driver/mobile/driver.go @@ -220,6 +220,10 @@ func (d *mobileDriver) Run() { }) } +func (*mobileDriver) SetDisableScreenBlanking(disable bool) { + setDisableScreenBlank(disable) +} + func (d *mobileDriver) handleLifecycle(e lifecycle.Event, w fyne.Window) { c := w.Canvas().(*mobileCanvas) switch e.Crosses(lifecycle.StageVisible) { diff --git a/internal/driver/mobile/driver_android.go b/internal/driver/mobile/driver_android.go new file mode 100644 index 0000000000..9068e312f5 --- /dev/null +++ b/internal/driver/mobile/driver_android.go @@ -0,0 +1,23 @@ +//go:build android + +package mobile + +import "fyne.io/fyne/v2/driver" + +/* +#include +#include + +void keepScreenOn(uintptr_t jni_env, uintptr_t ctx, bool disabled); +*/ +import "C" + +func setDisableScreenBlank(disable bool) { + driver.RunNative(func(ctx any) error { + ac := ctx.(*driver.AndroidContext) + + C.keepScreenOn(C.uintptr_t(ac.Env), C.uintptr_t(ac.Ctx), C.bool(disable)) + + return nil + }) +} diff --git a/internal/driver/mobile/driver_ios.go b/internal/driver/mobile/driver_ios.go new file mode 100644 index 0000000000..0affb7348f --- /dev/null +++ b/internal/driver/mobile/driver_ios.go @@ -0,0 +1,15 @@ +//go:build ios + +package mobile + +/* +#cgo darwin LDFLAGS: -framework UIKit +#import + +void disableIdleTimer(BOOL disabled); +*/ +import "C" + +func setDisableScreenBlank(disable bool) { + C.disableIdleTimer(C.BOOL(disable)) +} diff --git a/internal/driver/mobile/driver_ios.m b/internal/driver/mobile/driver_ios.m new file mode 100644 index 0000000000..33c29160cf --- /dev/null +++ b/internal/driver/mobile/driver_ios.m @@ -0,0 +1,12 @@ +//go:build ios + +#import +#import + +void disableIdleTimer(BOOL disabled) { + @autoreleasepool { + [[NSOperationQueue mainQueue] addOperationWithBlock:^ { + [UIApplication sharedApplication].idleTimerDisabled = disabled; + }]; + } +} diff --git a/test/testdriver.go b/test/testdriver.go index 6bc2a2269a..12d34e5d74 100644 --- a/test/testdriver.go +++ b/test/testdriver.go @@ -134,3 +134,7 @@ func (d *testDriver) removeWindow(w *testWindow) { func (d *testDriver) DoubleTapDelay() time.Duration { return 300 * time.Millisecond } + +func (d *testDriver) SetDisableScreenBlanking(_ bool) { + // no-op for test +}