// F13 libdecor comparison variant (see ../app.d for the hand-rolled CSD this
// is measured against, and ../../../f13-decorations.md for the LOC ledger).
// The sanctioned helper-library exception: libdecor_new + libdecor_decorate
// replace the *entire* shell layer — xdg_surface/xdg_toplevel creation, the
// configure/ack dance, xdg-decoration negotiation, set_window_geometry, title
// bar drawing, move/resize hit zones, themes, shadows, double-click-to-
// maximize all live inside libdecor and its cairo/gtk plugin. The app keeps
// only: connect, bind wl_compositor+wl_shm, paint content, commit, dispatch.
// A maximize/unmaximize storm exercises libdecor's geometry handling.
// Headless-safe: no compositor → SKIP, exit 0.
module (module) appapp;
import c; // ImportC: wayland-client + libdecor.h + wsi_* core helpers
import instrument;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdioD header file for C99 <stdio.h>
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdio.h.html, stdio.h
Source
core/stdc/stdio.d
stdio : (alias) app.printf = int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stdlibD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/stdlib.h.html, stdlib.h
Source
core/stdc/stdlib.d
stdlib : (alias) app.getenv = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv, (alias) app.atoi = int core.stdc.stdlib.atoi(scope const(char*) nptr) nothrow @nogcatoi;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.stringD header file for C99.
pubs.opengroup.org/onlinepubs/009695399/basedefs/string.h.html, string.h
Source
core/stdc/string.d
string : (alias) app.strcmp = int core.stdc.string.strcmp(scope const(char*) s1, scope const(char*) s2) pure nothrow @nogcstrcmp;
enum int (constant) int app.defaultWidth = 640defaultWidth = 640, (constant) int app.defaultHeight = 480defaultHeight = 480;
__gshared
{
wl_display* _error_ app.g_displayg_display;
wl_registry* _error_ app.g_registryg_registry;
wl_compositor* _error_ app.g_compositorg_compositor;
wl_shm* _error_ app.g_shmg_shm;
wl_surface* _error_ app.g_surfaceg_surface;
libdecor* _error_ app.g_ctxg_ctx;
libdecor_frame* _error_ app.g_frameg_frame;
wl_buffer* _error_ app.g_bufferg_buffer; // single-buffered: weston/sway release shm fast
uint* (__gshared global) uint* app.g_pixelsg_pixels;
(alias) object.size_t = ulongsize_t (__gshared global) ulong app.g_byteSizeg_byteSize;
int (__gshared global) int app.g_widthg_width = defaultWidth, (__gshared global) int app.g_heightg_height = defaultHeight;
int (__gshared global) int app.g_configuresg_configures;
bool (__gshared global) bool app.g_runningg_running = true;
long (__gshared global) long app.g_runUsCapg_runUsCap = 2_500_000;
bool (__gshared global) bool app.g_maximizedSentg_maximizedSent, (__gshared global) bool app.g_unmaximizedSentg_unmaximizedSent;
}
extern (C) void app.onGlobalonGlobal(void* data, wl_registry* reg, uint name,
const(char)* iface, uint ver) nothrow @nogc
{
if (strcmp(iface, wl_compositor_interface.name) == 0)
g_compositor = cast(wl_compositor*) wsi_registry_bind(reg, name,
&wl_compositor_interface, ver < 4 ? ver : 4);
else if (strcmp(iface, wl_shm_interface.name) == 0)
g_shm = cast(wl_shm*) wsi_registry_bind(reg, name, &wl_shm_interface, 1);
}
extern (C) void app.onGlobalRemoveonGlobalRemove(void* data, wl_registry* reg, uint name) nothrow @nogc
{
}
__gshared wl_registry_listener _error_ app.g_registryListenerg_registryListener = {&onGlobal, &onGlobalRemove};
void void app.paintAndCommit() nothrow @nogcpaintAndCommit() nothrow @nogc
{
immutable (local variable) immutable(int) stridestride = (__gshared global) int app.g_widthg_width * 4;
immutable (local variable) immutable(ulong) sizesize = cast((alias) object.size_t = ulongsize_t) (local variable) immutable(int) stridestride * (__gshared global) int app.g_heightg_height;
if (g_buffer !is null)
{
wsi_buffer_destroy(g_buffer);
munmap(g_pixels, g_byteSize);
g_buffer = null;
}
immutable (local variable) immutable(_error_) fdfd = memfd_create("wsi-f13-libdecor", MFD_CLOEXEC);
if (fd < 0 || ftruncate(fd, cast(long) size) != 0)
return;
(__gshared global) uint* app.g_pixelsg_pixels = cast(uint*) mmap(null, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
(__gshared global) ulong app.g_byteSizeg_byteSize = (local variable) immutable(ulong) sizesize;
wl_shm_pool* (local variable) _error_ poolpool = wsi_shm_create_pool(g_shm, fd, cast(int) size);
g_buffer = wsi_shm_pool_create_buffer(pool, 0, g_width, g_height, stride,
WL_SHM_FORMAT_ARGB8888);
wsi_shm_pool_destroy(pool);
close(fd);
foreach ((local variable) int yy; 0 .. (__gshared global) int app.g_heightg_height) // content gradient only — NO frame drawing here
{
uint* (local variable) uint* rowrow = (__gshared global) uint* app.g_pixelsg_pixels + cast((alias) object.size_t = ulongsize_t) (local variable) int yy * (__gshared global) int app.g_widthg_width;
immutable uint (local variable) immutable(uint) gg = cast(uint)((local variable) int yy * 255 / ((__gshared global) int app.g_heightg_height - 1)) << 8;
foreach ((local variable) int xx; 0 .. (__gshared global) int app.g_widthg_width)
(local variable) uint* rowrow[(local variable) int xx] = 0xff00_0000 | (cast(uint)((local variable) int xx * 255 / ((__gshared global) int app.g_widthg_width - 1)) << 16) | (local variable) immutable(uint) gg;
}
wsi_surface_attach(g_surface, g_buffer, 0, 0);
wsi_surface_damage_buffer(g_surface, 0, 0, g_width, g_height);
wsi_surface_commit(g_surface);
}
// ---------------------------------------------------- libdecor callbacks
extern (C) void app.onErroronError(libdecor* ctx, libdecor_error error, const(char)* message) nothrow @nogc
{
instrEvent("libdecor_error", "error=%d message=%s", error, message);
g_running = false;
}
/// The whole F13 negotiation, reduced to one callback: libdecor hands over the
/// *content* size (frame already subtracted) and takes back a libdecor_state —
/// ack_configure + set_window_geometry happen inside libdecor_frame_commit.
extern (C) void app.onConfigureThe whole F13 negotiation, reduced to one callback: libdecor hands over the
content size (frame already subtracted) and takes back a libdecor_state —
ack_configure + set_window_geometry happen inside libdecor_frame_commit.
onConfigure(libdecor_frame* frame, libdecor_configuration* conf,
void* userData) nothrow @nogc
{
int _error_ ww, _error_ hh;
if (!libdecor_configuration_get_content_size(conf, frame, &w, &h))
{
w = defaultWidth;
h = defaultHeight;
}
libdecor_window_state _error_ wsws;
if (!libdecor_configuration_get_window_state(conf, &ws))
ws = LIBDECOR_WINDOW_STATE_NONE;
g_configures++;
instrEvent("configure", "content=%dx%d window_state=0x%x", w, h, cast(uint) ws);
g_width = w;
g_height = h;
libdecor_state* _error_ statestate = libdecor_state_new(w, h);
libdecor_frame_commit(frame, state, conf);
libdecor_state_free(state);
paintAndCommit();
if (g_configures == 1)
instrFirstPixelPresented(); // first content commit; frame is libdecor's
}
extern (C) void app.onCloseonClose(libdecor_frame* frame, void* userData) nothrow @nogc
{
instrCloseRequested();
g_running = false;
}
extern (C) void app.onCommitonCommit(libdecor_frame* frame, void* userData) nothrow @nogc
{
wsi_surface_commit(g_surface); // sync-subsurface decorations need this
}
extern (C) void app.onDismissPopuponDismissPopup(libdecor_frame* frame, const(char)* seatName, void* userData) nothrow @nogc
{
}
__gshared libdecor_interface _error_ app.g_ifaceg_iface = {&onError};
__gshared libdecor_frame_interface _error_ app.g_frameIfaceg_frameIface = {
&onConfigure, &onClose, &onCommit, &onDismissPopup
};
// --------------------------------------------------------------------- main
int int D main()main()
{
instrInit("f13-libdecor");
const (local variable) const(char*) autoEnvautoEnv = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("WSI_AUTO_EXIT");
immutable (local variable) immutable(bool) autoExitautoExit = (local variable) const(char*) autoEnvautoEnv !is null && *(local variable) const(char*) autoEnvautoEnv == '1';
if (const (local variable) const(char*) msms = char* core.stdc.stdlib.getenv(scope const(char*) name) nothrow @nogcgetenv("WSI_RUN_MS"))
(__gshared global) long app.g_runUsCapg_runUsCap = int core.stdc.stdlib.atoi(scope const(char*) nptr) nothrow @nogcatoi((local variable) const(char*) msms) * 1000L;
g_display = wl_display_connect(null);
if (g_display is null)
{
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("SKIP: no Wayland compositor (wl_display_connect returned null)\n");
return 0;
}
g_registry = wsi_display_get_registry(g_display);
wsi_registry_add_listener(g_registry, &g_registryListener, null);
wl_display_roundtrip(g_display);
if (g_compositor is null || g_shm is null)
{
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("SKIP: compositor lacks wl_compositor/wl_shm\n");
wl_display_disconnect(g_display);
return 0;
}
g_surface = wsi_compositor_create_surface(g_compositor);
g_ctx = libdecor_new(g_display, &g_iface); // binds xdg-shell + xdg-deco itself
instrEvent("step", "name=libdecor_new");
g_frame = libdecor_decorate(g_ctx, g_surface, &g_frameIface, null);
instrEvent("step", "name=libdecor_decorate");
libdecor_frame_set_title(g_frame, "wsi-f13-libdecor");
libdecor_frame_set_app_id(g_frame, "wsi-f13-libdecor");
libdecor_frame_map(g_frame);
instrWindowCreated();
// libdecor owns the event loop entry point: its dispatch wraps the wl
// socket *and* the plugin's own timers/frame machinery.
while ((__gshared global) bool app.g_runningg_running && libdecor_dispatch(g_ctx, 100) >= 0)
{
immutable (local variable) immutable(_error_) nownow = instrNowUs();
if ((local variable) immutable(bool) autoExitautoExit && !(__gshared global) bool app.g_maximizedSentg_maximizedSent && now > (__gshared global) long app.g_runUsCapg_runUsCap / 3)
{
(__gshared global) bool app.g_maximizedSentg_maximizedSent = true;
instrEvent("state_request", "kind=set_maximized");
libdecor_frame_set_maximized(g_frame);
}
else if ((local variable) immutable(bool) autoExitautoExit && !(__gshared global) bool app.g_unmaximizedSentg_unmaximizedSent && now > 2 * (__gshared global) long app.g_runUsCapg_runUsCap / 3)
{
(__gshared global) bool app.g_unmaximizedSentg_unmaximizedSent = true;
instrEvent("state_request", "kind=unset_maximized");
libdecor_frame_unset_maximized(g_frame);
}
if ((local variable) immutable(bool) autoExitautoExit && now > (__gshared global) long app.g_runUsCapg_runUsCap)
(__gshared global) bool app.g_runningg_running = false;
}
libdecor_frame_unref(g_frame);
libdecor_unref(g_ctx); // tears down its xdg/decoration objects
if (g_buffer !is null)
{
wsi_buffer_destroy(g_buffer);
munmap(g_pixels, g_byteSize);
}
wsi_surface_destroy(g_surface);
wl_proxy_destroy(cast(wl_proxy*) g_shm);
wl_proxy_destroy(cast(wl_proxy*) g_compositor);
wl_proxy_destroy(cast(wl_proxy*) g_registry);
wl_display_disconnect(g_display);
int core.stdc.stdio.printf(scope const(char*) format, scope const ...) nothrow @nogcprintf("ok: %d configures, final content %dx%d\n", (__gshared global) int app.g_configuresg_configures, (__gshared global) int app.g_widthg_width, (__gshared global) int app.g_heightg_height);
return 0;
}