diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2023-04-06 23:29:18 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2023-04-06 23:29:18 +0200 |
| commit | 63288c4d50dea63a5a403b86762cf66c9db35325 (patch) | |
| tree | 1874b283366216f6a91a0084a63a9c44da3d6c2b | |
create green window using xdg-surface
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Makefile | 33 | ||||
| -rw-r--r-- | config.def.h | 0 | ||||
| -rw-r--r-- | config.h | 0 | ||||
| -rw-r--r-- | drw.c | 271 | ||||
| -rw-r--r-- | drw.h | 44 | ||||
| -rw-r--r-- | swt.c | 173 | ||||
| -rw-r--r-- | util.c | 11 | ||||
| -rw-r--r-- | util.h | 6 | ||||
| -rw-r--r-- | wayland.c | 66 | ||||
| -rw-r--r-- | wayland.h | 18 |
11 files changed, 625 insertions, 0 deletions
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 --- /dev/null +++ b/config.def.h diff --git a/config.h b/config.h new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/config.h @@ -0,0 +1,271 @@ +#include <sys/mman.h> +#include <unistd.h> +#include <math.h> +#include <grapheme.h> + +#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; +} @@ -0,0 +1,44 @@ +#ifndef DRW_H +#define DRW_H + +#include <stdint.h> +#include <ft2build.h> +#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 @@ -0,0 +1,173 @@ +#include <string.h> +#include <stdio.h> + +#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; +} @@ -0,0 +1,11 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "util.h" + +void +die(const char *error) +{ + fprintf(stderr, "error: %s\n", error); + exit(-1); +} @@ -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 <errno.h> +#include <time.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <unistd.h> + +#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 <wayland-client.h> +#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 |