From 8b50a91e0a3a26feb8ee26c59ac15c57cce5aded Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 06:58:39 +0000 Subject: [PATCH 1/5] Initial plan From ada610e51b8b7768b1d5bea526ea7b08cc62e81f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 07:06:41 +0000 Subject: [PATCH 2/5] Add set_keep_awake API to prevent screen dimming/sleep with gamepad input Agent-Logs-Url: https://github.com/subsoap/defos/sessions/df5acc75-529a-4503-a815-05247ebd9deb Co-authored-by: subsoap <409170+subsoap@users.noreply.github.com> --- README.md | 22 ++++++++++++++++++++++ defos/api/defos.script_api | 16 ++++++++++++++++ defos/ext.manifest | 4 ++-- defos/src/defos.cpp | 17 +++++++++++++++++ defos/src/defos_html5.cpp | 31 +++++++++++++++++++++++++++++++ defos/src/defos_linux.cpp | 16 ++++++++++++++++ defos/src/defos_mac.mm | 31 +++++++++++++++++++++++++++++++ defos/src/defos_private.h | 3 +++ defos/src/defos_win.cpp | 21 +++++++++++++++++++++ 9 files changed, 159 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 99b5325..19aec2d 100644 --- a/README.md +++ b/README.md @@ -381,6 +381,28 @@ arguments = defos.get_arguments() --- +**Prevent screen dimming and sleep** when the game is active (e.g. when using a gamepad). On HTML5, requires an active/visible tab and a browser that supports the Screen Wake Lock API (Chrome 84+, Firefox 126+, Safari 16.4+). Use `defos.is_keep_awake_supported()` to check browser support before calling `defos.set_keep_awake(true)` on HTML5. + +```lua +defos.set_keep_awake(bool_value) +bool_value = defos.is_keep_awake_supported() +``` + +Example usage with gamepad input: +```lua +local keep_awake = false +function on_input(self, action_id, action) + if action.gamepad ~= nil then + if not keep_awake then + keep_awake = true + defos.set_keep_awake(true) + end + end +end +``` + +--- + If you'd like to see any other features, open an issue. ## Example diff --git a/defos/api/defos.script_api b/defos/api/defos.script_api index 4fde369..3573b68 100644 --- a/defos/api/defos.script_api +++ b/defos/api/defos.script_api @@ -406,6 +406,22 @@ type: table desc: Table of command line arguments + - name: set_keep_awake + type: function + desc: Prevents or allows the screen from dimming or going to sleep. Useful when the game is controlled by a gamepad. On HTML5, requires an active/visible tab and browser support (Chrome 84+, Firefox 126+, Safari 16.4+). Platforms - Linux, Windows, OSX, HTML5. + parameters: + - name: keep_awake + type: boolean + desc: Whether to prevent screen dimming and sleep + + - name: is_keep_awake_supported + type: function + desc: Checks if the keep awake feature is supported on the current platform/browser. Platforms - Linux, Windows, OSX, HTML5. + returns: + - name: supported + type: boolean + desc: Whether keep awake is supported + - name: CURSOR_ARROW type: number desc: Default arrow cursor diff --git a/defos/ext.manifest b/defos/ext.manifest index 2116d39..f0d79ab 100644 --- a/defos/ext.manifest +++ b/defos/ext.manifest @@ -3,9 +3,9 @@ name: "defos" platforms: osx: context: - frameworks: ["AppKit"] + frameworks: ["AppKit", "IOKit"] linux: context: frameworks: ["X11", "Xrender"] - libs: ["Xrender", "Xfixes"] + libs: ["Xrender", "Xfixes", "Xss"] diff --git a/defos/src/defos.cpp b/defos/src/defos.cpp index 8851381..b138fdd 100644 --- a/defos/src/defos.cpp +++ b/defos/src/defos.cpp @@ -629,6 +629,20 @@ void defos_emit_event(DefosEvent event) assert(top == lua_gettop(L)); } +// Keep awake + +static int set_keep_awake(lua_State *L) +{ + defos_set_keep_awake(checkboolean(L, 1)); + return 0; +} + +static int is_keep_awake_supported(lua_State *L) +{ + lua_pushboolean(L, defos_is_keep_awake_supported()); + return 1; +} + // Lua module initialization static const luaL_reg Module_methods[] = @@ -685,6 +699,8 @@ static const luaL_reg Module_methods[] = {"get_bundle_root", get_bundle_root}, {"get_arguments", get_arguments}, {"get_parameters", get_arguments}, // For backwards compatibility + {"set_keep_awake", set_keep_awake}, + {"is_keep_awake_supported", is_keep_awake_supported}, {0, 0}}; static void LuaInit(lua_State *L) @@ -754,6 +770,7 @@ dmExtension::Result InitializeDefos(dmExtension::Params *params) dmExtension::Result FinalizeDefos(dmExtension::Params *params) { + defos_set_keep_awake(false); defos_final(); return dmExtension::RESULT_OK; } diff --git a/defos/src/defos_html5.cpp b/defos/src/defos_html5.cpp index 2ddf795..8909edf 100644 --- a/defos/src/defos_html5.cpp +++ b/defos/src/defos_html5.cpp @@ -410,4 +410,35 @@ DisplayID defos_get_current_display() { return NULL; } +void defos_set_keep_awake(bool keep_awake) { + EM_ASM({ + var keep = $0; + if (keep) { + if (!Module.__defosjs_wakeLock && 'wakeLock' in navigator) { + navigator.wakeLock.request('screen').then(function(lock) { + Module.__defosjs_wakeLock = lock; + lock.addEventListener('release', function() { + Module.__defosjs_wakeLock = null; + }); + }).catch(function() {}); + } + } else { + if (Module.__defosjs_wakeLock) { + Module.__defosjs_wakeLock.release().then(function() { + Module.__defosjs_wakeLock = null; + }).catch(function() { + Module.__defosjs_wakeLock = null; + }); + } + } + }, keep_awake ? 1 : 0); +} + +bool defos_is_keep_awake_supported() { + int supported = EM_ASM_INT({ + return ('wakeLock' in navigator) ? 1 : 0; + }); + return supported != 0; +} + #endif diff --git a/defos/src/defos_linux.cpp b/defos/src/defos_linux.cpp index 7b762b7..7a45b0b 100644 --- a/defos/src/defos_linux.cpp +++ b/defos/src/defos_linux.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -926,4 +927,19 @@ static void send_message(Window &window, Atom type, long a, long b, long c, long XSendEvent(disp, root, False, SubstructureNotifyMask | SubstructureRedirectMask, &event); } +static bool g_keep_awake = false; + +void defos_set_keep_awake(bool keep_awake) +{ + if (keep_awake == g_keep_awake) { return; } + g_keep_awake = keep_awake; + XScreenSaverSuspend(disp, keep_awake ? True : False); + XFlush(disp); +} + +bool defos_is_keep_awake_supported() +{ + return true; +} + #endif diff --git a/defos/src/defos_mac.mm b/defos/src/defos_mac.mm index bf7b2b8..a7f03cc 100644 --- a/defos/src/defos_mac.mm +++ b/defos/src/defos_mac.mm @@ -817,4 +817,35 @@ static void disable_mouse_tracking() { mouse_tracker = nil; } +#import + +static IOPMAssertionID g_assertion_id = kIOPMNullAssertionID; + +void defos_set_keep_awake(bool keep_awake) +{ + if (keep_awake && g_assertion_id == kIOPMNullAssertionID) + { + CFStringRef reason = CFSTR("Defold game active"); + IOReturn ret = IOPMAssertionCreateWithName( + kIOPMAssertionTypePreventUserIdleDisplaySleep, + kIOPMAssertionLevelOn, + reason, + &g_assertion_id); + if (ret != kIOReturnSuccess) + { + g_assertion_id = kIOPMNullAssertionID; + } + } + else if (!keep_awake && g_assertion_id != kIOPMNullAssertionID) + { + IOPMAssertionRelease(g_assertion_id); + g_assertion_id = kIOPMNullAssertionID; + } +} + +bool defos_is_keep_awake_supported() +{ + return true; +} + #endif diff --git a/defos/src/defos_private.h b/defos/src/defos_private.h index aa481e3..d487900 100644 --- a/defos/src/defos_private.h +++ b/defos/src/defos_private.h @@ -124,3 +124,6 @@ extern void defos_reset_cursor(); extern void defos_get_displays(dmArray &displayList); extern void defos_get_display_modes(DisplayID displayID, dmArray &modeList); extern DisplayID defos_get_current_display(); + +extern void defos_set_keep_awake(bool keep_awake); +extern bool defos_is_keep_awake_supported(); diff --git a/defos/src/defos_win.cpp b/defos/src/defos_win.cpp index da6cd0c..2152129 100644 --- a/defos/src/defos_win.cpp +++ b/defos/src/defos_win.cpp @@ -656,6 +656,27 @@ DisplayID defos_get_current_display() return copy_string(monitorInfo.szDevice); } +static bool g_keep_awake = false; + +void defos_set_keep_awake(bool keep_awake) +{ + if (keep_awake == g_keep_awake) { return; } + g_keep_awake = keep_awake; + if (keep_awake) + { + SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED); + } + else + { + SetThreadExecutionState(ES_CONTINUOUS); + } +} + +bool defos_is_keep_awake_supported() +{ + return true; +} + /******************** * internal functions ********************/ From 61b23dacc3858ec773b6cec8a6e0271096cefa93 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 07:16:23 +0000 Subject: [PATCH 3/5] Add toggle_keep_awake button to example demo Agent-Logs-Url: https://github.com/subsoap/defos/sessions/6c3bcf85-1f02-48c1-9fcc-c825ab80100d Co-authored-by: subsoap <409170+subsoap@users.noreply.github.com> --- example/example.gui | 35 +++++++++++++++++++++++++++++++++++ example/example.gui_script | 12 ++++++++++++ 2 files changed, 47 insertions(+) diff --git a/example/example.gui b/example/example.gui index 18fea8d..4219f15 100644 --- a/example/example.gui +++ b/example/example.gui @@ -769,6 +769,41 @@ nodes { overridden_fields: 18 template_node_child: true } +nodes { + position { + x: 635.0 + y: 350.0 + } + scale { + x: 0.75 + y: 0.75 + } + type: TYPE_TEMPLATE + id: "toggle_keep_awake" + inherit_alpha: true + template: "/dirtylarry/button.gui" +} +nodes { + type: TYPE_BOX + id: "toggle_keep_awake/larrybutton" + parent: "toggle_keep_awake" + template_node_child: true +} +nodes { + size { + x: 250.0 + y: 100.0 + } + type: TYPE_TEXT + text: "Keep screen awake" + id: "toggle_keep_awake/larrylabel" + line_break: true + parent: "toggle_keep_awake/larrybutton" + overridden_fields: 4 + overridden_fields: 8 + overridden_fields: 18 + template_node_child: true +} nodes { position { x: 525.0 diff --git a/example/example.gui_script b/example/example.gui_script index c04294f..13fa23b 100644 --- a/example/example.gui_script +++ b/example/example.gui_script @@ -95,6 +95,11 @@ function init(self) if gui.get_flipbook(gui.get_node("toggle_borderless/larrybutton")) == hash("button_pressed") then defos.toggle_borderless() end + if gui.get_flipbook(gui.get_node("toggle_keep_awake/larrybutton")) == hash("button_pressed") then + self.keep_awake = not self.keep_awake + defos.set_keep_awake(self.keep_awake) + gui.set_text(gui.get_node("toggle_keep_awake/larrylabel"), self.keep_awake and "Allow screen sleep" or "Keep screen awake") + end end) end @@ -106,6 +111,7 @@ function init(self) self.cursor_visible = true self.cursor_clipped = false self.cursor_locked = false + self.keep_awake = false msg.post(".", "acquire_input_focus") -- Console related commands only work on Windows and in bundled builds not in editor builds @@ -253,4 +259,10 @@ function on_input(self, action_id, action) defos.set_cursor(self.cursors[self.current_cursor]) end) + + dirtylarry:button("toggle_keep_awake", action_id, action, function() + self.keep_awake = not self.keep_awake + defos.set_keep_awake(self.keep_awake) + gui.set_text(gui.get_node("toggle_keep_awake/larrylabel"), self.keep_awake and "Allow screen sleep" or "Keep screen awake") + end) end From 95c59499e183a140d68946115ba0ad5c42d8a260 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 07:37:12 +0000 Subject: [PATCH 4/5] Fix toggle_keep_awake HTML5 double-toggle bug and improve wake lock error logging Agent-Logs-Url: https://github.com/subsoap/defos/sessions/fd598dad-c808-4199-9013-3a1d3a5df658 Co-authored-by: subsoap <409170+subsoap@users.noreply.github.com> --- defos/src/defos_html5.cpp | 7 +++++-- example/example.gui_script | 8 +++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/defos/src/defos_html5.cpp b/defos/src/defos_html5.cpp index 8909edf..48f04ea 100644 --- a/defos/src/defos_html5.cpp +++ b/defos/src/defos_html5.cpp @@ -420,13 +420,16 @@ void defos_set_keep_awake(bool keep_awake) { lock.addEventListener('release', function() { Module.__defosjs_wakeLock = null; }); - }).catch(function() {}); + }).catch(function(err) { + console.error('defos: wake lock request failed:', err); + }); } } else { if (Module.__defosjs_wakeLock) { Module.__defosjs_wakeLock.release().then(function() { Module.__defosjs_wakeLock = null; - }).catch(function() { + }).catch(function(err) { + console.error('defos: wake lock release failed:', err); Module.__defosjs_wakeLock = null; }); } diff --git a/example/example.gui_script b/example/example.gui_script index 13fa23b..09ce99c 100644 --- a/example/example.gui_script +++ b/example/example.gui_script @@ -261,8 +261,10 @@ function on_input(self, action_id, action) end) dirtylarry:button("toggle_keep_awake", action_id, action, function() - self.keep_awake = not self.keep_awake - defos.set_keep_awake(self.keep_awake) - gui.set_text(gui.get_node("toggle_keep_awake/larrylabel"), self.keep_awake and "Allow screen sleep" or "Keep screen awake") + if system_name ~= "HTML5" then + self.keep_awake = not self.keep_awake + defos.set_keep_awake(self.keep_awake) + gui.set_text(gui.get_node("toggle_keep_awake/larrylabel"), self.keep_awake and "Allow screen sleep" or "Keep screen awake") + end end) end From 3d816299d2a8f3837fc1c68a698db5aa79abd65e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 08:00:52 +0000 Subject: [PATCH 5/5] Apply reviewer feedback: wake lock desired-state tracking, visibility re-acquire, Linux XSS check, README fix Agent-Logs-Url: https://github.com/subsoap/defos/sessions/e1c7b7ee-6953-4abf-9d79-0387fc4c10c0 Co-authored-by: subsoap <409170+subsoap@users.noreply.github.com> --- README.md | 2 +- defos/src/defos_html5.cpp | 41 ++++++++++++++++++++++++++++++++++---- defos/src/defos_linux.cpp | 11 +++++++++- example/example.gui_script | 10 +++++++--- 4 files changed, 55 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 19aec2d..6714b0c 100644 --- a/README.md +++ b/README.md @@ -385,7 +385,7 @@ arguments = defos.get_arguments() ```lua defos.set_keep_awake(bool_value) -bool_value = defos.is_keep_awake_supported() +local supported = defos.is_keep_awake_supported() ``` Example usage with gamepad input: diff --git a/defos/src/defos_html5.cpp b/defos/src/defos_html5.cpp index 48f04ea..bd31aab 100644 --- a/defos/src/defos_html5.cpp +++ b/defos/src/defos_html5.cpp @@ -412,18 +412,51 @@ DisplayID defos_get_current_display() { void defos_set_keep_awake(bool keep_awake) { EM_ASM({ - var keep = $0; - if (keep) { - if (!Module.__defosjs_wakeLock && 'wakeLock' in navigator) { + var keep = $0 !== 0; + + // Initialize shared state and helpers once. + if (!Module.__defosjs_keepAwakeInitialized) { + Module.__defosjs_keepAwakeInitialized = true; + Module.__defosjs_keepAwakeDesired = false; + Module.__defosjs_wakeLock = null; + + Module.__defosjs_requestWakeLock = function() { + if (!Module.__defosjs_keepAwakeDesired) { return; } + if (!('wakeLock' in navigator)) { return; } + if (Module.__defosjs_wakeLock) { return; } navigator.wakeLock.request('screen').then(function(lock) { + // Check whether the desired state changed while the promise was in flight. + if (!Module.__defosjs_keepAwakeDesired) { + lock.release(); + return; + } Module.__defosjs_wakeLock = lock; lock.addEventListener('release', function() { Module.__defosjs_wakeLock = null; + // Re-acquire if still desired (e.g. browser released due to tab hide). + if (Module.__defosjs_keepAwakeDesired) { + Module.__defosjs_requestWakeLock(); + } }); }).catch(function(err) { console.error('defos: wake lock request failed:', err); }); - } + }; + + // Re-try when the document becomes visible again. + document.addEventListener('visibilitychange', function() { + if (document.visibilityState === 'visible' && + Module.__defosjs_keepAwakeDesired && + !Module.__defosjs_wakeLock) { + Module.__defosjs_requestWakeLock(); + } + }); + } + + Module.__defosjs_keepAwakeDesired = keep; + + if (keep) { + Module.__defosjs_requestWakeLock(); } else { if (Module.__defosjs_wakeLock) { Module.__defosjs_wakeLock.release().then(function() { diff --git a/defos/src/defos_linux.cpp b/defos/src/defos_linux.cpp index 7a45b0b..9c25056 100644 --- a/defos/src/defos_linux.cpp +++ b/defos/src/defos_linux.cpp @@ -44,6 +44,7 @@ static Display *disp; static int screen; static Window win; static Window root; +static bool g_xss_supported = false; // TODO: add support checking static Atom UTF8_STRING; @@ -105,6 +106,13 @@ void defos_init() is_cursor_actually_visible = true; window_has_focus = true; + // Probe for XScreenSaver extension availability. + // event/error base and version outputs are required by the API but not used beyond the check. + int xss_event_base, xss_error_base; + int xss_major = 0, xss_minor = 0; + g_xss_supported = XScreenSaverQueryExtension(disp, &xss_event_base, &xss_error_base) && + XScreenSaverQueryVersion(disp, &xss_major, &xss_minor); + current_cursor = NULL; memset(default_cursors, 0, DEFOS_CURSOR_INTMAX * sizeof(CustomCursor*)); } @@ -933,13 +941,14 @@ void defos_set_keep_awake(bool keep_awake) { if (keep_awake == g_keep_awake) { return; } g_keep_awake = keep_awake; + if (!g_xss_supported) { return; } XScreenSaverSuspend(disp, keep_awake ? True : False); XFlush(disp); } bool defos_is_keep_awake_supported() { - return true; + return g_xss_supported; } #endif diff --git a/example/example.gui_script b/example/example.gui_script index 09ce99c..fad6c44 100644 --- a/example/example.gui_script +++ b/example/example.gui_script @@ -96,9 +96,13 @@ function init(self) defos.toggle_borderless() end if gui.get_flipbook(gui.get_node("toggle_keep_awake/larrybutton")) == hash("button_pressed") then - self.keep_awake = not self.keep_awake - defos.set_keep_awake(self.keep_awake) - gui.set_text(gui.get_node("toggle_keep_awake/larrylabel"), self.keep_awake and "Allow screen sleep" or "Keep screen awake") + if defos.is_keep_awake_supported() then + self.keep_awake = not self.keep_awake + defos.set_keep_awake(self.keep_awake) + gui.set_text(gui.get_node("toggle_keep_awake/larrylabel"), self.keep_awake and "Allow screen sleep" or "Keep screen awake") + else + print("defos: Keep-awake / Wake Lock is not supported on this platform.") + end end end) end