Skip to content
Draft
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
14 changes: 13 additions & 1 deletion src/detection/displayserver/linux/wayland/global-output.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ static void waylandOutputScaleListener(void* data, [[maybe_unused]] struct wl_ou
display->dpi = 96 * (uint32_t) scale;
}

static void waylandOutputDoneListener(void* data, [[maybe_unused]] struct wl_output* output) {
WaylandDisplay* display = data;
display->done = true;
}

static void waylandOutputGeometryListener(void* data,
[[maybe_unused]] struct wl_output* output,
[[maybe_unused]] int32_t x,
Expand Down Expand Up @@ -54,7 +59,7 @@ static void handleXdgLogicalSize(void* data, [[maybe_unused]] struct zxdg_output
static void* outputListener[] = {
waylandOutputGeometryListener, // geometry
waylandOutputModeListener, // mode
stubListener, // done
waylandOutputDoneListener, // done
waylandOutputScaleListener, // scale
ffWaylandOutputNameListener, // name
ffWaylandOutputDescriptionListener, // description
Expand Down Expand Up @@ -122,6 +127,13 @@ const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry*
wldata->ffwl_proxy_destroy(output);
return "Failed to roundtrip wl_output";
}
if (bindVersion >= WL_OUTPUT_DONE_SINCE_VERSION && !display.done) {
const char* error = ffWaylandWaitForDone(&display);
if (error) {
wldata->ffwl_proxy_destroy(output);
return error;
}
}

if (wldata->zxdgOutputManager) {
uint32_t bindVersion = min(version, ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION);
Expand Down
14 changes: 13 additions & 1 deletion src/detection/displayserver/linux/wayland/kde-output.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,16 @@ static void waylandKdePriorityListener(void* data, [[maybe_unused]] struct kde_o
display->primary = priority == 1;
}

static void waylandKdeDoneListener(void* data, [[maybe_unused]] struct kde_output_device_v2* kde_output_device_v2) {
WaylandDisplay* display = data;
display->done = true;
}

static struct kde_output_device_v2_listener outputListener = {
.geometry = waylandKdeGeometryListener,
.current_mode = waylandKdeCurrentModeListener,
.mode = waylandKdeModeListener,
.done = (void*) stubListener,
.done = waylandKdeDoneListener,
.scale = waylandKdeScaleListener,
.edid = waylandKdeEdidListener,
.enabled = waylandKdeEnabledListener,
Expand Down Expand Up @@ -211,6 +216,13 @@ static const char* waylandKdeHandleOutput(WaylandData* wldata, struct wl_proxy*
wldata->ffwl_proxy_destroy(output);
return "Failed to roundtrip kde_output_device_v2";
}
if (!display.done) {
const char* error = ffWaylandWaitForDone(&display);
if (error) {
wldata->ffwl_proxy_destroy(output);
return error;
}
}
// Destroy any mode proxies that were created during the listeners.
// wl proxies created for modes are not automatically freed by destroying
// the parent output proxy, so destroy them explicitly to avoid leaks.
Expand Down
21 changes: 21 additions & 0 deletions src/detection/displayserver/linux/wayland/wayland.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <sys/socket.h>

#include "common/properties.h"
#include "common/time.h"

#include "wayland.h"
#include "kde-output-device-v2-client-protocol.h"
Expand Down Expand Up @@ -217,6 +218,26 @@ uint32_t ffWaylandHandleRotation(WaylandDisplay* display) {
return rotation;
}

const char* ffWaylandWaitForDone(WaylandDisplay* display) {
WaylandData* wldata = display->parent;
double deadline = ffTimeGetTick() + instance.config.general.processingTimeout;

@CarterLi CarterLi Jul 14, 2026

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.

processingTimeout is a signed integer. -1 means it should block infinitely, which should be handled independently.

In addition, ffTimeGetTick() returns integer too. Not sure why you use double here


// Some compositors emit the final output state asynchronously after the roundtrip callback (#2074)
while (!display->done) {
if (ffTimeGetTick() >= deadline) {
return "Timeout waiting for Wayland output information";
}
if (wldata->ffwl_display_roundtrip(wldata->display) < 0) {
return "Failed to roundtrip Wayland output information";
}
if (!display->done) {
ffTimeSleep(1);
}
Comment on lines +230 to +235

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 both wastes time (keep posting sync events) and adds latency.

I'd use wl_display_dispatch_timeout here, which blocks until receiving an event. The problem is that wl_display_dispatch_timeout is relatively new. However, as users who stuck at an old distro, they may not care about using the newest fastfetch either, so it won't be a serious problem.

}

return nullptr;
}

const char* ffdsConnectWayland(FFDisplayServerResult* result) {
if (getenv("XDG_RUNTIME_DIR") == nullptr) {
return "Wayland requires $XDG_RUNTIME_DIR being set";
Expand Down
2 changes: 2 additions & 0 deletions src/detection/displayserver/linux/wayland/wayland.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ typedef struct WaylandDisplay {
FFstrbuf serial;
uint8_t bitDepth;
bool primary;
bool done;
} WaylandDisplay;

inline static void stubListener(void* data, ...) {
Expand All @@ -79,6 +80,7 @@ void ffWaylandOutputNameListener(void* data, [[maybe_unused]] void* output, cons
void ffWaylandOutputDescriptionListener(void* data, [[maybe_unused]] void* output, const char* description);
// Modifies content of display. Don't call this function when calling ffdsAppendDisplay
uint32_t ffWaylandHandleRotation(WaylandDisplay* display);
const char* ffWaylandWaitForDone(WaylandDisplay* display);

const char* ffWaylandHandleGlobalOutput(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
const char* ffWaylandHandleKdeOutputRegistry(WaylandData* wldata, struct wl_registry* registry, uint32_t name, uint32_t version);
Expand Down