From 63288c4d50dea63a5a403b86762cf66c9db35325 Mon Sep 17 00:00:00 2001 From: Nathan Reiner Date: Thu, 6 Apr 2023 23:29:18 +0200 Subject: create green window using xdg-surface --- .gitignore | 3 + Makefile | 33 ++++++++ config.def.h | 0 config.h | 0 drw.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ drw.h | 44 ++++++++++ swt.c | 173 ++++++++++++++++++++++++++++++++++++++ util.c | 11 +++ util.h | 6 ++ wayland.c | 66 +++++++++++++++ wayland.h | 18 ++++ 11 files changed, 625 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 config.def.h create mode 100644 config.h create mode 100644 drw.c create mode 100644 drw.h create mode 100644 swt.c create mode 100644 util.c create mode 100644 util.h create mode 100644 wayland.c create mode 100644 wayland.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..388e8ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +swt +*.o +*-protocol.* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d34a6af --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +PROTOCOLS = xdg-shell-client-protocol.c +SRC = ${PROTOCOLS} swt.c drw.c util.c wayland.c +OBJ = ${SRC:.c=.o} +CFLAGS = -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/harfbuzz -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/sysprof-4 -pthread +LIBS = -lfreetype -lwayland-client -lrt -lxkbcommon -lm -lgrapheme + +all: swt + +config.h: + cp config.def.h config.h + +xdg-shell-client-protocol.h: + wayland-scanner client-header < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml > xdg-shell-client-protocol.h + +xdg-shell-client-protocol.c: xdg-shell-client-protocol.h + wayland-scanner private-code < /usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml > xdg-shell-client-protocol.c + +.c.o: + gcc -c $< ${CFLAGS} + +${OBJ}: ${PROTOCOLS} config.h + +swt: ${OBJ} ${SRC} + gcc ${OBJ} -o swt ${LIBS} + +install: swt + install -D ./swt /usr/local/bin/swt + +run: swt + ./swt + +clean: + rm *.o swt ${PROTOCOLS} ${PROTOCOLS:.c=.h} diff --git a/config.def.h b/config.def.h new file mode 100644 index 0000000..e69de29 diff --git a/config.h b/config.h new file mode 100644 index 0000000..e69de29 diff --git a/drw.c b/drw.c new file mode 100644 index 0000000..f65f330 --- /dev/null +++ b/drw.c @@ -0,0 +1,271 @@ +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +/* static function declarations */ +static Color to_color(uint32_t color); +static uint32_t to_uint32_t(Color color); +static void draw_bitmap(Canvas *canvas, FT_Bitmap *bitmap, unsigned x, unsigned y, uint32_t color); + + +Color +to_color(uint32_t color) +{ + return *(Color*)(&color); +} + + +uint32_t +to_uint32_t(Color color) +{ + return *(uint32_t*)(&color); +} + + +Canvas* +create_drw(struct wl_shm *shm, unsigned width, unsigned height) +{ + struct wl_shm_pool *pool; + Canvas *canvas; + int stride; + int fd; + + canvas = malloc(sizeof(Canvas)); + canvas->width = width; + canvas->height = height; + + stride = width * sizeof(uint32_t); + canvas->size = stride * height; + + fd = allocate_shm_file(canvas->size); + if (fd == -1) { + return NULL; + } + + canvas->data = mmap( + NULL, + canvas->size, + PROT_READ | PROT_WRITE, + MAP_SHARED, + fd, + 0 + ); + + if (canvas->data == MAP_FAILED) { + close(fd); + return NULL; + } + + pool = wl_shm_create_pool( + shm, + fd, + canvas->size + ); + + canvas->buffer = wl_shm_pool_create_buffer( + pool, + 0, + width, + height, + stride, + WL_SHM_FORMAT_XRGB8888 + ); + + wl_shm_pool_destroy(pool); + close(fd); + + return canvas; +} + + +void +free_drw(Canvas *canvas) +{ + munmap(canvas->data, canvas->size); + free(canvas); +} + + +void +draw_point(Canvas *canvas, unsigned x, unsigned y, uint32_t color) +{ + if (!(x < canvas->width && y < canvas->height)) + return; + + canvas->data[x + canvas->width * y] = color; +} + + +void +draw_rect(Canvas *canvas, unsigned x, unsigned y, unsigned width, unsigned height, uint32_t color) { + int py; + int px; + + for (py = y; py < y + height; ++py) { + for (px = x; px < x + width; ++px) { + draw_point(canvas, px, py, color); + } + } +} + +Font * +create_font(const char *fontpath, unsigned size) +{ + FT_Error error; + Font * font; + + font = malloc(sizeof(Font)); + + error = FT_Init_FreeType(&font->library); + + if (error) + die("cannot init library"); + + error = FT_New_Face(font->library, fontpath, 0, &font->face); + if (error) + die("cannot open face"); + + error = FT_Set_Pixel_Sizes(font->face, 0, size); + if (error) + die("cannot set char size"); + + error = FT_Select_Charmap(font->face, ft_encoding_unicode); + if (error) + die("cannot set unicode"); + + return font; +} + +void +free_font(Font *font) +{ + FT_Done_Face(font->face); + FT_Done_FreeType(font->library); + free(font); +} + + +void +draw_bitmap(Canvas *canvas, FT_Bitmap *bitmap, unsigned x, unsigned y, uint32_t color) +{ + FT_Int i, j, p, q; + double strength; + uint32_t pixel; + + Color fg = to_color(color); + Color bg; + Color c; + + c.a = fg.a; + + i = x; + p = 0; + + while (p < bitmap->width) { + j = y; + q = 0; + + while (q < bitmap->rows) { + if (i < 0 || j < 0 || i >= canvas->width || j >= canvas->height) { + j++; + q++; + continue; + } + + bg = to_color(canvas->data[i + j * canvas->width]); + strength = bitmap->buffer[p + q * bitmap->width]; + strength /= 255; + + c.r = (strength * fg.r) + ((1 - strength) * bg.r); + c.g = (strength * fg.g) + ((1 - strength) * bg.g); + c.b = (strength * fg.b) + ((1 - strength) * bg.b); + + pixel = to_uint32_t(c); + draw_point(canvas, i, j, pixel); + + j++; + q++; + } + + i++; + p++; + } +} + + +unsigned +draw_font(Canvas *canvas, Font *font, const char *text, unsigned x, unsigned y, uint32_t color) +{ + FT_GlyphSlot slot; + FT_Error error; + FT_UInt glyph_index; + FT_UInt previous = 0; + uint_least32_t charcode; + FT_Bool has_kerning = FT_HAS_KERNING(font->face); + FT_Vector delta; + + slot = font->face->glyph; + + for (; *text;) { + text += grapheme_decode_utf8(text, sizeof(uint_least32_t), &charcode); + + glyph_index = FT_Get_Char_Index(font->face, charcode); + + if (has_kerning && previous && glyph_index) { + FT_Get_Kerning(font->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); + x += delta.x >> 6; + } + + error = FT_Load_Glyph(font->face, glyph_index, FT_LOAD_RENDER); + if (error) { + fprintf(stderr, "warning: char not loaded, skipping...\n"); + continue; + } + + draw_bitmap(canvas, &slot->bitmap, x + slot->bitmap_left, y - slot->bitmap_top, color); + + x += slot->advance.x >> 6; + } + + return x; +} + +unsigned +font_width(Font *font, const char *text) +{ + FT_GlyphSlot slot; + FT_Error error; + FT_UInt glyph_index; + FT_UInt previous = 0; + uint_least32_t charcode; + FT_Bool has_kerning = FT_HAS_KERNING(font->face); + FT_Vector delta; + unsigned x = 0; + + slot = font->face->glyph; + + for (; *text;) { + text += grapheme_decode_utf8(text, sizeof(uint_least32_t), &charcode); + + glyph_index = FT_Get_Char_Index(font->face, charcode); + + if (has_kerning && previous && glyph_index) { + FT_Get_Kerning(font->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); + x += delta.x >> 6; + } + + error = FT_Load_Glyph(font->face, glyph_index, FT_LOAD_RENDER); + if (error) { + fprintf(stderr, "warning: char not loaded, skipping...\n"); + continue; + } + + x += slot->advance.x >> 6; + } + + return x; +} diff --git a/drw.h b/drw.h new file mode 100644 index 0000000..84bec3b --- /dev/null +++ b/drw.h @@ -0,0 +1,44 @@ +#ifndef DRW_H +#define DRW_H + +#include +#include +#include FT_FREETYPE_H +#include FT_BITMAP_H + +#include "wayland.h" + +/* type definitions */ +typedef struct { + uint32_t *data; + unsigned width; + unsigned height; + unsigned size; + struct wl_buffer *buffer; +} Canvas; + +typedef struct { + FT_Library library; + FT_Face face; +} Font; + +typedef struct { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +} Color; + +/* exported functions */ +Canvas* create_drw(struct wl_shm *shm, unsigned width, unsigned height); +void free_drw(Canvas *canvas); + +void draw_rect(Canvas *canvas, unsigned x, unsigned y, unsigned width, unsigned height, uint32_t color); +void draw_point(Canvas *canvas, unsigned x, unsigned y, uint32_t color); + +Font* create_font(const char *fontpath, unsigned size); +void free_font(Font *font); +unsigned draw_font(Canvas *canvas, Font *font, const char *text, unsigned x, unsigned y, uint32_t color); +unsigned font_width(Font *font, const char *text); + +#endif diff --git a/swt.c b/swt.c new file mode 100644 index 0000000..d204bb0 --- /dev/null +++ b/swt.c @@ -0,0 +1,173 @@ +#include +#include + +#include "wayland.h" +#include "drw.h" +#include "xdg-shell-client-protocol.h" + +/* macro definitions */ +#define match_then_bind(obj, inter, ver) \ + if (strcmp(interface, inter.name) == 0) { \ + obj = wl_registry_bind(registry, name, &inter, ver); + +#define end_match } +#define or_match } else + +/* struct definitions */ +typedef struct { + unsigned width; + unsigned height; + struct { + struct wl_surface *wl; + struct xdg_surface *xdg; + struct xdg_toplevel *toplevel; + } surface; + Canvas *canvas; + Font *font; +} Window; + + +static void buffer_release(void *data, struct wl_buffer *buffer); +static void draw_frame(); +static void surface_configure(void *data, struct xdg_surface *surface, uint32_t serial); +static void toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states); +static void toplevel_close(void *data, struct xdg_toplevel *toplevel); +static void wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial); +static void registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); +static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name); + + +/* global variables */ +static const struct wl_registry_listener registry_listener = { + .global = registry_global, + .global_remove = registry_global_remove +}; + +static const struct wl_buffer_listener buffer_listener = { + .release = buffer_release +}; + +static const struct xdg_surface_listener surface_listener = { + .configure = surface_configure, +}; + +static const struct xdg_toplevel_listener toplevel_listener = { + .configure = toplevel_configure, + .close = toplevel_close +}; + +static const struct xdg_wm_base_listener wm_base_listener = { + .ping = wm_base_ping +}; + +struct client_state state = { 0 }; +Window win = { 0 }; +int running = 1; + +#include "config.h" + +/* function implementation */ +void +buffer_release(void *data, struct wl_buffer *buffer) +{ + wl_buffer_destroy(buffer); +} + + +void +draw_frame() +{ + draw_rect(win.canvas, 0, 0, win.width, win.height, 0xff00ff00); +} + + +void +surface_configure(void *data, struct xdg_surface *surface, uint32_t serial) +{ + xdg_surface_ack_configure(surface, serial); + + fprintf(stderr, "configure\n"); + + if (win.canvas != 0) { + free_drw(win.canvas); + } + win.canvas = create_drw(state.shm, win.width, win.height); + + draw_frame(); + wl_surface_attach(win.surface.wl, win.canvas->buffer, 0, 0); + wl_surface_damage_buffer(win.surface.wl, 0, 0, win.width, win.height); + wl_surface_commit(win.surface.wl); +} + + +void +toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states) +{ + if (width == 0 || height == 0) { + win.width = 800; + win.height = 600; + } else { + win.width = width; + win.height = height; + } +} + + +void +toplevel_close(void *data, struct xdg_toplevel *toplevel) +{ + running = 0; +} + + +void +wm_base_ping(void *data, struct xdg_wm_base *wm_base, uint32_t serial) +{ + xdg_wm_base_pong(wm_base, serial); +} + + +void +registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) +{ + match_then_bind(state.shm, wl_shm_interface, 1) + or_match match_then_bind(state.compositor, wl_compositor_interface, 4) + or_match match_then_bind(state.wm_base, xdg_wm_base_interface, 1) + /* TODO: or_match match_then_bind(state.seat, wl_seat_interface, 7) + wl_seat_add_listener(state.seat, &seat_listener, &state);*/ + end_match +} + + +void +registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) +{ + /* TODO */ +} + + +int main() +{ + state.display = wl_display_connect(0); + state.registry = wl_display_get_registry(state.display); + wl_registry_add_listener(state.registry, ®istry_listener, &state); + wl_display_roundtrip(state.display); + + xdg_wm_base_add_listener(state.wm_base, &wm_base_listener, &state); + wl_display_roundtrip(state.display); + + win.surface.wl = wl_compositor_create_surface(state.compositor); + win.surface.xdg = xdg_wm_base_get_xdg_surface(state.wm_base, win.surface.wl); + xdg_surface_add_listener(win.surface.xdg, &surface_listener, &win); + win.surface.toplevel = xdg_surface_get_toplevel(win.surface.xdg); + xdg_toplevel_add_listener(win.surface.toplevel, &toplevel_listener, &win); + xdg_toplevel_set_title(win.surface.toplevel, "swt"); + wl_surface_commit(win.surface.wl); + + while (wl_display_dispatch(state.display) && running) { + } + + wl_display_disconnect(state.display); + + return 0; +} diff --git a/util.c b/util.c new file mode 100644 index 0000000..2e8a1c8 --- /dev/null +++ b/util.c @@ -0,0 +1,11 @@ +#include +#include + +#include "util.h" + +void +die(const char *error) +{ + fprintf(stderr, "error: %s\n", error); + exit(-1); +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..0feb54a --- /dev/null +++ b/util.h @@ -0,0 +1,6 @@ +#ifndef UTIL_H +#define UTIL_H + +void die(const char *error); + +#endif diff --git a/wayland.c b/wayland.c new file mode 100644 index 0000000..0787ec3 --- /dev/null +++ b/wayland.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include + +#include "wayland.h" + +static void randname(char *buf); +static int create_shm_file(void); + + +void +randname(char *buf) +{ + struct timespec ts; + long r; + int i; + + clock_gettime(CLOCK_REALTIME, &ts); + r = ts.tv_nsec; + for (i = 0; i < 6; ++i) { + buf[i] = 'A'+(r&15)+(r&16)*2; + r >>= 5; + } +} + +int +create_shm_file(void) +{ + int fd; + int retries = 100; + char name[] = "/wl_shm-XXXXXX"; + + do { + randname(name + sizeof(name) - 7); + --retries; + 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 ret; + int fd = create_shm_file(); + + if (fd < 0) + return -1; + + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + close(fd); + return -1; + } + + return fd; +} diff --git a/wayland.h b/wayland.h new file mode 100644 index 0000000..494c0e3 --- /dev/null +++ b/wayland.h @@ -0,0 +1,18 @@ +#ifndef WAYLAND_H +#define WAYLAND_H + +#include +#include "xdg-shell-client-protocol.h" + +struct client_state { + struct wl_display *display; + struct wl_registry *registry; + struct wl_shm *shm; + struct wl_compositor *compositor; + struct xdg_wm_base *wm_base; + struct wl_seat *seat; +}; + +int allocate_shm_file(size_t size); + +#endif -- cgit v1.2.3-70-g09d2