From a48a68cfb04b2020737c0adfb0e7667451a22b5c Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Mon, 6 Nov 2023 11:54:08 -0500 Subject: Add camu - Subprojects temporarily omitted Signed-off-by: Andrew Opalach --- src/screen/meson.build | 4 + src/screen/screen.c | 402 +++++++++++++++++++++++++++++++++++++++++++++++++ src/screen/screen.h | 78 ++++++++++ src/screen/view.c | 28 ++++ src/screen/view.h | 23 +++ 5 files changed, 535 insertions(+) create mode 100644 src/screen/meson.build create mode 100644 src/screen/screen.c create mode 100644 src/screen/screen.h create mode 100644 src/screen/view.c create mode 100644 src/screen/view.h (limited to 'src/screen') diff --git a/src/screen/meson.build b/src/screen/meson.build new file mode 100644 index 0000000..cd78988 --- /dev/null +++ b/src/screen/meson.build @@ -0,0 +1,4 @@ +screen_src = ['screen.c', 'view.c'] +sekihi = subproject('sekihi').get_variable('sekihi') +screen = declare_dependency(sources: screen_src, + dependencies: [sekihi]) diff --git a/src/screen/screen.c b/src/screen/screen.c new file mode 100644 index 0000000..82c7b12 --- /dev/null +++ b/src/screen/screen.c @@ -0,0 +1,402 @@ +#include +#include + +#include "screen.h" + +#define ZOOM_MAX 100.0 +#define ZOOM_MIN 0.02 + +#define PAN_BOUNDS 35.0 + +#define X_IN_BOUNDS(scr, view, x) \ + ((x + PAN_BOUNDS <= scr->width) && (PAN_BOUNDS - x <= view->width * view->zoom)) +#define Y_IN_BOUNDS(scr, view, y) \ + ((y + PAN_BOUNDS <= scr->height) && (PAN_BOUNDS - y <= view->height * view->zoom)) + +#define CAMU_DEFAULT_VIEW CAMU_VIEW_AUTOFIT + +static struct camu_view *get_view_from_mouse_pos(struct camu_screen *scr) +{ + struct camu_view *view = NULL; + struct camu_screen_video *video; + al_array_foreach_ptr(scr->videos, i, video) { + if (!view) { + view = &video->view; + continue; + } + if (view->zindex < video->view.zindex) { + view = &video->view; + } + } + return view; +} + +static void should_close_callback(void *userdata) +{ + struct camu_screen *scr = (struct camu_screen *)userdata; + scr->callback(scr->userdata, CAMU_SCREEN_CLOSE, 0.0); +} + +static void resize_callback(void *userdata, s32 width, s32 height) +{ + struct camu_screen *scr = (struct camu_screen *)userdata; + if (scr->renderer) { + scr->renderer->resize(scr->renderer, &width, &height); + } + scr->width = width; + scr->height = height; + struct camu_screen_video *video; + al_array_foreach_ptr(scr->videos, i, video) { + camu_view_calculate(&video->view, scr->width, scr->height); + } +} + +static void pointer_pos_callback(void *userdata, f64 x, f64 y) +{ + struct camu_screen *scr = (struct camu_screen *)userdata; + struct camu_view *view = get_view_from_mouse_pos(scr); + if (view) { + if (scr->flags & CAMU_SCREEN_DRAGGING) { + if (scr->flags & CAMU_SCREEN_ZOOM_PAN_SIMPLE) { + f64 nx = view->x_offset + x - scr->last_mouse_x; + if (X_IN_BOUNDS(scr, view, nx)) { + view->x_offset = nx; + } + f64 ny = view->y_offset + y - scr->last_mouse_y; + if (Y_IN_BOUNDS(scr, view, ny)) { + view->y_offset = ny; + } + view->mode = CAMU_VIEW_DETACHED; + } + scr->last_click_ts = 0; + } + } + scr->last_mouse_x = x; + scr->last_mouse_y = y; +} + +static void mouse_button_callback(void *userdata, u8 state, u8 button) +{ + struct camu_screen *scr = (struct camu_screen *)userdata; + switch (button) { + case SEKIHI_MOUSE1: + switch (state) { + case SEKIHI_BUTTON_PRESSED: + scr->flags |= CAMU_SCREEN_DRAGGING; + scr->last_click_ts = aki_get_timestamp(); + break; + case SEKIHI_BUTTON_RELEASED: + scr->flags &= ~CAMU_SCREEN_DRAGGING; + if (scr->last_click_ts && aki_get_timestamp() - scr->last_click_ts <= 300000) { + if (scr->flags & CAMU_SCREEN_MODIFIER) { + scr->callback(scr->userdata, CAMU_SCREEN_SEEK, scr->last_mouse_x / scr->width); + } else { + if (scr->last_mouse_x >= scr->width / 2.f) { + scr->callback(scr->userdata, CAMU_SCREEN_NEXT, 0.0); + } else { + scr->callback(scr->userdata, CAMU_SCREEN_PREVIOUS, 0.0); + } + } + } + break; + } + break; + case SEKIHI_MOUSE2: + break; // Move window. + default: + break; + } +} + +static void zoom_on_point(struct camu_screen *scr, struct camu_view *view, f64 x, f64 y, f64 v) +{ + x -= view->x_offset; + y -= view->y_offset; + x /= view->zoom; + y /= view->zoom; + + f64 prev_width = scr->width * view->zoom; + f64 prev_height = scr->height * view->zoom; + + f64 x_ratio = x / (f64)scr->width; + f64 y_ratio = y / (f64)scr->height; + + f64 zoom = view->zoom + (v * view->zoom); + if (zoom > ZOOM_MAX || zoom < ZOOM_MIN) return; + + f64 nx = view->x_offset - x_ratio * ((scr->width * zoom) - prev_width); + f64 ny = view->y_offset - y_ratio * ((scr->height * zoom) - prev_height); + if (X_IN_BOUNDS(scr, view, nx) && Y_IN_BOUNDS(scr, view, ny)) { + view->x_offset = nx; + view->y_offset = ny; + view->zoom = zoom; + } +} + +static void scroll_callback(void *userdata, f64 y) +{ + struct camu_screen *scr = (struct camu_screen *)userdata; + + struct camu_view *view = get_view_from_mouse_pos(scr); + if (!view) return; + + if (scr->flags & CAMU_SCREEN_ZOOM_PAN_SIMPLE) { + y = -y; +#if defined SEKIHI_WINDOW_GLFW + y /= 4.5; +#elif defined SEKIHI_WINDOW_WAYLAND + y /= 100.0; +#else + y /= 14.0; +#endif + zoom_on_point(scr, view, scr->last_mouse_x, scr->last_mouse_y, y); + view->mode = CAMU_VIEW_DETACHED; + } +} + +static void key_callback(void *userdata, u8 state, u8 button) +{ + struct camu_screen *scr = (struct camu_screen *)userdata; + switch (state) { + case SEKIHI_BUTTON_PRESSED: + switch (button) { + case 0x10: + case 0x18: + case 'q': + scr->callback(scr->userdata, CAMU_SCREEN_CLOSE, 0.0); + break; + case 0x6a: + case 0x31: + case 'n': + scr->callback(scr->userdata, CAMU_SCREEN_NEXT, 0.0); + break; + case 0x69: + case 0x30: + case 'b': + scr->callback(scr->userdata, CAMU_SCREEN_PREVIOUS, 0.0); + break; + case 0x41: + case 0x39: + scr->callback(scr->userdata, CAMU_SCREEN_TOGGLE_PAUSE, 0.0); + break; + case 0x13: + case 0x1b: + case 'r': { + struct camu_view *view = get_view_from_mouse_pos(scr); + if (!view) return; + view->mode = CAMU_DEFAULT_VIEW; + camu_view_calculate(view, scr->width, scr->height); + break; + } + case 0x1d: + scr->flags |= CAMU_SCREEN_MODIFIER; + break; + default: + break; + } + break; + case SEKIHI_BUTTON_RELEASED: + switch (button) { + case 0x1d: + scr->flags &= ~CAMU_SCREEN_MODIFIER; + break; + } + default: + break; + } +} + +static void key_immediate_callback(void *userdata, u8 state, u8 button) +{ + struct camu_screen *scr = (struct camu_screen *)userdata; + switch (state) { + case SEKIHI_BUTTON_PRESSED: + switch (button) { + case 0x29: + case 0x21: + scr->window->toggle_fullscreen(scr->window); + break; + } + break; + default: + break; + } +} + +bool camu_screen_init(struct camu_screen *scr) +{ + al_atomic_s32_store(&scr->state, CAMU_SCREEN_PAUSED, AL_ATOMIC_RELAXED); + al_array_init(scr->videos); + scr->renderer = NULL; + scr->window = skh_window_create(); + scr->window->pointer_pos_callback = pointer_pos_callback; + scr->window->scroll_callback = scroll_callback; + scr->window->mouse_button_callback = mouse_button_callback; + scr->window->key_callback = key_callback; + scr->window->key_immediate_callback = key_immediate_callback; + scr->window->resize_callback = resize_callback; + scr->window->should_close_callback = should_close_callback; + scr->window->userdata = scr; +#ifdef CAMU_SCREEN_THREADED + al_array_init(scr->add_queue); + al_array_init(scr->rem_queue); + al_atomic_bool_store(&scr->queued, false, AL_ATOMIC_RELAXED); + aki_mutex_init(&scr->mutex); +#endif + scr->last_mouse_x = 0; + scr->last_mouse_y = 0; + scr->flags = CAMU_SCREEN_ZOOM_PAN_SIMPLE; + return true; +} + +bool camu_screen_create_window(struct camu_screen *scr, const char *name) +{ + if (!scr->window->create_window(scr->window, CAMU_SCREEN_WIDTH, CAMU_SCREEN_HEIGHT, name)) { + return false; + } + scr->width = scr->window->width; + scr->height = scr->window->height; + return true; +} + +bool camu_screen_create_renderer(struct camu_screen *scr, struct camu_renderer *renderer) +{ + scr->renderer = renderer; + return renderer->create_renderer(renderer, &scr->width, &scr->height, + scr->window->poll, +#if defined CAMU_RENDERER_VULKAN + scr->window->get_vk_proc_address, + scr->window->vk_create_surface, + scr->window->vk_get_extensions, +#elif defined CAMU_RENDERER_OPENGL + scr->window->get_gl_proc_address, + scr->window->gl_load_loader, + scr->window->gl_make_current, + scr->window->gl_release_current, + scr->window->gl_swap_buffers, +#endif + scr->window); +} + +static void add_buffer_internal(struct camu_screen *scr, struct camu_video_buffer *buf) +{ + struct camu_stream *stream = buf->stream; + struct camu_screen_video video = { + .buf = buf, + .view = { + .mode = CAMU_DEFAULT_VIEW, + .width = stream->video.width, + .height = stream->video.height, + .zindex = 0 + } + }; +#ifdef CAMU_SCREEN_THREADED + al_atomic_bool_store(&buf->ref, true, AL_ATOMIC_RELAXED); +#endif + camu_view_calculate(&video.view, scr->width, scr->height); + al_array_push(scr->videos, video); + scr->window->resize(scr->window, stream->video.width, stream->video.height); +} + +static void remove_buffer_internal(struct camu_screen *scr, struct camu_video_buffer *buf) +{ + struct camu_screen_video *video; + al_array_foreach_ptr(scr->videos, i, video) { + if (video->buf == buf) { +#ifdef CAMU_SCREEN_THREADED + al_atomic_bool_store(&buf->ref, false, AL_ATOMIC_RELAXED); +#endif + al_array_remove_at_iter(scr->videos, i); + break; + } + } +} + +void camu_screen_add_buffer(struct camu_screen *scr, struct camu_video_buffer *buf) +{ +#ifdef CAMU_SCREEN_THREADED + aki_mutex_lock(&scr->mutex); + al_array_push(scr->add_queue, buf); + al_array_reserve(scr->videos, scr->videos.size + scr->add_queue.size); + al_atomic_bool_store(&scr->queued, true, AL_ATOMIC_RELAXED); + aki_mutex_unlock(&scr->mutex); +#else + add_buffer_internal(scr, buf); +#endif + camu_screen_wake(scr); +} + +#ifdef CAMU_SCREEN_THREADED +static void run_queue_internal(struct camu_screen *scr) +{ + struct camu_video_buffer *buf; + al_array_foreach(scr->add_queue, i, buf) { + add_buffer_internal(scr, buf); + al_array_remove_at_iter(scr->add_queue, i); + } + al_array_foreach(scr->rem_queue, i, buf) { + remove_buffer_internal(scr, buf); + al_array_remove_at_iter(scr->rem_queue, i); + } +} +#endif + +void camu_screen_remove_buffer(struct camu_screen *scr, struct camu_video_buffer *buf) +{ +#ifdef CAMU_SCREEN_THREADED + aki_mutex_lock(&scr->mutex); + al_array_push(scr->rem_queue, buf); + al_atomic_bool_store(&scr->queued, true, AL_ATOMIC_RELAXED); + if (al_atomic_s32_load(&scr->state, AL_ATOMIC_RELAXED) == CAMU_SCREEN_CLOSED) { + run_queue_internal(scr); + } + aki_mutex_unlock(&scr->mutex); +#else + remove_buffer_internal(scr, buf); +#endif + camu_screen_wake(scr); +} + +#ifdef CAMU_SCREEN_THREADED +void camu_screen_run_queue(struct camu_screen *scr) +{ + aki_mutex_lock(&scr->mutex); + run_queue_internal(scr); + al_atomic_bool_store(&scr->queued, false, AL_ATOMIC_RELAXED); + aki_mutex_unlock(&scr->mutex); +} +#endif + +void camu_screen_set_state(struct camu_screen *scr, u8 state) +{ + al_atomic_s32_store(&scr->state, state, AL_ATOMIC_RELAXED); +} + +bool camu_screen_tick(struct camu_screen *scr) +{ +#if defined SEKIHI_POLL_CONSERVATIVE + scr->window->poll(scr->window, + al_atomic_s32_load(&scr->state, AL_ATOMIC_RELAXED) == CAMU_SCREEN_PAUSED); + return skh_window_read_events(scr->window); +#elif defined SEKIHI_POLL_INLINE + return scr->window->poll(scr->window, + al_atomic_s32_load(&scr->state, AL_ATOMIC_RELAXED) == CAMU_SCREEN_PAUSED); +#endif +} + +void camu_screen_wake(struct camu_screen *scr) +{ +#ifdef SEKIHI_PAUSE + scr->window->wake(scr->window); +#endif +} + +void camu_screen_close(struct camu_screen *scr) +{ +#ifdef CAMU_SCREEN_THREADED + al_array_free(scr->add_queue); + al_array_free(scr->rem_queue); + aki_mutex_destroy(&scr->mutex); +#endif + al_array_free(scr->videos); +} diff --git a/src/screen/screen.h b/src/screen/screen.h new file mode 100644 index 0000000..d45d5ee --- /dev/null +++ b/src/screen/screen.h @@ -0,0 +1,78 @@ +#pragma once + +#define CAMU_SCREEN_THREADED + +#include +#ifdef CAMU_SCREEN_THREADED +#include +#include +#endif +#include + +#include "../buffer/video.h" +#include "../render/renderer.h" +#include "../codec/codec.h" + +#include "view.h" + +#define CAMU_SCREEN_WIDTH 700 +#define CAMU_SCREEN_HEIGHT 700 + +enum { + CAMU_SCREEN_ZOOM_PAN_SIMPLE = 1, + CAMU_SCREEN_DRAGGING = 1 << 1, + CAMU_SCREEN_MODIFIER = 1 << 2, +}; + +enum { + CAMU_SCREEN_PAUSED = 0, + CAMU_SCREEN_PLAYING, + CAMU_SCREEN_CLOSED +}; + +enum { + CAMU_SCREEN_TOGGLE_PAUSE = 0, + CAMU_SCREEN_NEXT, + CAMU_SCREEN_PREVIOUS, + CAMU_SCREEN_SEEK, + CAMU_SCREEN_CLOSE +}; + +struct camu_screen_video { + struct camu_view view; + struct camu_video_buffer *buf; +}; + +struct camu_screen { + atomic_s32 state; + u32 flags; + struct skh_window *window; + struct camu_renderer *renderer; + s32 width; + s32 height; + u64 last_click_ts; + f64 last_mouse_y; + f64 last_mouse_x; + array(struct camu_screen_video) videos; +#ifdef CAMU_SCREEN_THREADED + array(struct camu_video_buffer *) add_queue; + array(struct camu_video_buffer *) rem_queue; + atomic_bool queued; + struct aki_mutex mutex; +#endif + void (*callback)(void *, u8, f64); + void *userdata; +}; + +bool camu_screen_init(struct camu_screen *scr); +bool camu_screen_create_window(struct camu_screen *scr, const char *name); +bool camu_screen_create_renderer(struct camu_screen *scr, struct camu_renderer *renderer); +void camu_screen_add_buffer(struct camu_screen *scr, struct camu_video_buffer *buf); +void camu_screen_remove_buffer(struct camu_screen *scr, struct camu_video_buffer *buf); +#ifdef CAMU_SCREEN_THREADED +void camu_screen_run_queue(struct camu_screen *scr); +#endif +void camu_screen_set_state(struct camu_screen *scr, u8 state); +bool camu_screen_tick(struct camu_screen *scr); +void camu_screen_wake(struct camu_screen *scr); +void camu_screen_close(struct camu_screen *scr); diff --git a/src/screen/view.c b/src/screen/view.c new file mode 100644 index 0000000..cf23ed4 --- /dev/null +++ b/src/screen/view.c @@ -0,0 +1,28 @@ +#include "view.h" + +void camu_view_calculate(struct camu_view *view, s32 window_width, s32 window_height) +{ + f64 ratio = window_width / (f64)window_height; + f64 frame_ratio = view->width / (f64)view->height; + view->ratio = ratio / frame_ratio; + switch (view->mode) { + case CAMU_VIEW_AUTOFIT: + if (frame_ratio > ratio) { + view->zoom = (window_width / (f64)view->width); + } else { + view->zoom = (window_height / (f64)view->height); + } + break; + case CAMU_VIEW_ZOOM: + if (frame_ratio < ratio) { + view->zoom = (window_width / (f64)view->width); + } else { + view->zoom = (window_height / (f64)view->height); + } + break; + default: + return; + } + view->x_offset = ((window_width - (view->width * view->zoom)) / 2.0); + view->y_offset = ((window_height - (view->height * view->zoom)) / 2.0); +} diff --git a/src/screen/view.h b/src/screen/view.h new file mode 100644 index 0000000..0546062 --- /dev/null +++ b/src/screen/view.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +enum { + CAMU_VIEW_HIDDEN = 0, + CAMU_VIEW_AUTOFIT, + CAMU_VIEW_ZOOM, + CAMU_VIEW_DETACHED +}; + +struct camu_view { + u8 mode; + s32 width; + s32 height; + f64 ratio; + f64 zoom; + f64 x_offset; + f64 y_offset; + s32 zindex; +}; + +void camu_view_calculate(struct camu_view *view, s32 window_width, s32 window_height); -- cgit v1.2.3-101-g0448