Skip to content
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
3 changes: 0 additions & 3 deletions rendercanvas/core/coreutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,6 @@ def close_agen(agen):
SYSTEM_IS_WAYLAND = "wayland" in os.getenv("XDG_SESSION_TYPE", "").lower()

if sys.platform.startswith("linux") and SYSTEM_IS_WAYLAND:
# Force glfw to use X11. Note that this does not work if glfw is already imported.
if "glfw" not in sys.modules:
os.environ["PYGLFW_LIBRARY_VARIANT"] = "x11"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change means that with default glfw Python lib installed, on Wayland, the window has no decorations, which would be a regression.

You mentioned the libglfw3 system package. How do you tell pyGLFW to use that system libary, or does that happen automatically?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The glfw package should choose the appropriate library automatically according to its documentation, although you can force it by setting PYGLFW_LIBRARY_VARIANT. It will also automatically determine which specific .so file to load upon import (on my system, it actually prefers the system library over the locally installed one for some reason). You can also optionally force a specific .so with PYGLFW_LIBRARY.

I've just tested it on my system again, and the window shows the correct decorations in both cases. For safety, I forced the .so to the bundled one in site-packages/glfw/<wayland/x11>/libglfw.so.

Wayland:
glfw_wayland

X11:
glfw_x11

The missing decoration might be an underlying issue with your Wayland compositor. Decorations on Wayland are a frustrating mess at the moment because windows are responsible for drawing their own decorations (server-side decorations like on X11 are not available unless you force the applications into Xwayland), and each implementation does it differently, especially GNOME. This is also the reason why my Wayland window has a different decoration in the screenshot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did apt install libglfw3, but glfw prefers the packaged lib, because it's a newer version (3.4 vs 3.3.10 for libglfw.so.3).

When I force it to use the system glfw, the decorations are ok, but with the packaged glfw the decorations are not there. This is on Ubuntu 24.04 LTS. I think we'll need to force x11 until the lib from glfw itself works properly on Wayland.

My proposal would be to bring back the setting of PYGLFW_LIBRARY_VARIANT, but only if it's not already set and if PYGLFW_LIBRARY is not set. That way we still fall back, but make it easier for users to disable the fallback.

# Force Qt to use X11. Qt is more flexible - it ok if e.g. PySide6 is already imported.
os.environ["QT_QPA_PLATFORM"] = "xcb"
# Force wx to use X11, probably.
Expand Down
43 changes: 28 additions & 15 deletions rendercanvas/glfw.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,20 @@

import sys
import time
import warnings

import glfw

from .base import BaseRenderCanvas, BaseCanvasGroup
from .asyncio import loop
from .core.coreutils import SYSTEM_IS_WAYLAND, weakbind, logger
from .core.coreutils import weakbind


# Make sure that glfw is new enough
glfw_version_info = tuple(int(i) for i in glfw.__version__.split(".")[:2])
if glfw_version_info < (1, 9):
raise ImportError("rendercanvas requires glfw 1.9 or higher.")

# Do checks to prevent pitfalls on hybrid Xorg/Wayland systems
api_is_wayland = False
if sys.platform.startswith("linux") and SYSTEM_IS_WAYLAND:
if not hasattr(glfw, "get_x11_window"):
# Probably glfw was imported before this module, so we missed our chance
# to set the env var to make glfw use x11.
api_is_wayland = True
logger.warning("Using GLFW with Wayland, which is experimental.")


# Some glfw functions are not always available
set_window_content_scale_callback = lambda *args: None
set_window_maximize_callback = lambda *args: None
Expand Down Expand Up @@ -135,22 +126,44 @@ def get_glfw_present_info(window):
}

elif sys.platform.startswith("linux"):
if api_is_wayland:
# When the respective platform (X11 or Wayland) is not initialized, glfw emits a GLFWError warning and returns None. We treat None as "backend not active" here so that the warning carries no actionable information for the caller.
with warnings.catch_warnings():
warnings.simplefilter("ignore", glfw.GLFWError)
wayland_display = (
glfw.get_wayland_display()
if hasattr(glfw, "get_wayland_display")
else None
Comment thread
almarklein marked this conversation as resolved.
)

if wayland_display is not None:
return {
"method": "screen",
"platform": "wayland",
"window": int(glfw.get_wayland_window(window)),
"display": int(glfw.get_wayland_display()),
"display": int(wayland_display),
}

else:
# fall back to X11 if obtaining a wayland window handle fails (e.g. if Wayland is not available)
with warnings.catch_warnings():
warnings.simplefilter("ignore", glfw.GLFWError)
x11_display = (
glfw.get_x11_display() if hasattr(glfw, "get_x11_display") else None
)
if x11_display is not None:
return {
"method": "screen",
"platform": "x11",
"window": int(glfw.get_x11_window(window)),
"display": int(glfw.get_x11_display()),
"display": int(x11_display),
}

raise RuntimeError(
"Cannot get GLFW surface info on Linux: neither a Wayland nor "
"an X11 display handle is available. "
"Make sure a display server is running and the matching glfw "
"library variant is installed."
)

else:
raise RuntimeError(f"Cannot get GLFW surface info on {sys.platform}.")

Expand Down
Loading