@@ 1,20 1,20 @@
#define _POSIX_C_SOURCE 200809L
#include <assert.h>
-#include <sys/poll.h>
-#include <EGL/egl.h>
-#include <GLES2/gl2.h>
-#include <GL/glut.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
+#include <sys/poll.h>
+#include <time.h>
#include <unistd.h>
-#include <limits.h>
-#include <math.h>
#include <wayland-client.h>
#include <wayland-client-protocol.h>
-#include <wayland-egl.h>
+
#ifdef __linux__
#include <linux/input-event-codes.h>
#elif __FreeBSD__
@@ 40,8 40,130 @@ enum event_loop_fd {
EVENT_LOOP_PIPEWIRE,
};
+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 anonymous_shm_open(void) {
+ char name[] = "/wlavu-XXXXXX";
+ int retries = 100;
+
+ do {
+ randname(name + strlen(name) - 6);
+ --retries;
+ // shm_open guarantees that O_CLOEXEC is set
+ 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 create_shm_file(off_t size) {
+ int fd = anonymous_shm_open();
+ if (fd < 0) {
+ return fd;
+ }
+
+ if (ftruncate(fd, size) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+struct shm_buf {
+ uint32_t *data;
+ int width;
+ int height;
+ int size;
+ bool inuse;
+
+ struct wl_buffer *buffer;
+};
+
+static void wl_buffer_release(void *data, struct wl_buffer *wl_buffer) {
+ struct shm_buf *buf = data;
+ buf->inuse = false;
+}
+
+static const struct wl_buffer_listener wl_buffer_listener = {
+ .release = wl_buffer_release,
+};
+
+static int prepare_buffer(struct shm_buf *buf, struct wl_shm *shm, int width, int height) {
+ if (buf == NULL) {
+ return -1;
+ }
+ if (buf->data != NULL && buf->width == width && buf->height == height) {
+ return 0;
+ }
+
+ buf->width = width;
+ buf->height = height;
+
+ int stride = width * 4;
+ int size = stride * height;
+ if (buf->data != NULL) {
+ munmap(buf->data, buf->size);
+ buf->data = NULL;
+ }
+ buf->size = size;
+
+ int fd = create_shm_file(size);
+ if (fd == -1) {
+ return -1;
+ }
+ buf->data = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (buf->data == MAP_FAILED) {
+ close(fd);
+ return -1;
+ }
+
+ struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size);
+ buf->buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888);
+ wl_shm_pool_destroy(pool);
+ close(fd);
+
+ wl_buffer_add_listener(buf->buffer, &wl_buffer_listener, buf);
+ return 0;
+}
+
+static void use_buf(struct shm_buf *buf) {
+ buf->inuse = true;
+}
+
+struct shm_bufs {
+ struct shm_buf bufs[3];
+ int next;
+};
+
+static struct shm_buf *next_buf(struct shm_bufs *bufs) {
+ if (!bufs->bufs[0].inuse) {
+ return &bufs->bufs[0];
+ } else if (!bufs->bufs[1].inuse) {
+ return &bufs->bufs[1];
+ } else if (!bufs->bufs[2].inuse) {
+ return &bufs->bufs[2];
+ } else {
+ fprintf(stderr, "all buffers in use\n");
+ return NULL;
+ }
+}
+
struct context {
struct wl_display *display;
+ struct wl_shm *shm;
struct wl_compositor *compositor;
struct wl_surface *surface;
struct xdg_wm_base *xdg_wm_base;
@@ 57,12 179,15 @@ struct context {
struct pw_loop *pw_loop;
struct pw_stream *pw_stream;
+ struct shm_bufs bufs;
+
int width;
int height;
bool dirty;
bool pending;
bool running;
+ int last_extend;
int channels;
int rate;
@@ 74,12 199,6 @@ struct context {
double last_val;
double last_peak;
double peak_decay;
-
- struct wl_egl_window *wl_egl_window;
- EGLConfig egl_config;
- EGLContext egl_context;
- EGLDisplay egl_display;
- EGLSurface egl_surface;
};
static void layer_surface_configure(void *data,
@@ 88,13 207,6 @@ static void layer_surface_configure(void *data,
struct context *ctx = data;
ctx->width = width;
ctx->height = height;
- if (ctx->wl_egl_window) {
- wl_egl_window_resize(ctx->wl_egl_window, width, height, 0, 0);
- }
- if (ctx->egl_surface) {
- eglMakeCurrent(ctx->egl_display, ctx->egl_surface, ctx->egl_surface, ctx->egl_context);
- glViewport(0, 0, width, height);
- }
zwlr_layer_surface_v1_ack_configure(surface, serial);
}
@@ 111,6 223,7 @@ struct zwlr_layer_surface_v1_listener layer_surface_listener = {
.closed = layer_surface_closed,
};
+static int draw(struct context *ctx);
static void xdg_toplevel_configure(void *data,
struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height,
struct wl_array *states) {
@@ 123,13 236,8 @@ static void xdg_toplevel_configure(void *data,
}
ctx->width = width;
ctx->height = height;
- if (ctx->wl_egl_window) {
- wl_egl_window_resize(ctx->wl_egl_window, width, height, 0, 0);
- }
- if (ctx->egl_surface != NULL) {
- eglMakeCurrent(ctx->egl_display, ctx->egl_surface, ctx->egl_surface, ctx->egl_context);
- glViewport(0, 0, width, height);
- }
+ ctx->dirty = true;
+ draw(ctx);
}
static void xdg_surface_handle_configure(void *data,
@@ 200,7 308,9 @@ static void handle_global(void *data, struct wl_registry *registry,
wl_seat_add_listener(ctx->seat, &seat_listener, ctx);
} else if (strcmp(interface, wl_compositor_interface.name) == 0) {
ctx->compositor =
- wl_registry_bind(registry, name, &wl_compositor_interface, 1);
+ wl_registry_bind(registry, name, &wl_compositor_interface, 4);
+ } else if (strcmp(interface, wl_shm_interface.name) == 0) {
+ ctx->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
ctx->xdg_wm_base =
wl_registry_bind(registry, name, &xdg_wm_base_interface, 1);
@@ 221,20 331,6 @@ static const struct wl_registry_listener registry_listener = {
.global_remove = handle_global_remove,
};
-static void draw_horiz(double val) {
- glVertex2f(-1,-1);
- glVertex2f(2*val-1, -1);
- glVertex2f(2*val-1, 1);
- glVertex2f(-1, 1);
-}
-
-static void draw_vert(double val) {
- glVertex2f(-1,-1);
- glVertex2f(1, -1);
- glVertex2f(1, 2*val-1);
- glVertex2f(-1, 2*val-1);
-}
-
static int draw(struct context *ctx);
static void frame_handle_done(void *data, struct wl_callback *callback,
uint32_t time) {
@@ 249,6 345,63 @@ const struct wl_callback_listener frame_listener = {
.done = frame_handle_done,
};
+static int max(int a, int b) {
+ return a > b ? a : b;
+}
+
+static int draw_vert(struct context *ctx, struct shm_buf *buf) {
+ int peak_rows = (ctx->peak * (double)buf->height);
+ int val_rows = (ctx->val * (double)buf->height);
+
+ int peak_start = (buf->height - peak_rows) * buf->width;
+ int val_start = (buf->height - val_rows) * buf->width;
+
+ int black_color = 0xFF000000;
+ int val_color = 0xFF00FF00;
+ int peak_color = 0xFF404040;
+ if (ctx->peak > 0.9) {
+ peak_color = 0xFF800000;
+ }
+
+ int idx = 0;
+ while (idx < peak_start) {
+ buf->data[idx++] = black_color;
+ }
+ while (idx < val_start) {
+ buf->data[idx++] = peak_color;
+ }
+ while (idx < buf->height * buf->width) {
+ buf->data[idx++] = val_color;
+ }
+ return peak_rows;
+}
+
+static int draw_horiz(struct context *ctx, struct shm_buf *buf) {
+ int peak_cols = (ctx->peak * (double)buf->width);
+ int val_cols = (ctx->val * (double)buf->width);
+
+ int black_color = 0xFF000000;
+ int val_color = 0xFF00FF00;
+ int peak_color = 0xFF404040;
+ if (ctx->peak > 0.9) {
+ peak_color = 0xFF800000;
+ }
+ for (int row = 0; row < buf->height; row++) {
+ int offset = row * buf->width;
+ int idx = offset;
+ while (idx < offset + val_cols) {
+ buf->data[idx++] = val_color;
+ }
+ while (idx < offset + peak_cols) {
+ buf->data[idx++] = peak_color;
+ }
+ while (idx < offset + buf->width) {
+ buf->data[idx++] = black_color;
+ }
+ }
+ return peak_cols;
+}
+
static int draw(struct context *ctx) {
if (ctx->pending) {
return 0;
@@ 258,41 411,33 @@ static int draw(struct context *ctx) {
}
ctx->dirty = false;
- glClear(GL_COLOR_BUFFER_BIT);
-
- void (*draw_bar)(double val);
- if (ctx->height > ctx->width) {
- draw_bar = draw_vert;
- } else {
- draw_bar = draw_horiz;
+ struct shm_buf *buf = next_buf(&ctx->bufs);
+ if (prepare_buffer(buf, ctx->shm, ctx->width, ctx->height) == -1) {
+ return -1;
}
- if (ctx->peak > 0.9) {
- glColor3f(.5, 0.0, 0.0);
+ use_buf(buf);
+
+ struct wl_callback *callback = wl_surface_frame(ctx->surface);
+ wl_callback_add_listener(callback, &frame_listener, ctx);
+ ctx->pending = true;
+
+ if (buf->height > buf->width) {
+ int extend = draw_vert(ctx, buf);
+ wl_surface_damage_buffer(ctx->surface, 0, ctx->height - max(ctx->last_extend, extend), buf->width, max(ctx->last_extend, extend));
+ ctx->last_extend = extend;
} else {
- glColor3f(0.25, .25, 0.25);
+ int extend = draw_horiz(ctx, buf);
+ wl_surface_damage_buffer(ctx->surface, 0, 0, max(ctx->last_extend, extend), buf->height);
+ ctx->last_extend = extend;
}
- glBegin(GL_POLYGON);
- draw_bar(ctx->peak);
- glEnd();
- glColor3f(0.0, 1.0, 0.0);
- glBegin(GL_POLYGON);
- draw_bar(ctx->val);
- glEnd();
+ wl_surface_attach(ctx->surface, buf->buffer, 0, 0);
+ wl_surface_commit(ctx->surface);
- ctx->pending = true;
if (ctx->peak_decay != 0) {
ctx->peak_decay--;
}
- struct wl_callback *callback = wl_surface_frame(ctx->surface);
- wl_callback_add_listener(callback, &frame_listener, ctx);
-
- if (!eglSwapBuffers(ctx->egl_display, ctx->egl_surface)) {
- fprintf(stderr, "eglSwapBuffers failed\n");
- return -1;
- }
-
return 0;
}
@@ 343,15 488,11 @@ static bool equal_within(double a, double b, double hysteresis) {
}
static double max_f32(void *buffer, int frames) {
- float max = 0;
+ double max = 0;
for (int idx = 0; idx < frames; idx++) {
- float val = fabsf(((float*)buffer)[idx]);
- if (val > max) {
- max = val;
- }
+ max = fmax(fabs(((float*)buffer)[idx]), max);
}
-
- return max;
+ return fmin(max, 1.0);
}
static void on_process(void *userdata) {
@@ 479,47 620,6 @@ static int setup_wayland(struct context *ctx, bool layer_shell, bool vertical, i
return -1;
}
- ctx->egl_display = eglGetDisplay((EGLNativeDisplayType)ctx->display);
- if (ctx->egl_display == EGL_NO_DISPLAY) {
- fprintf(stderr, "failed to create EGL display\n");
- return -1;
- }
-
- eglBindAPI(EGL_OPENGL_API);
-
- EGLint major, minor;
- if (!eglInitialize(ctx->egl_display, &major, &minor)) {
- fprintf(stderr, "failed to initialize EGL\n");
- return -1;
- }
-
- EGLint count;
- eglGetConfigs(ctx->egl_display, NULL, 0, &count);
-
- EGLint config_attribs[] = {
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_NONE,
- };
- EGLint n = 0;
- EGLConfig *configs = calloc(count, sizeof(EGLConfig));
- eglChooseConfig(ctx->egl_display, config_attribs, configs, count, &n);
- if (n == 0) {
- fprintf(stderr, "failed to choose an EGL config\n");
- return -1;
- }
- ctx->egl_config = configs[0];
-
- EGLint context_attribs[] = {
- EGL_CONTEXT_CLIENT_VERSION, 2,
- EGL_NONE,
- };
- ctx->egl_context = eglCreateContext(ctx->egl_display, ctx->egl_config,
- EGL_NO_CONTEXT, context_attribs);
-
ctx->surface = wl_compositor_create_surface(ctx->compositor);
if (!layer_shell) {
@@ 552,17 652,6 @@ static int setup_wayland(struct context *ctx, bool layer_shell, bool vertical, i
wl_surface_commit(ctx->surface);
wl_display_roundtrip(ctx->display);
-
- ctx->wl_egl_window = wl_egl_window_create(ctx->surface, ctx->width, ctx->height);
- ctx->egl_surface = eglCreateWindowSurface(ctx->egl_display, ctx->egl_config,
- (EGLNativeWindowType)ctx->wl_egl_window, NULL);
-
- if (!eglMakeCurrent(ctx->egl_display, ctx->egl_surface, ctx->egl_surface, ctx->egl_context)) {
- fprintf(stderr, "Could not make EGL context current\n");
- return -1;
- }
-
- eglSwapInterval(ctx->egl_display, 0);
return 0;
}
@@ 650,8 739,6 @@ int main(int argc, char **argv) {
return EXIT_FAILURE;
}
- glutInit(&argc, argv);
-
if (setup_wayland(&ctx, use_layer_shell, vertical, anchor) == -1) {
return EXIT_FAILURE;
}