aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Reiner <nathan@nathanreiner.xyz>2023-04-06 23:29:18 +0200
committerNathan Reiner <nathan@nathanreiner.xyz>2023-04-06 23:29:18 +0200
commit63288c4d50dea63a5a403b86762cf66c9db35325 (patch)
tree1874b283366216f6a91a0084a63a9c44da3d6c2b
create green window using xdg-surface
-rw-r--r--.gitignore3
-rw-r--r--Makefile33
-rw-r--r--config.def.h0
-rw-r--r--config.h0
-rw-r--r--drw.c271
-rw-r--r--drw.h44
-rw-r--r--swt.c173
-rw-r--r--util.c11
-rw-r--r--util.h6
-rw-r--r--wayland.c66
-rw-r--r--wayland.h18
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
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 <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;
+}
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 <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
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 <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, &registry_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 <stdio.h>
+#include <stdlib.h>
+
+#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 <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