diff options
| author | 3gg <3gg@shellblade.net> | 2025-12-27 12:03:39 -0800 |
|---|---|---|
| committer | 3gg <3gg@shellblade.net> | 2025-12-27 12:03:39 -0800 |
| commit | 5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch) | |
| tree | 8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/docs/README-wayland.md | |
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/docs/README-wayland.md')
| -rw-r--r-- | contrib/SDL-3.2.8/docs/README-wayland.md | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/docs/README-wayland.md b/contrib/SDL-3.2.8/docs/README-wayland.md new file mode 100644 index 0000000..557a813 --- /dev/null +++ b/contrib/SDL-3.2.8/docs/README-wayland.md | |||
| @@ -0,0 +1,238 @@ | |||
| 1 | Wayland | ||
| 2 | ======= | ||
| 3 | Wayland is a replacement for the X11 window system protocol and architecture and is favored over X11 by default in SDL3 | ||
| 4 | for communicating with desktop compositors. It works well for the majority of applications, however, applications may | ||
| 5 | encounter limitations or behavior that is different from other windowing systems. | ||
| 6 | |||
| 7 | ## Common issues: | ||
| 8 | |||
| 9 | ### Legacy, DPI-unaware applications are blurry | ||
| 10 | |||
| 11 | - Wayland handles high-DPI displays by scaling the desktop, which causes applications that are not designed to be | ||
| 12 | DPI-aware to be automatically scaled by the window manager, which results in them being blurry. SDL can _attempt_ to | ||
| 13 | scale these applications such that they will be output with a 1:1 pixel aspect, however this may be buggy, especially | ||
| 14 | with odd-sized windows and/or scale factors that aren't quarter-increments (125%, 150%, etc...). To enable this, set | ||
| 15 | the environment variable `SDL_VIDEO_WAYLAND_SCALE_TO_DISPLAY=1` | ||
| 16 | |||
| 17 | ### Window decorations are missing, or the decorations look strange | ||
| 18 | |||
| 19 | - On some desktops (i.e. GNOME), Wayland applications use a library | ||
| 20 | called [libdecor](https://gitlab.freedesktop.org/libdecor/libdecor) to provide window decorations. If this library is | ||
| 21 | not installed, the decorations will be missing. This library uses plugins to generate different decoration styles, and | ||
| 22 | if a plugin to generate native-looking decorations is not installed (i.e. the GTK plugin), the decorations will not | ||
| 23 | appear to be 'native'. | ||
| 24 | |||
| 25 | ### Windows do not appear immediately after creation | ||
| 26 | |||
| 27 | - Wayland requires that the application initially present a buffer before the window becomes visible. Additionally, | ||
| 28 | applications _must_ have an event loop and processes messages on a regular basis, or the application can appear | ||
| 29 | unresponsive to both the user and desktop compositor. | ||
| 30 | |||
| 31 | ### The display reported as the primary by ```SDL_GetPrimaryDisplay()``` is incorrect | ||
| 32 | |||
| 33 | - Wayland doesn't natively have the concept of a primary display, so SDL attempts to determine it by querying various | ||
| 34 | system settings, and falling back to a selection algorithm if this fails. If it is incorrect, it can be manually | ||
| 35 | overridden by setting the ```SDL_VIDEO_DISPLAY_PRIORITY``` hint. | ||
| 36 | |||
| 37 | ### ```SDL_SetWindowPosition()``` doesn't work on non-popup windows | ||
| 38 | |||
| 39 | - Wayland does not allow toplevel windows to position themselves programmatically. | ||
| 40 | |||
| 41 | ### Retrieving the global mouse cursor position when the cursor is outside a window doesn't work | ||
| 42 | |||
| 43 | - Wayland only provides applications with the cursor position within the borders of the application windows. Querying | ||
| 44 | the global position when an application window does not have mouse focus returns 0,0 as the actual cursor position is | ||
| 45 | unknown. In most cases, applications don't actually need the global cursor position and should use the window-relative | ||
| 46 | coordinates as provided by the mouse movement event or from ```SDL_GetMouseState()``` instead. | ||
| 47 | |||
| 48 | ### Warping the mouse cursor to or from a point outside the window doesn't work | ||
| 49 | |||
| 50 | - The cursor can be warped only within the window with mouse focus, provided that the `zwp_pointer_confinement_v1` | ||
| 51 | protocol is supported by the compositor. | ||
| 52 | |||
| 53 | ### The application icon can't be set via ```SDL_SetWindowIcon()``` | ||
| 54 | |||
| 55 | - Wayland requires compositor support for the `xdg-toplevel-icon-v1` protocol to set window icons programmatically. | ||
| 56 | Otherwise, the launcher icon from the associated desktop entry file, aka a `.desktop` file, will typically be used. | ||
| 57 | Please see the [Desktop Entry Specification](https://specifications.freedesktop.org/desktop-entry-spec/latest/) for | ||
| 58 | more information on the format of this file. Note that if your application manually sets the application ID via the | ||
| 59 | `SDL_APP_ID` hint string, the desktop entry file name should match the application ID. For example, if your | ||
| 60 | application ID is set to `org.my_org.sdl_app`, the desktop entry file should be named `org.my_org.sdl_app.desktop`. | ||
| 61 | |||
| 62 | ## Using custom Wayland windowing protocols with SDL windows | ||
| 63 | |||
| 64 | Under normal operation, an `SDL_Window` corresponds to an XDG toplevel window, which provides a standard desktop window. | ||
| 65 | If an application wishes to use a different windowing protocol with an SDL window (e.g. wlr_layer_shell) while still | ||
| 66 | having SDL handle input and rendering, it needs to create a custom, roleless surface and attach that surface to its own | ||
| 67 | toplevel window. | ||
| 68 | |||
| 69 | This is done by using `SDL_CreateWindowWithProperties()` and setting the | ||
| 70 | `SDL_PROP_WINDOW_CREATE_WAYLAND_SURFACE_ROLE_CUSTOM_BOOLEAN` property to `true`. Once the window has been | ||
| 71 | successfully created, the `wl_display` and `wl_surface` objects can then be retrieved from the | ||
| 72 | `SDL_PROP_WINDOW_WAYLAND_DISPLAY_POINTER` and `SDL_PROP_WINDOW_WAYLAND_SURFACE_POINTER` properties respectively. | ||
| 73 | |||
| 74 | Surfaces don't receive any size change notifications, so if an application changes the window size, it must inform SDL | ||
| 75 | that the surface size has changed by calling SDL_SetWindowSize() with the new dimensions. | ||
| 76 | |||
| 77 | Custom surfaces will automatically handle scaling internally if the window was created with the | ||
| 78 | `SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN` property set to `true`. In this case, applications should | ||
| 79 | not manually attach viewports or change the surface scale value, as SDL will handle this internally. Calls | ||
| 80 | to `SDL_SetWindowSize()` should use the logical size of the window, and `SDL_GetWindowSizeInPixels()` should be used to | ||
| 81 | query the size of the backbuffer surface in pixels. If this property is not set or is `false`, applications can | ||
| 82 | attach their own viewports or change the surface scale manually, and the SDL backend will not interfere or change any | ||
| 83 | values internally. In this case, calls to `SDL_SetWindowSize()` should pass the requested surface size in pixels, not | ||
| 84 | the logical window size, as no scaling calculations will be done internally. | ||
| 85 | |||
| 86 | All window functions that control window state aside from `SDL_SetWindowSize()` are no-ops with custom surfaces. | ||
| 87 | |||
| 88 | Please see the minimal example in `tests/testwaylandcustom.c` for an example of how to use a custom, roleless surface | ||
| 89 | and attach it to an application-managed toplevel window. | ||
| 90 | |||
| 91 | ## Importing external surfaces into SDL windows | ||
| 92 | |||
| 93 | Wayland windows and surfaces are more intrinsically tied to the client library than other windowing systems, therefore, | ||
| 94 | when importing surfaces, it is necessary for both SDL and the application or toolkit to use the same `wl_display` | ||
| 95 | object. This can be set/queried via the global `SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER` property. To | ||
| 96 | import an external `wl_display`, set this property before initializing the SDL video subsystem, and read the value to | ||
| 97 | export the internal `wl_display` after the video subsystem has been initialized. Setting this property after the video | ||
| 98 | subsystem has been initialized has no effect, and reading it when the video subsystem is uninitialized will either | ||
| 99 | return the user provided value, if one was set while in the uninitialized state, or NULL. | ||
| 100 | |||
| 101 | Once this is done, and the application has created or obtained the `wl_surface` to be wrapped in an `SDL_Window`, the | ||
| 102 | window is created with `SDL_CreateWindowWithProperties()` with the | ||
| 103 | `SDL_PROP_WINDOW_CREATE_WAYLAND_WL_SURFACE_POINTER` property to set to the `wl_surface` object that is to be | ||
| 104 | imported by SDL. | ||
| 105 | |||
| 106 | SDL receives no notification regarding size changes on external surfaces or toplevel windows, so if the external surface | ||
| 107 | needs to be resized, SDL must be informed by calling SDL_SetWindowSize() with the new dimensions. | ||
| 108 | |||
| 109 | If desired, SDL can automatically handle the scaling for the surface by setting the | ||
| 110 | `SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN` property to `true`, however, if the surface being imported | ||
| 111 | already has, or will have, a viewport/fractional scale manager attached to it by the application or an external toolkit, | ||
| 112 | a protocol violation will result. Avoid setting this property if importing surfaces from toolkits such as Qt or GTK. | ||
| 113 | |||
| 114 | If the window is flagged as high pixel density, calls to `SDL_SetWindowSize()` should pass the logical size of the | ||
| 115 | window and `SDL_GetWindowSizeInPixels()` should be used to retrieve the backbuffer size in pixels. Otherwise, calls to | ||
| 116 | `SDL_SetWindowSize()` should pass the requested surface size in pixels, not the logical window size, as no scaling | ||
| 117 | calculations will be done internally. | ||
| 118 | |||
| 119 | All window functions that control window state aside from `SDL_SetWindowSize()` are no-ops with external surfaces. | ||
| 120 | |||
| 121 | An example of how to use external surfaces with a `wl_display` owned by SDL can be seen in `tests/testnativewayland.c`, | ||
| 122 | and the following is a minimal example of interoperation with Qt 6, with Qt owning the `wl_display`: | ||
| 123 | |||
| 124 | ```c++ | ||
| 125 | #include <QApplication> | ||
| 126 | #include <QWindow> | ||
| 127 | #include <qpa/qplatformnativeinterface.h> | ||
| 128 | |||
| 129 | #include <SDL3/SDL.h> | ||
| 130 | |||
| 131 | int main(int argc, char *argv[]) | ||
| 132 | { | ||
| 133 | int ret = -1; | ||
| 134 | int done = 0; | ||
| 135 | SDL_PropertiesID props; | ||
| 136 | SDL_Event e; | ||
| 137 | SDL_Window *sdlWindow = NULL; | ||
| 138 | SDL_Renderer *sdlRenderer = NULL; | ||
| 139 | struct wl_display *display = NULL; | ||
| 140 | struct wl_surface *surface = NULL; | ||
| 141 | |||
| 142 | /* Initialize Qt */ | ||
| 143 | QApplication qtApp(argc, argv); | ||
| 144 | QWindow qtWindow; | ||
| 145 | |||
| 146 | /* The windowing system must be Wayland. */ | ||
| 147 | if (QApplication::platformName() != "wayland") { | ||
| 148 | goto exit; | ||
| 149 | } | ||
| 150 | |||
| 151 | { | ||
| 152 | /* Get the wl_display object from Qt */ | ||
| 153 | QNativeInterface::QWaylandApplication *qtWlApp = qtApp.nativeInterface<QNativeInterface::QWaylandApplication>(); | ||
| 154 | display = qtWlApp->display(); | ||
| 155 | |||
| 156 | if (!display) { | ||
| 157 | goto exit; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | /* Set SDL to use the existing wl_display object from Qt and initialize. */ | ||
| 162 | SDL_SetPointerProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_VIDEO_WAYLAND_WL_DISPLAY_POINTER, display); | ||
| 163 | SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); | ||
| 164 | |||
| 165 | /* Create a basic, frameless QWindow */ | ||
| 166 | qtWindow.setFlags(Qt::FramelessWindowHint); | ||
| 167 | qtWindow.setGeometry(0, 0, 640, 480); | ||
| 168 | qtWindow.show(); | ||
| 169 | |||
| 170 | { | ||
| 171 | /* Get the native wl_surface backing resource for the window */ | ||
| 172 | QPlatformNativeInterface *qtNative = qtApp.platformNativeInterface(); | ||
| 173 | surface = (struct wl_surface *)qtNative->nativeResourceForWindow("surface", &qtWindow); | ||
| 174 | |||
| 175 | if (!surface) { | ||
| 176 | goto exit; | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | /* Create a window that wraps the wl_surface from the QWindow. | ||
| 181 | * Qt objects should not be flagged as DPI-aware or protocol violations will result. | ||
| 182 | */ | ||
| 183 | props = SDL_CreateProperties(); | ||
| 184 | SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_WAYLAND_WL_SURFACE_POINTER, surface); | ||
| 185 | SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN, true); | ||
| 186 | SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, 640); | ||
| 187 | SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, 480); | ||
| 188 | sdlWindow = SDL_CreateWindowWithProperties(props); | ||
| 189 | SDL_DestroyProperties(props); | ||
| 190 | if (!sdlWindow) { | ||
| 191 | goto exit; | ||
| 192 | } | ||
| 193 | |||
| 194 | /* Create a renderer */ | ||
| 195 | sdlRenderer = SDL_CreateRenderer(sdlWindow, NULL); | ||
| 196 | if (!sdlRenderer) { | ||
| 197 | goto exit; | ||
| 198 | } | ||
| 199 | |||
| 200 | /* Draw a blue screen for the window until ESC is pressed or the window is no longer visible. */ | ||
| 201 | while (!done) { | ||
| 202 | while (SDL_PollEvent(&e)) { | ||
| 203 | if (e.type == SDL_EVENT_KEY_DOWN && e.key.key == SDLK_ESCAPE) { | ||
| 204 | done = 1; | ||
| 205 | } | ||
| 206 | } | ||
| 207 | |||
| 208 | qtApp.processEvents(); | ||
| 209 | |||
| 210 | /* Update the backbuffer size if the window scale changed. */ | ||
| 211 | qreal scale = qtWindow.devicePixelRatio(); | ||
| 212 | SDL_SetWindowSize(sdlWindow, SDL_lround(640. * scale), SDL_lround(480. * scale)); | ||
| 213 | |||
| 214 | if (qtWindow.isVisible()) { | ||
| 215 | SDL_SetRenderDrawColor(sdlRenderer, 0, 0, 255, SDL_ALPHA_OPAQUE); | ||
| 216 | SDL_RenderClear(sdlRenderer); | ||
| 217 | SDL_RenderPresent(sdlRenderer); | ||
| 218 | } else { | ||
| 219 | done = 1; | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | ret = 0; | ||
| 224 | |||
| 225 | exit: | ||
| 226 | /* Cleanup */ | ||
| 227 | if (sdlRenderer) { | ||
| 228 | SDL_DestroyRenderer(sdlRenderer); | ||
| 229 | } | ||
| 230 | if (sdlWindow) { | ||
| 231 | SDL_DestroyWindow(sdlWindow); | ||
| 232 | } | ||
| 233 | |||
| 234 | SDL_Quit(); | ||
| 235 | return ret; | ||
| 236 | } | ||
| 237 | ``` | ||
| 238 | |||
