Переглянути джерело

Merge pull request #35536 from shroudedcode/keep-screen-on-linux

Make "Keep screen on" work on Linux
Rémi Verschelde 4 роки тому
батько
коміт
a738ddb9a4

+ 3 - 3
.github/workflows/linux_builds.yml

@@ -26,7 +26,7 @@ jobs:
       - name: Configure dependencies
         run: |
           sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
-            libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm
+            libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm
 
       # Upload cache on completion and check it out now
       - name: Load .scons_cache directory
@@ -111,7 +111,7 @@ jobs:
       - name: Configure dependencies
         run: |
           sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
-            libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm \
+            libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm \
             xvfb wget unzip
 
       # Upload cache on completion and check it out now
@@ -204,7 +204,7 @@ jobs:
       - name: Configure dependencies
         run: |
           sudo apt-get install build-essential pkg-config libx11-dev libxcursor-dev \
-            libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libudev-dev libxi-dev libxrandr-dev yasm
+            libxinerama-dev libgl1-mesa-dev libglu-dev libasound2-dev libpulse-dev libdbus-1-dev libudev-dev libxi-dev libxrandr-dev yasm
 
       # Upload cache on completion and check it out now
       - name: Load .scons_cache directory

+ 1 - 0
platform/linuxbsd/SCsub

@@ -9,6 +9,7 @@ common_linuxbsd = [
     "crash_handler_linuxbsd.cpp",
     "os_linuxbsd.cpp",
     "joypad_linux.cpp",
+    "freedesktop_screensaver.cpp",
 ]
 
 if "x11" in env and env["x11"]:

+ 9 - 0
platform/linuxbsd/detect.py

@@ -72,6 +72,7 @@ def get_opts():
         BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False),
         BoolVariable("use_msan", "Use LLVM compiler memory sanitizer (MSAN)", False),
         BoolVariable("pulseaudio", "Detect and use PulseAudio", True),
+        BoolVariable("dbus", "Detect and use D-Bus to handle screensaver", True),
         BoolVariable("udev", "Use udev for gamepad connection callbacks", True),
         BoolVariable("x11", "Enable X11 display", True),
         BoolVariable("debug_symbols", "Add debugging symbols to release/release_debug builds", True),
@@ -347,6 +348,14 @@ def configure(env):
         else:
             print("PulseAudio development libraries not found, disabling driver")
 
+    if env["dbus"]:
+        if os.system("pkg-config --exists dbus-1") == 0:  # 0 means found
+            print("Enabling D-Bus")
+            env.Append(CPPDEFINES=["DBUS_ENABLED"])
+            env.ParseConfig("pkg-config --cflags --libs dbus-1")
+        else:
+            print("D-Bus development libraries not found, disabling dependent features")
+
     if platform.system() == "Linux":
         env.Append(CPPDEFINES=["JOYDEV_ENABLED"])
         if env["udev"]:

+ 32 - 0
platform/linuxbsd/display_server_x11.cpp

@@ -121,6 +121,9 @@ bool DisplayServerX11::has_feature(Feature p_feature) const {
 		case FEATURE_ICON:
 		case FEATURE_NATIVE_ICON:
 		case FEATURE_SWAP_BUFFERS:
+#ifdef DBUS_ENABLED
+		case FEATURE_KEEP_SCREEN_ON:
+#endif
 			return true;
 		default: {
 		}
@@ -822,6 +825,26 @@ bool DisplayServerX11::screen_is_touchscreen(int p_screen) const {
 	return DisplayServer::screen_is_touchscreen(p_screen);
 }
 
+#ifdef DBUS_ENABLED
+void DisplayServerX11::screen_set_keep_on(bool p_enable) {
+	if (screen_is_kept_on() == p_enable) {
+		return;
+	}
+
+	if (p_enable) {
+		screensaver->inhibit();
+	} else {
+		screensaver->uninhibit();
+	}
+
+	keep_screen_on = p_enable;
+}
+
+bool DisplayServerX11::screen_is_kept_on() const {
+	return keep_screen_on;
+}
+#endif
+
 Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const {
 	_THREAD_SAFE_METHOD_
 
@@ -4270,6 +4293,11 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
 
 	_update_real_mouse_position(windows[MAIN_WINDOW_ID]);
 
+#ifdef DBUS_ENABLED
+	screensaver = memnew(FreeDesktopScreenSaver);
+	screen_set_keep_on(GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true));
+#endif
+
 	r_error = OK;
 }
 
@@ -4334,6 +4362,10 @@ DisplayServerX11::~DisplayServerX11() {
 	if (xmbstring) {
 		memfree(xmbstring);
 	}
+
+#ifdef DBUS_ENABLED
+	memdelete(screensaver);
+#endif
 }
 
 void DisplayServerX11::register_x11_driver() {

+ 14 - 0
platform/linuxbsd/display_server_x11.h

@@ -55,6 +55,10 @@
 #include "platform/linuxbsd/vulkan_context_x11.h"
 #endif
 
+#if defined(DBUS_ENABLED)
+#include "freedesktop_screensaver.h"
+#endif
+
 #include <X11/Xcursor/Xcursor.h>
 #include <X11/Xlib.h>
 #include <X11/extensions/XInput2.h>
@@ -103,6 +107,11 @@ class DisplayServerX11 : public DisplayServer {
 	RenderingDeviceVulkan *rendering_device_vulkan;
 #endif
 
+#if defined(DBUS_ENABLED)
+	FreeDesktopScreenSaver *screensaver;
+	bool keep_screen_on = false;
+#endif
+
 	struct WindowData {
 		Window x11_window;
 		::XIC xic;
@@ -291,6 +300,11 @@ public:
 	virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
 	virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
 
+#if defined(DBUS_ENABLED)
+	virtual void screen_set_keep_on(bool p_enable);
+	virtual bool screen_is_kept_on() const;
+#endif
+
 	virtual Vector<DisplayServer::WindowID> get_window_list() const;
 
 	virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i());

+ 124 - 0
platform/linuxbsd/freedesktop_screensaver.cpp

@@ -0,0 +1,124 @@
+/*************************************************************************/
+/*  freedesktop_screensaver.cpp                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "freedesktop_screensaver.h"
+
+#ifdef DBUS_ENABLED
+
+#include "core/config/project_settings.h"
+
+#include <dbus/dbus.h>
+
+#define BUS_OBJECT_NAME "org.freedesktop.ScreenSaver"
+#define BUS_OBJECT_PATH "/org/freedesktop/ScreenSaver"
+#define BUS_INTERFACE "org.freedesktop.ScreenSaver"
+
+void FreeDesktopScreenSaver::inhibit() {
+	if (unsupported) {
+		return;
+	}
+
+	DBusError error;
+	dbus_error_init(&error);
+
+	DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
+	if (dbus_error_is_set(&error)) {
+		unsupported = true;
+		return;
+	}
+
+	String app_name_string = ProjectSettings::get_singleton()->get("application/config/name");
+	const char *app_name = app_name_string.is_empty() ? "Godot Engine" : app_name_string.utf8().get_data();
+
+	const char *reason = "Running Godot Engine project";
+
+	DBusMessage *message = dbus_message_new_method_call(
+			BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE,
+			"Inhibit");
+	dbus_message_append_args(
+			message,
+			DBUS_TYPE_STRING, &app_name,
+			DBUS_TYPE_STRING, &reason,
+			DBUS_TYPE_INVALID);
+
+	DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
+	dbus_message_unref(message);
+	if (dbus_error_is_set(&error)) {
+		dbus_connection_unref(bus);
+		unsupported = false;
+		return;
+	}
+
+	DBusMessageIter reply_iter;
+	dbus_message_iter_init(reply, &reply_iter);
+	dbus_message_iter_get_basic(&reply_iter, &cookie);
+	print_verbose("FreeDesktopScreenSaver: Acquired screensaver inhibition cookie: " + uitos(cookie));
+
+	dbus_message_unref(reply);
+	dbus_connection_unref(bus);
+}
+
+void FreeDesktopScreenSaver::uninhibit() {
+	if (unsupported) {
+		return;
+	}
+
+	DBusError error;
+	dbus_error_init(&error);
+
+	DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
+	if (dbus_error_is_set(&error)) {
+		unsupported = true;
+		return;
+	}
+
+	DBusMessage *message = dbus_message_new_method_call(
+			BUS_OBJECT_NAME, BUS_OBJECT_PATH, BUS_INTERFACE,
+			"UnInhibit");
+	dbus_message_append_args(
+			message,
+			DBUS_TYPE_UINT32, &cookie,
+			DBUS_TYPE_INVALID);
+
+	DBusMessage *reply = dbus_connection_send_with_reply_and_block(bus, message, 50, &error);
+	if (dbus_error_is_set(&error)) {
+		dbus_connection_unref(bus);
+		unsupported = true;
+		return;
+	}
+
+	print_verbose("FreeDesktopScreenSaver: Released screensaver inhibition cookie: " + uitos(cookie));
+
+	dbus_message_unref(message);
+	dbus_message_unref(reply);
+	dbus_connection_unref(bus);
+}
+
+#endif // DBUS_ENABLED

+ 47 - 0
platform/linuxbsd/freedesktop_screensaver.h

@@ -0,0 +1,47 @@
+/*************************************************************************/
+/*  freedesktop_screensaver.h                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifdef DBUS_ENABLED
+
+#include <dbus/dbus.h>
+#include <stdint.h>
+
+class FreeDesktopScreenSaver {
+private:
+	uint32_t cookie = 0;
+	bool unsupported = false;
+
+public:
+	FreeDesktopScreenSaver() {}
+	void inhibit();
+	void uninhibit();
+};
+
+#endif // DBUS_ENABLED