Skip to content

Commit

Permalink
Add Wayland support
Browse files Browse the repository at this point in the history
Not everything is yet implemented, either for Godot or personal
limitations (I don't have all hardware in the world). A brief list of
the most important issues follows:

- Single-window only: the `DisplayServer` API doesn't expose enough
information for properly creating XDG shell windows.

- Very dumb rendering loop: this is very complicated, just know that
the low consumption mode is forced to 2000 Hz and some clever hacks are
in place to overcome a specific Wayland limitation. This will be
improved to the extent possible both downstream and upstream.

- Features to implement yet: IME, touch input, native file dialog,
drawing tablet (commented out due to a refactor), screen recording.

- Mouse passthrough can't be implement through a poly API, we need a
rect-based one.

- The cursor doesn't yet support fractional scaling.

- Auto scale is rounded up when using fractional scaling as we don't
have a per-window scale query API (basically we need
`DisplayServer::window_get_scale`).

- Building with `x11=no wayland=yes opengl=yes openxr=yes` fails.

This also adds a new project property and editor setting for selecting the
default DisplayServer to start, to allow this backend to start first in
exported projects (X11 is still the default for now). The editor setting
always overrides the project setting.

Special thanks to Drew Devault, toger5, Sebastian Krzyszkowiak, Leandro
Benedet Garcia, Subhransu, Yury Zhuravlev and Mara Huldra.
  • Loading branch information
Riteo committed Jan 30, 2024
1 parent 51991e2 commit 7e0f7d3
Show file tree
Hide file tree
Showing 71 changed files with 25,085 additions and 80 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/linux_builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ jobs:
sudo add-apt-repository "deb https://ppa.launchpadcontent.net/kisak/turtle/ubuntu focal main"
sudo apt-get install -qq mesa-vulkan-drivers
# TODO: Figure out somehow how to embed this one.
- name: wayland-scanner dependency
run: |
sudo apt-get install libwayland-bin
- name: Free disk space on runner
run: |
echo "Disk usage before:" && df -h
Expand Down
20 changes: 20 additions & 0 deletions COPYRIGHT.txt
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,26 @@ Comment: Vulkan Memory Allocator
Copyright: 2017-2021, Advanced Micro Devices, Inc.
License: Expat

Files: ./thirdparty/wayland/
Comment: Wayland core protocol
Copyright: 2008-2012, Kristian Høgsberg
2010-2012, Intel Corporation
2011, Benjamin Franzke
2012, Collabora, Ltd.
License: Expat

Files: ./thirdparty/wayland-protocols/
Comment: Wayland protocols that add functionality not available in the core protocol
Copyright: 2008-2013, Kristian Høgsberg
2010-2013, Intel Corporation
2013, Rafael Antognolli
2013, Jasper St. Pierre
2014, Jonas Ådahl
2014, Jason Ekstrand
2014-2015, Collabora, Ltd.
2015, Red Hat Inc.
License: Expat

Files: ./thirdparty/wslay/
Comment: Wslay
Copyright: 2011, 2012, 2015, Tatsuhiro Tsujikawa
Expand Down
88 changes: 47 additions & 41 deletions doc/classes/DisplayServer.xml

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions doc/classes/EditorSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,9 @@
<member name="run/output/font_size" type="int" setter="" getter="">
The size of the font in the [b]Output[/b] panel at the bottom of the editor. This setting does not impact the font size of the script editor (see [member interface/editor/code_font_size]).
</member>
<member name="run/platforms/linuxbsd/prefer_wayland" type="bool" setter="" getter="">
If [code]true[/code], on Linux/BSD, the editor will check for Wayland first instead of X11 (if available).
</member>
<member name="run/window_placement/android_window" type="int" setter="" getter="">
The Android window to display the project on when starting the project from the editor.
[b]Note:[/b] Only available in the Android editor.
Expand Down
18 changes: 18 additions & 0 deletions doc/classes/ProjectSettings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,24 @@
<member name="debug/shapes/paths/geometry_width" type="float" setter="" getter="" default="2.0">
Line width of the curve path geometry, visible when "Visible Paths" is enabled in the Debug menu.
</member>
<member name="display/display_server/driver" type="String" setter="" getter="">
Sets the driver to be used by the display server. This property can not be edited directly, instead, set the driver using the platform-specific overrides.
</member>
<member name="display/display_server/driver.android" type="String" setter="" getter="">
Android override for [member display/display_server/driver].
</member>
<member name="display/display_server/driver.ios" type="String" setter="" getter="">
iOS override for [member display/display_server/driver].
</member>
<member name="display/display_server/driver.linuxbsd" type="String" setter="" getter="">
LinuxBSD override for [member display/display_server/driver].
</member>
<member name="display/display_server/driver.macos" type="String" setter="" getter="">
MacOS override for [member display/display_server/driver].
</member>
<member name="display/display_server/driver.windows" type="String" setter="" getter="">
Windows override for [member display/display_server/driver].
</member>
<member name="display/mouse_cursor/custom_image" type="String" setter="" getter="" default="&quot;&quot;">
Custom image for the mouse cursor (limited to 256×256).
</member>
Expand Down
7 changes: 5 additions & 2 deletions drivers/vulkan/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ if env["platform"] == "android":
env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_ANDROID_KHR"])
elif env["platform"] == "ios":
env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_IOS_MVK"])
elif env["platform"] == "linuxbsd" and env["x11"]:
env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_XLIB_KHR"])
elif env["platform"] == "linuxbsd":
if env["x11"]:
env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_XLIB_KHR"])
if env["wayland"]:
env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_WAYLAND_KHR"])
elif env["platform"] == "macos":
env.AppendUnique(CPPDEFINES=["VK_USE_PLATFORM_MACOS_MVK"])
elif env["platform"] == "windows":
Expand Down
10 changes: 10 additions & 0 deletions editor/editor_settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("run/output/always_open_output_on_play", true);
_initial_set("run/output/always_close_output_on_stop", false);

// Platform
_initial_set("run/platforms/linuxbsd/prefer_wayland", false);
set_restart_if_changed("run/platforms/linuxbsd/prefer_wayland", true);

/* Network */

// Debug
Expand Down Expand Up @@ -1419,6 +1423,12 @@ String EditorSettings::get_editor_layouts_config() const {
}

float EditorSettings::get_auto_display_scale() const {
#ifdef LINUXBSD_ENABLED
if (DisplayServer::get_singleton()->get_name() == "Wayland") {
return DisplayServer::get_singleton()->screen_get_max_scale();
}
#endif

#if defined(MACOS_ENABLED) || defined(ANDROID_ENABLED)
return DisplayServer::get_singleton()->screen_get_max_scale();
#else
Expand Down
96 changes: 67 additions & 29 deletions main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,12 @@ static bool _start_success = false;

// Drivers

String display_driver = "";
String tablet_driver = "";
String text_driver = "";
String rendering_driver = "";
String rendering_method = "";
static int text_driver_idx = -1;
static int display_driver_idx = -1;
static int audio_driver_idx = -1;

// Engine config/tools
Expand Down Expand Up @@ -824,7 +824,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
I = I->next();
}

String display_driver = "";
String audio_driver = "";
String project_path = ".";
bool upwards = false;
Expand Down Expand Up @@ -2122,23 +2121,13 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
// Display driver, e.g. X11, Wayland.
// Make sure that headless is the last one, which it is assumed to be by design.
DEV_ASSERT(NULL_DISPLAY_DRIVER == DisplayServer::get_create_function_name(DisplayServer::get_create_function_count() - 1));
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
String name = DisplayServer::get_create_function_name(i);
if (display_driver == name) {
display_driver_idx = i;
break;
}
}

if (display_driver_idx < 0) {
// If the requested driver wasn't found, pick the first entry.
// If all else failed it would be the headless server.
display_driver_idx = 0;
}

// Store this in a globally accessible place, so we can retrieve the rendering drivers
// list from the display driver for the editor UI.
OS::get_singleton()->set_display_driver_id(display_driver_idx);
GLOBAL_DEF_RST_NOVAL("display/display_server/driver", "default");
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "display/display_server/driver.windows", PROPERTY_HINT_ENUM_SUGGESTION, "default,windows,headless"), "default");
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "display/display_server/driver.linuxbsd", PROPERTY_HINT_ENUM_SUGGESTION, "default,x11,wayland,headless"), "default");
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "display/display_server/driver.android", PROPERTY_HINT_ENUM_SUGGESTION, "default,android,headless"), "default");
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "display/display_server/driver.ios", PROPERTY_HINT_ENUM_SUGGESTION, "default,iOS,headless"), "default");
GLOBAL_DEF_RST_NOVAL(PropertyInfo(Variant::STRING, "display/display_server/driver.macos", PROPERTY_HINT_ENUM_SUGGESTION, "default,macos,headless"), "default");

GLOBAL_DEF_RST_NOVAL("audio/driver/driver", AudioDriverManager::get_driver(0)->get_name());
if (audio_driver.is_empty()) { // Specified in project.godot.
Expand Down Expand Up @@ -2382,7 +2371,26 @@ Error Main::setup2() {
rp_new.ext_func = _parse_resource_dummy;
rp_new.sub_func = _parse_resource_dummy;

while (true) {
bool screen_found = false;
String screen_property;

bool prefer_wayland_found = false;

if (editor) {
screen_property = "interface/editor/editor_screen";
} else if (project_manager) {
screen_property = "interface/editor/project_manager_screen";
} else {
// Skip.
screen_found = true;
}

if (!display_driver.is_empty()) {
// Skip.
prefer_wayland_found = true;
}

while (!screen_found || !prefer_wayland_found) {
assign = Variant();
next_tag.fields.clear();
next_tag.name = String();
Expand All @@ -2391,17 +2399,21 @@ Error Main::setup2() {
if (err == ERR_FILE_EOF) {
break;
}

if (err == OK && !assign.is_empty()) {
if (project_manager) {
if (assign == "interface/editor/project_manager_screen") {
init_screen = value;
break;
}
} else if (editor) {
if (assign == "interface/editor/editor_screen") {
init_screen = value;
break;
if (!screen_found && assign == screen_property) {
init_screen = value;
screen_found = true;
}

if (!prefer_wayland_found && assign == "run/platforms/linuxbsd/prefer_wayland") {
if (value) {
display_driver = "wayland";
} else {
display_driver = "default";
}

prefer_wayland_found = true;
}
}
}
Expand Down Expand Up @@ -2459,7 +2471,33 @@ Error Main::setup2() {
{
OS::get_singleton()->benchmark_begin_measure("Servers", "Display");

String display_driver = DisplayServer::get_create_function_name(display_driver_idx);
if (display_driver.is_empty()) {
display_driver = GLOBAL_GET("display/display_server/driver");
}

int display_driver_idx = -1;

if (display_driver.is_empty() || display_driver == "default") {
display_driver_idx = 0;
} else {
for (int i = 0; i < DisplayServer::get_create_function_count(); i++) {
String name = DisplayServer::get_create_function_name(i);
if (display_driver == name) {
display_driver_idx = i;
break;
}
}

if (display_driver_idx < 0) {
// If the requested driver wasn't found, pick the first entry.
// If all else failed it would be the headless server.
display_driver_idx = 0;
}
}

// Store this in a globally accessible place, so we can retrieve the rendering drivers
// list from the display driver for the editor UI.
OS::get_singleton()->set_display_driver_id(display_driver_idx);

Vector2i *window_position = nullptr;
Vector2i position = init_custom_pos;
Expand Down
3 changes: 3 additions & 0 deletions modules/openxr/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ elif env["platform"] == "linuxbsd":
if env["x11"]:
env_openxr.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_XLIB"])

if env["wayland"]:
env_openxr.AppendUnique(CPPDEFINES=["XR_USE_PLATFORM_WAYLAND"])

# FIXME: Review what needs to be set for Android and macOS.
env_openxr.AppendUnique(CPPDEFINES=["HAVE_SECURE_GETENV"])
elif env["platform"] == "windows":
Expand Down
12 changes: 8 additions & 4 deletions modules/openxr/extensions/platform/openxr_opengl_extension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ bool OpenXROpenGLExtension::check_graphics_api_support(XrVersion p_desired_versi

#ifdef WIN32
XrGraphicsBindingOpenGLWin32KHR OpenXROpenGLExtension::graphics_binding_gl;
#elif ANDROID_ENABLED
#elif defined(ANDROID_ENABLED)
XrGraphicsBindingOpenGLESAndroidKHR OpenXROpenGLExtension::graphics_binding_gl;
#else
#elif defined(X11_ENABLED)
XrGraphicsBindingOpenGLXlibKHR OpenXROpenGLExtension::graphics_binding_gl;
#endif

Expand All @@ -147,20 +147,24 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex

DisplayServer *display_server = DisplayServer::get_singleton();

#ifdef WAYLAND_ENABLED
ERR_FAIL_COND_V_MSG(display_server->get_name() == "Wayland", p_next_pointer, "OpenXR is not yet supported on OpenGL Wayland.");
#endif

#ifdef WIN32
graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_WIN32_KHR,
graphics_binding_gl.next = p_next_pointer;

graphics_binding_gl.hDC = (HDC)display_server->window_get_native_handle(DisplayServer::WINDOW_VIEW);
graphics_binding_gl.hGLRC = (HGLRC)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
#elif ANDROID_ENABLED
#elif defined(ANDROID_ENABLED)
graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_ES_ANDROID_KHR;
graphics_binding_gl.next = p_next_pointer;

graphics_binding_gl.display = (void *)display_server->window_get_native_handle(DisplayServer::DISPLAY_HANDLE);
graphics_binding_gl.config = (EGLConfig)0; // https://github.com/KhronosGroup/OpenXR-SDK-Source/blob/master/src/tests/hello_xr/graphicsplugin_opengles.cpp#L122
graphics_binding_gl.context = (void *)display_server->window_get_native_handle(DisplayServer::OPENGL_CONTEXT);
#else
#elif defined(X11_ENABLED)
graphics_binding_gl.type = XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR;
graphics_binding_gl.next = p_next_pointer;

Expand Down
3 changes: 3 additions & 0 deletions platform/linuxbsd/SCsub
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ if env["use_sowrap"]:
if env["x11"]:
common_linuxbsd += SConscript("x11/SCsub")

if env["wayland"]:
common_linuxbsd += SConscript("wayland/SCsub")

if env["speechd"]:
common_linuxbsd.append("tts_linux.cpp")
if env["use_sowrap"]:
Expand Down
44 changes: 41 additions & 3 deletions platform/linuxbsd/detect.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ def get_opts():
BoolVariable("fontconfig", "Use fontconfig for system fonts support", True),
BoolVariable("udev", "Use udev for gamepad connection callbacks", True),
BoolVariable("x11", "Enable X11 display", True),
BoolVariable("wayland", "Enable Wayland display", True),
BoolVariable("libdecor", "Enable libdecor support", True),
BoolVariable("touch", "Enable touch events", True),
BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False),
]
Expand Down Expand Up @@ -204,6 +206,11 @@ def configure(env: "Environment"):
if env["use_sowrap"]:
env.Append(CPPDEFINES=["SOWRAP_ENABLED"])

if env["wayland"]:
if os.system("wayland-scanner -v") != 0:
print("wayland-scanner not found. Aborting.")
sys.exit(255)

if env["touch"]:
env.Append(CPPDEFINES=["TOUCH_ENABLED"])

Expand Down Expand Up @@ -364,9 +371,13 @@ def configure(env: "Environment"):
env.ParseConfig("pkg-config xkbcommon --cflags --libs")
env.Append(CPPDEFINES=["XKB_ENABLED"])
else:
print(
"Warning: libxkbcommon development libraries not found. Disabling dead key composition and key label support."
)
if env["wayland"]:
print("Error: libxkbcommon development libraries required by Wayland not found. Aborting.")
sys.exit(255)
else:
print(
"Warning: libxkbcommon development libraries not found. Disabling dead key composition and key label support."
)
else:
env.Append(CPPDEFINES=["XKB_ENABLED"])

Expand Down Expand Up @@ -433,6 +444,33 @@ def configure(env: "Environment"):
env.ParseConfig("pkg-config xi --cflags --libs")
env.Append(CPPDEFINES=["X11_ENABLED"])

if env["wayland"]:
if not env["use_sowrap"]:
if os.system("pkg-config --exists libdecor-0"):
print("Warning: libdecor development libraries not found. Disabling client-side decorations.")
env["libdecor"] = False
else:
env.ParseConfig("pkg-config libdecor-0 --cflags --libs")
if os.system("pkg-config --exists wayland-client"):
print("Error: Wayland client library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config wayland-client --cflags --libs")
if os.system("pkg-config --exists wayland-cursor"):
print("Error: Wayland cursor library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config wayland-cursor --cflags --libs")
if os.system("pkg-config --exists wayland-egl"):
print("Error: Wayland EGL library not found. Aborting.")
sys.exit(255)
env.ParseConfig("pkg-config wayland-egl --cflags --libs")

if env["libdecor"]:
env.Append(CPPDEFINES=["LIBDECOR_ENABLED"])

env.Prepend(CPPPATH=["#platform/linuxbsd", "#thirdparty/linuxbsd_headers/wayland/"])
env.Append(CPPDEFINES=["WAYLAND_ENABLED"])
env.Append(LIBS=["rt"]) # Needed by glibc, used by _allocate_shm_file

if env["vulkan"]:
env.Append(CPPDEFINES=["VULKAN_ENABLED", "RD_ENABLED"])
if not env["use_volk"]:
Expand Down
2 changes: 1 addition & 1 deletion platform/linuxbsd/export/export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void register_linuxbsd_exporter_types() {
void register_linuxbsd_exporter() {
Ref<EditorExportPlatformLinuxBSD> platform;
platform.instantiate();
platform->set_name("Linux/X11");
platform->set_name("Linux");
platform->set_os_name("Linux");
platform->set_chmod_flags(0755);

Expand Down
Loading

0 comments on commit 7e0f7d3

Please sign in to comment.