commit d31ebb5431df658a1914ebe73654c11a993fff02
parent e91f3ae17b8994a92566413d0a8a0c19912957e9
Author: Luke Willis <lukejw@loquat.dev>
Date:   Mon, 30 Mar 2026 21:06:16 -0400

Rewrite everything and begin using pixman

Diffstat:
M.clang-format | 144+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
MREADME.md | 20++++++++++++++++++--
Mguix.scm | 6++++--
Mmeson.build | 5++---
Mscm/wig.scm | 2+-
Msrc/main.c | 422+++++++++++++++++++++++++++++++------------------------------------------------
Asrc/surface.c | 162+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/surface.h | 26++++++++++++++++++++++++++
Asrc/window.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/window.h | 19+++++++++++++++++++
Dsrc/wl.c | 44--------------------------------------------
Dsrc/wl.h | 6------
12 files changed, 603 insertions(+), 318 deletions(-)

diff --git a/.clang-format b/.clang-format @@ -1,3 +1,141 @@ ---- -BasedOnStyle: Google -... +# Stole from suckless' libgrapheme +# https://git.suckless.org/libgrapheme/file/.clang-format.html + +AccessModifierOffset: 0 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false +AlignConsecutiveDeclarations: + Enabled: false +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +# Kind: Always +# OverEmptyLines: 1 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: None +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: TopLevelDefinitions +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: [] +BinPackArguments: true +BinPackParameters: true +BitFieldColonSpacing: Both +BreakAfterJavaFieldAnnotations: false +#BreakArrays: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: WebKit +BreakBeforeConceptDeclarations: Always +#BreakBeforeInlineASMColon: Always +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterComma +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: "" +CompactNamespaces: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DeriveLineEnding: false +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: Never +FixNamespaceComments: true +ForEachMacros: [] +IfMacros: [] +IncludeBlocks: Preserve +IncludeIsMainRegex: "" +IncludeIsMainSourceRegex: "" +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: NoIndent +IndentGotoLabels: false +IndentPPDirectives: BeforeHash +IndentRequiresClause: false +IndentWidth: 8 +IndentWrappedFunctionNames: false +InsertBraces: true +InsertTrailingCommas: None +JavaImportGroups: [] +JavaScriptQuotes: Double +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +LambdaBodyIndentation: Signature +Language: Cpp +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +NamespaceMacros: [] +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 8 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true +PPIndentWidth: 0 +PackConstructorInitializers: Never +PointerAlignment: Right +QualifierAlignment: Left +RawStringFormats: [] +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +#RemoveSemicolon: false +RequiresClausePosition: OwnLine +#RequiresExpressionIndentation: Keyword +SeparateDefinitionBlocks: Always +ShortNamespaceLines: 0 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceAroundPointerQualifiers: Before +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 4 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: 1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +StatementAttributeLikeMacros: [] +StatementMacros: [] +TabWidth: 8 +TypenameMacros: [] +UseCRLF: false +UseTab: AlignWithSpaces +#WhitespaceSensitiveMacros: [] diff --git a/README.md b/README.md @@ -1,3 +1,19 @@ -# wig - Widgets in Guile +# Widgets in Guile -Wig is a widget engine for Wayland configured via Guile Scheme. +`wig` is a lightweight tool for creating custom Wayland widgets via Guile +Scheme. + +## Design + +`wig` is split into two main halves, C and Scheme. + +The Scheme half both provides methods for the user configuration and prepares +it for use in C by validating and flattening it. + +The C half manages the main event loop, input, rendering and so on. + +## TODO + +- Improve README +- Handle callbacks +- Proper GUI diff --git a/guix.scm b/guix.scm @@ -6,7 +6,8 @@ (gnu packages) (gnu packages freedesktop) (gnu packages guile) - (gnu packages pkg-config)) + (gnu packages pkg-config) + (gnu packages xdisorg)) (define wig (package @@ -18,7 +19,8 @@ wayland-protocols wlr-protocols)) (inputs (list wayland - guile-3.0)) + guile-3.0 + pixman)) (synopsis "Widgets in Guile") (description #f) (home-page "https://git.monastech.xyz/wig") diff --git a/meson.build b/meson.build @@ -9,6 +9,7 @@ deps = [ m_dep, dependency('wayland-client'), dependency('guile-3.0'), + dependency('pixman-1'), ] # Set up wayland protocols @@ -26,9 +27,7 @@ devenv = environment() devenv.append('GUILE_LOAD_PATH', join_paths(meson.current_source_dir(), 'scm')) meson.add_devenv(devenv) -common_sources = files( - 'src/wl.c' -) +common_sources = files('src/surface.c', 'src/window.c') executable( 'wig', diff --git a/scm/wig.scm b/scm/wig.scm @@ -66,7 +66,7 @@ (anchors-left anchors) (anchors-right anchors) exclusive) - (error "A window cannot anchor to all sides and still be exclusive.")) + (error "A window cannot anchor to all sides and be exclusive.")) (make-window label anchors exclusive width height margin)) (define-record-type <configuration> diff --git a/src/main.c b/src/main.c @@ -2,280 +2,188 @@ #include <stdint.h> #include <stdio.h> #include <string.h> -#include <sys/mman.h> #include <unistd.h> #include <wayland-client.h> -#include "wl.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" -struct wig_window { - int configured; - - char* label; - uint32_t width; - uint32_t height; - - struct wl_shm* shm; - - struct wl_buffer* wl_buffer; - int wl_buffer_released; - - struct wl_surface* wl_surface; - struct zwlr_layer_surface_v1* wlr_layer_surface; - - uint32_t* data; -}; +#include "surface.h" +#include "window.h" struct wig_state { - struct wl_display* display; - struct wl_registry* registry; - - struct wl_compositor* compositor; - struct wl_shm* shm; - struct zwlr_layer_shell_v1* layer_shell; - - struct wig_window* windows; -}; - -/* - * Buffer - */ - -static void wl_buffer_release(void* data, struct wl_buffer* wl_buffer) { - struct wig_window* window = data; - window->wl_buffer_released = 1; -} - -static const struct wl_buffer_listener wl_buffer_listener = { - .release = wl_buffer_release, -}; - -int reconfigure_window(struct wig_window* window, int width, int height) { - if (window->data != NULL) { - wl_buffer_destroy(window->wl_buffer); - munmap(window->data, window->width * window->height * 4); - } - - int stride = width * 4; - int size = stride * height; - - int fd = allocate_shm_file(size); - if (fd == -1) { - return 0; - } - - uint32_t* data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) { - close(fd); - return 0; - } - - struct wl_shm_pool* pool = wl_shm_create_pool(window->shm, fd, size); - struct wl_buffer* buffer = wl_shm_pool_create_buffer( - pool, 0, width, height, stride, WL_SHM_FORMAT_XRGB8888); - wl_shm_pool_destroy(pool); - close(fd); - - /* Draw checkerboxed background */ - for (int y = 0; y < height; ++y) { - for (int x = 0; x < width; ++x) { - if ((x + y / 8 * 8) % 16 < 8) - data[y * width + x] = 0xFF666666; - else - data[y * width + x] = 0xFFEEEEEE; - } - } - wl_buffer_add_listener(buffer, &wl_buffer_listener, window); - - window->data = data; - window->wl_buffer = buffer; - window->configured = 1; - - return 0; -} - -/* - * Surface - */ - -static void zwlr_layer_surface_configure( - void* data, struct zwlr_layer_surface_v1* zwlr_layer_surface_v1, - uint32_t serial, uint32_t width, uint32_t height) { - struct wig_window* window = data; - zwlr_layer_surface_v1_ack_configure(zwlr_layer_surface_v1, serial); - - /* Skip reconfiguration if the size hasn't changed */ - if (width == window->width && height == window->height) return; + struct wl_display *wl_display; + struct wl_registry *wl_registry; - int result = reconfigure_window(window, width, height); + struct wl_compositor *wl_compositor; + struct wl_shm *wl_shm; + struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1; - if (result) { - fprintf(stderr, "Failed to reconfigure \"%s\"", window->label); - return; - } - - wl_surface_attach(window->wl_surface, window->wl_buffer, 0, 0); - wl_surface_commit(window->wl_surface); - window->wl_buffer_released = 0; - - window->width = width; - window->height = height; - - fprintf(stderr, "\"%s\" reconfigured\n", window->label); - fprintf(stderr, " %u x %u\n", width, height); -} - -static void zwlr_layer_surface_closed( - void* data, struct zwlr_layer_surface_v1* zwlr_layer_surface_v1) { - /* TODO */ -} - -static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { - .configure = zwlr_layer_surface_configure, - .closed = zwlr_layer_surface_closed, + struct wig_window *windows; }; -/* - * Registry - */ - -static void registry_handle_global(void* data, struct wl_registry* registry, - uint32_t name, const char* interface, - uint32_t version) { - struct wig_state* state = data; - - if (strcmp(interface, wl_compositor_interface.name) == 0) { - state->compositor = - wl_registry_bind(registry, name, &wl_compositor_interface, 4); - } else if (strcmp(interface, wl_shm_interface.name) == 0) { - state->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); - } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { - state->layer_shell = - wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 2); - } +static void +wl_registry_handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, + uint32_t version) +{ + struct wig_state *state = data; + + if (strcmp(interface, wl_compositor_interface.name) == 0) { + state->wl_compositor = wl_registry_bind( + registry, name, &wl_compositor_interface, 4); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + state->wl_shm = + wl_registry_bind(registry, name, &wl_shm_interface, 1); + } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + state->zwlr_layer_shell_v1 = wl_registry_bind( + registry, name, &zwlr_layer_shell_v1_interface, 2); + } } -static void registry_handle_global_remove(void* data, - struct wl_registry* registry, - uint32_t name) { - /* TODO */ +static void +wl_registry_handle_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ + /* TODO */ } -static const struct wl_registry_listener registry_listener = { - .global = registry_handle_global, - .global_remove = registry_handle_global_remove, +static const struct wl_registry_listener wl_registry_listener = { + .global = wl_registry_handle_global, + .global_remove = wl_registry_handle_global_remove, }; -/* Main */ - -static void inner_main(void* data, int argc, char** argv) { - struct wig_state* state = data; - - /* Evaluate configuration */ - SCM wig_configuration_type = scm_c_private_ref("wig", "<configuration>"); - - SCM config = scm_c_primitive_load("wig.scm"); - - /* TODO: Catch errors properly */ - if (scm_is_false( - scm_eq_p(scm_struct_vtable(config), wig_configuration_type))) { - fprintf(stderr, "Invalid configuration!\n"); - return; - } - - SCM windows = scm_struct_ref(config, scm_from_int(0)); - size_t num_windows = scm_to_size_t(scm_length(windows)); - state->windows = malloc(num_windows * sizeof(struct wig_window)); - - for (size_t i = 0; i < num_windows; i++) { - SCM scm_window = scm_car(windows); - - char* label = - scm_to_utf8_stringn(scm_struct_ref(scm_window, scm_from_int(0)), NULL); - - SCM anchors = scm_struct_ref(scm_window, scm_from_int(1)); - uint32_t anchor = 0; - for (uint32_t i = 0; i < 4; i++) - if (scm_is_true(scm_struct_ref(anchors, scm_from_uint32(i)))) - anchor |= (1 << i); - - int exclusive = scm_is_true(scm_struct_ref(scm_window, scm_from_int(2))); - - uint32_t width = scm_to_uint32(scm_struct_ref(scm_window, scm_from_int(3))); - uint32_t height = - scm_to_uint32(scm_struct_ref(scm_window, scm_from_int(4))); - uint32_t margin = - scm_to_uint32(scm_struct_ref(scm_window, scm_from_int(5))); - - struct wig_window* window = &state->windows[i]; - window->label = label; - window->width = width; - window->height = height; - - window->shm = state->shm; - - window->data = NULL; - window->wl_surface = wl_compositor_create_surface(state->compositor); - window->wlr_layer_surface = zwlr_layer_shell_v1_get_layer_surface( - state->layer_shell, window->wl_surface, NULL, - ZWLR_LAYER_SHELL_V1_LAYER_TOP, label); - - zwlr_layer_surface_v1_add_listener(window->wlr_layer_surface, - &layer_surface_listener, window); - zwlr_layer_surface_v1_set_size(window->wlr_layer_surface, width, height); - zwlr_layer_surface_v1_set_anchor(window->wlr_layer_surface, anchor); - zwlr_layer_surface_v1_set_margin(window->wlr_layer_surface, margin, margin, - margin, margin); - - if (exclusive) { - if ((anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) && - (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { - zwlr_layer_surface_v1_set_exclusive_zone(window->wlr_layer_surface, - width); - } else if ((anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) && - (anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { - zwlr_layer_surface_v1_set_exclusive_zone(window->wlr_layer_surface, - height); - } - } - - wl_surface_commit(window->wl_surface); - - windows = scm_cdr(windows); - } - - /* Loop */ - while (wl_display_dispatch(state->display) != -1) { - for (size_t i = 0; i < num_windows; i++) { - state->windows[i].data[1] = 0xFFFF00FF; - - wl_surface_attach(state->windows[i].wl_surface, - state->windows[i].wl_buffer, 0, 0); - wl_surface_damage_buffer(state->windows[i].wl_surface, 1, 0, 1, 1); - wl_surface_commit(state->windows[i].wl_surface); - } - } - - /* Cleanup */ - wl_display_disconnect(state->display); +void * +configure_and_run(void *data) +{ + struct wig_state *state = data; + + SCM wig_scm_config_type = scm_c_private_ref("wig", "<configuration>"); + + SCM scm_config = scm_c_primitive_load("wig.scm"); + + if (scm_is_false(scm_eq_p(scm_struct_vtable(scm_config), + wig_scm_config_type))) { + fprintf(stderr, "Invalid configuration!\n"); + return NULL; + } + + /* Load and create windows */ + SCM scm_windows = scm_struct_ref(scm_config, scm_from_int(0)); + size_t num_windows = scm_to_size_t(scm_length(scm_windows)); + state->windows = malloc(num_windows * sizeof(struct wig_window)); + + for (size_t i = 0; i < num_windows; i++) { + SCM scm_window = scm_car(scm_windows); + char *label = scm_to_utf8_stringn( + scm_struct_ref(scm_window, scm_from_int(0)), NULL); + + SCM scm_anchors = scm_struct_ref(scm_window, scm_from_int(1)); + uint32_t anchors = 0; + for (uint32_t i = 0; i < 4; i++) { + if scm_is_true (scm_struct_ref(scm_anchors, + scm_from_uint32(i))) { + anchors |= (1 << i); + } + } + + int exclusive = scm_is_true( + scm_struct_ref(scm_window, scm_from_int(2))); + uint32_t width = scm_to_uint32( + scm_struct_ref(scm_window, scm_from_int(3))); + uint32_t height = scm_to_uint32( + scm_struct_ref(scm_window, scm_from_int(4))); + uint32_t margin = scm_to_uint32( + scm_struct_ref(scm_window, scm_from_int(5))); + + struct wig_surface surface; + wig_surface_init(&surface, state->wl_shm, state->wl_compositor); + + struct wig_window *window = &state->windows[i]; + wig_window_init(window, label, surface, + state->zwlr_layer_shell_v1); + + zwlr_layer_surface_v1_set_size(window->zwlr_layer_surface_v1, + width, height); + zwlr_layer_surface_v1_set_anchor(window->zwlr_layer_surface_v1, + anchors); + + if (exclusive) { + if ((anchors & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP) && + (anchors & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + zwlr_layer_surface_v1_set_exclusive_zone( + window->zwlr_layer_surface_v1, width); + } else if ((anchors & + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT) && + (anchors & + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + zwlr_layer_surface_v1_set_exclusive_zone( + window->zwlr_layer_surface_v1, height); + } + } + + zwlr_layer_surface_v1_set_margin(window->zwlr_layer_surface_v1, + margin, margin, margin, + margin); + + wig_surface_commit(&window->surface); + + scm_windows = scm_cdr(scm_windows); + } + + while (wl_display_dispatch(state->wl_display) != -1) { + /* TODO */ + } + + return NULL; } -int main(int argc, char* argv[]) { - struct wig_state state = {NULL}; - - state.display = wl_display_connect(NULL); - if (!state.display) { - fprintf(stderr, "Failed to connect to Wayland display.\n"); - return 1; - } - - state.registry = wl_display_get_registry(state.display); - wl_registry_add_listener(state.registry, &registry_listener, &state); - wl_display_roundtrip(state.display); - - /* TODO: Validate registry */ - - scm_boot_guile(argc, argv, inner_main, &state); - return 0; +int +main(int argc, char *argv[]) +{ + struct wig_state state = { NULL }; + + state.wl_display = wl_display_connect(NULL); + if (!state.wl_display) { + fprintf(stderr, "Failed to connect to a Wayland display!"); + return 1; + } + + state.wl_registry = wl_display_get_registry(state.wl_display); + wl_registry_add_listener(state.wl_registry, &wl_registry_listener, + &state); + wl_display_roundtrip(state.wl_display); + + if (!state.wl_compositor || !state.wl_shm || + !state.zwlr_layer_shell_v1) { + fprintf(stderr, "Failed to bind the necessary globals!\n"); + return 1; + } + + fprintf(stderr, "Entering guile mode\n"); + scm_with_guile(configure_and_run, &state); + fprintf(stderr, "Exiting guile mode\n"); + + /* Demo window */ + /* + struct wig_surface surface; + wig_surface_init(&surface, state.wl_shm, state.wl_compositor); + + struct wig_window window; + wig_window_init(&window, "bottom_bar", surface, + state.zwlr_layer_shell_v1); + + zwlr_layer_surface_v1_set_size(window.zwlr_layer_surface_v1, 0, 64); + zwlr_layer_surface_v1_set_anchor( + window.zwlr_layer_surface_v1, + ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | + ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM); + zwlr_layer_surface_v1_set_exclusive_zone(window.zwlr_layer_surface_v1, + 64); + zwlr_layer_surface_v1_set_margin(window.zwlr_layer_surface_v1, 8, 8, 8, + 8); + wl_surface_commit(window.surface.wl_surface); + */ + + wl_display_disconnect(state.wl_display); } diff --git a/src/surface.c b/src/surface.c @@ -0,0 +1,162 @@ +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <time.h> +#include <unistd.h> + +#include <pixman.h> +#include <wayland-client.h> + +#include "surface.h" + +/* + * SHM Utils + */ +static void +randname(char *buf) +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + for (int i = 0; i < 6; ++i) { + buf[i] = 'A' + (r & 15) + (r & 16) * 2; + r >>= 5; + } +} + +static int +create_shm_file(void) +{ + int retries = 100; + do { + char name[] = "/wl_shm-XXXXXX"; + randname(name + sizeof(name) - 7); + --retries; + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); + return -1; +} + +static int +allocate_shm_file(size_t size) +{ + int fd = create_shm_file(); + if (fd < 0) { + return -1; + } + int ret; + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + close(fd); + return -1; + } + return fd; +} + +/* + * Surface + */ +int +wig_surface_init(struct wig_surface *surface, struct wl_shm *wl_shm, + struct wl_compositor *wl_compositor) +{ + surface->wl_shm = wl_shm; + + surface->configured = 0; + surface->width = 0; + surface->height = 0; + surface->wl_surface = wl_compositor_create_surface(wl_compositor); + + surface->free = 0; + surface->buffer = NULL; + surface->wl_buffer = NULL; + + return 0; +} + +static void +wl_buffer_release(void *data, struct wl_buffer *wl_buffer) +{ + struct wig_surface *surface = data; + surface->free = 1; +} + +static const struct wl_buffer_listener wl_buffer_listener = { + .release = wl_buffer_release, +}; + +int +wig_surface_reconfigure(struct wig_surface *surface, uint32_t width, + uint32_t height) +{ + if (surface->buffer != NULL) { + wl_buffer_destroy(surface->wl_buffer); + munmap(surface->buffer, surface->width * surface->height * 4); + } + + size_t stride = width * 4; + size_t size = stride * height; + + int fd = allocate_shm_file(size); + if (fd < 0) { + return -1; + } + + uint32_t *buffer = + mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (buffer == MAP_FAILED) { + close(fd); + return -1; + } + + struct wl_shm_pool *pool = + wl_shm_create_pool(surface->wl_shm, fd, size); + struct wl_buffer *wl_buffer = wl_shm_pool_create_buffer( + pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888); + wl_shm_pool_destroy(pool); + close(fd); + + pixman_fill(buffer, width, 32, 0, 0, width, height, 0xFFCECDC3); + + pixman_fill(buffer, width, 32, 2, 2, width - 4, height - 4, 0xFFFFFCF0); + + wl_buffer_add_listener(wl_buffer, &wl_buffer_listener, surface); + + surface->configured = 1; + surface->width = width; + surface->height = height; + + surface->buffer = buffer; + surface->wl_buffer = wl_buffer; + surface->free = 1; + + return 0; +} + +int +wig_surface_commit(struct wig_surface *surface) +{ + if (surface->configured) { + if (!surface->free) { + fprintf(stderr, + "Refusing to commit unreleased surface\n"); + return -1; + } + wl_surface_attach(surface->wl_surface, surface->wl_buffer, 0, + 0); + surface->free = 0; + } + + wl_surface_commit(surface->wl_surface); + + return 0; +} diff --git a/src/surface.h b/src/surface.h @@ -0,0 +1,26 @@ +#ifndef WIG_SURFACE +#define WIG_SURFACE + +#include <stdint.h> +#include <wayland-client.h> + +struct wig_surface { + struct wl_shm *wl_shm; + + int configured; + uint32_t width; + uint32_t height; + struct wl_surface *wl_surface; + + int free; + uint32_t *buffer; + struct wl_buffer *wl_buffer; +}; + +int wig_surface_init(struct wig_surface *surface, struct wl_shm *wl_shm, + struct wl_compositor *wl_compositor); +int wig_surface_reconfigure(struct wig_surface *surface, uint32_t width, + uint32_t height); +int wig_surface_commit(struct wig_surface *surface); + +#endif diff --git a/src/window.c b/src/window.c @@ -0,0 +1,65 @@ +#include <stdint.h> +#include <stdio.h> + +#include "wlr-layer-shell-unstable-v1-client-protocol.h" +#include <wayland-client.h> + +#include "surface.h" +#include "window.h" + +static void +zwlr_layer_surface_v1_configure( + void *data, struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1, + uint32_t serial, uint32_t width, uint32_t height) +{ + zwlr_layer_surface_v1_ack_configure(zwlr_layer_surface_v1, serial); + + struct wig_window *window = data; + + /* Skip pointless reconfiguration */ + if (window->surface.configured && width == window->surface.width && + height == window->surface.height) { + return; + } + + int result = wig_surface_reconfigure(&window->surface, width, height); + + if (result) { + fprintf(stderr, "Failed to reconfigure %s's surface\n", + window->label); + return; + } + + wig_surface_commit(&window->surface); +} + +static void +zwlr_layer_surface_v1_closed( + void *data, struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1) +{ + /* TODO */ +} + +static const struct zwlr_layer_surface_v1_listener + zwlr_layer_surface_v1_listener = { + .configure = zwlr_layer_surface_v1_configure, + .closed = zwlr_layer_surface_v1_closed, + }; + +int +wig_window_init(struct wig_window *window, char *label, + struct wig_surface surface, + struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1) +{ + window->label = label; + window->surface = surface; + + window->zwlr_layer_surface_v1 = zwlr_layer_shell_v1_get_layer_surface( + zwlr_layer_shell_v1, window->surface.wl_surface, NULL, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, label); + zwlr_layer_surface_v1_add_listener(window->zwlr_layer_surface_v1, + &zwlr_layer_surface_v1_listener, + window); + + return 0; +} diff --git a/src/window.h b/src/window.h @@ -0,0 +1,19 @@ +#ifndef WIG_WINDOW +#define WIG_WINDOW + +#include <stdint.h> +#include <wayland-client.h> + +#include "surface.h" + +struct wig_window { + char *label; + struct wig_surface surface; + struct zwlr_layer_surface_v1 *zwlr_layer_surface_v1; +}; + +int wig_window_init(struct wig_window *window, char *label, + struct wig_surface surface, + struct zwlr_layer_shell_v1 *zwlr_layer_shell_v1); + +#endif diff --git a/src/wl.c b/src/wl.c @@ -1,44 +0,0 @@ -#include <errno.h> -#include <fcntl.h> -#include <sys/mman.h> -#include <time.h> -#include <unistd.h> - -static void randname(char* buf) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - long r = ts.tv_nsec; - for (int i = 0; i < 6; ++i) { - buf[i] = 'A' + (r & 15) + (r & 16) * 2; - r >>= 5; - } -} - -static int create_shm_file(void) { - int retries = 100; - do { - char name[] = "/wl_shm-XXXXXX"; - randname(name + sizeof(name) - 7); - --retries; - int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); - if (fd >= 0) { - shm_unlink(name); - return fd; - } - } while (retries > 0 && errno == EEXIST); - return -1; -} - -int allocate_shm_file(size_t size) { - int fd = create_shm_file(); - if (fd < 0) return -1; - int ret; - do { - ret = ftruncate(fd, size); - } while (ret < 0 && errno == EINTR); - if (ret < 0) { - close(fd); - return -1; - } - return fd; -} diff --git a/src/wl.h b/src/wl.h @@ -1,6 +0,0 @@ -#ifndef WIG_WL -#define WIG_WL - -int allocate_shm_file(size_t size); - -#endif