Files
NASM/wayland/src/wayland-runtime.c

210 lines
5.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <errno.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <wayland-client.h>
#include <unistd.h>
#include "input.h"
#include "registry.h"
#include "wayland-runtime.h"
#include "window.h"
#include "figure-animate.h"
#define MAX_WINDOW_THREADS 128
struct window_thread_slot
{
pthread_t thread;
pthread_t aux_thread;
int active;
struct wl_event_queue *queue;
struct wayland_window window;
};
static struct wl_display *g_display = NULL;
static struct window_thread_slot g_slots[MAX_WINDOW_THREADS];
static pthread_mutex_t g_thread_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_thread_cond = PTHREAD_COND_INITIALIZER;
static atomic_int g_active_threads = 0;
static atomic_bool g_shutdown = 0;
static int g_initialized = 0;
static void signal_thread_exit(struct window_thread_slot *slot)
{
pthread_mutex_lock(&g_thread_lock);
slot->active = 0;
atomic_fetch_sub(&g_active_threads, 1);
pthread_cond_broadcast(&g_thread_cond);
pthread_mutex_unlock(&g_thread_lock);
}
static void *window_aux_loop(void *arg)
{
struct window_thread_slot *slot = arg;
struct window_draw_info *draw_info = &slot->window.draw_info;
while (!atomic_load(&g_shutdown) && !window_should_close(&slot->window))
{
/* На время обновления позиции фигуры локаем мутекс */
pthread_mutex_lock(&draw_info->figure_mutex);
figure_animation_step(draw_info);
pthread_mutex_unlock(&draw_info->figure_mutex);
usleep(30 * 1000);
}
return NULL;
}
static void *window_thread_main(void *arg)
{
struct window_thread_slot *slot = arg;
memset(&slot->window, 0, sizeof(slot->window));
slot->queue = wl_display_create_queue(g_display);
if (!slot->queue)
{
signal_thread_exit(slot);
return NULL;
}
if (window_init(g_display, slot->queue, &slot->window) < 0)
{
wl_event_queue_destroy(slot->queue);
slot->queue = NULL;
signal_thread_exit(slot);
return NULL;
}
/* Запуск вспомогательного потока, который пока просто крутится с паузой 100 ms.
* Поток завершится, как только окно будет помечено как закрыто. */
int aux_res = pthread_create(&slot->aux_thread, NULL, window_aux_loop, slot);
if (aux_res == 0)
pthread_detach(slot->aux_thread);
while (!atomic_load(&g_shutdown) && !window_should_close(&slot->window))
{
int dispatch = wl_display_dispatch_queue(g_display, slot->queue);
if (dispatch < 0)
{
if (errno == EINTR)
continue;
atomic_store(&g_shutdown, 1);
break;
}
}
printf("Window #%d closed\n", slot->window.id);
window_destroy(&slot->window);
if (slot->queue)
{
wl_event_queue_destroy(slot->queue);
slot->queue = NULL;
}
signal_thread_exit(slot);
return NULL;
}
int32_t init_wayland(void)
{
if (g_initialized)
return 0;
g_display = wl_display_connect(NULL);
if (!g_display)
return -1;
/* Привязать глобальные объекты и запустить поток обработки (дефолтная очередь).
* `registry_global_bind` добавляет listener и делает roundtrip. */
if (registry_global_bind(g_display) < 0)
{
wl_display_disconnect(g_display);
g_display = NULL;
return -1;
}
atomic_store(&g_shutdown, 0);
atomic_store(&g_active_threads, 0);
g_initialized = 1;
return 0;
}
int32_t run_window(void)
{
if (!g_initialized || !g_display)
return -1;
int slot_index = -1;
pthread_mutex_lock(&g_thread_lock);
for (int i = 0; i < MAX_WINDOW_THREADS; ++i)
{
if (!g_slots[i].active)
{
slot_index = i;
g_slots[i].active = 1;
atomic_fetch_add(&g_active_threads, 1);
break;
}
}
pthread_mutex_unlock(&g_thread_lock);
if (slot_index < 0)
return -1;
int create_res = pthread_create(&g_slots[slot_index].thread, NULL, window_thread_main, &g_slots[slot_index]);
if (create_res != 0)
{
pthread_mutex_lock(&g_thread_lock);
g_slots[slot_index].active = 0;
atomic_fetch_sub(&g_active_threads, 1);
pthread_mutex_unlock(&g_thread_lock);
return -1;
}
pthread_detach(g_slots[slot_index].thread);
return slot_index;
}
void wait_for_windows(void)
{
pthread_mutex_lock(&g_thread_lock);
while (atomic_load(&g_active_threads) > 0)
pthread_cond_wait(&g_thread_cond, &g_thread_lock);
pthread_mutex_unlock(&g_thread_lock);
}
void destroy_wayland(void)
{
if (!g_initialized)
return;
wait_for_windows();
input_cleanup();
/* Остановить поток глобального реестра и сбросить глобальные данные */
registry_global_unbind();
if (g_display)
{
wl_display_disconnect(g_display);
g_display = NULL;
}
memset(g_slots, 0, sizeof(g_slots));
g_initialized = 0;
atomic_store(&g_shutdown, 0);
}
struct wayland_window *get_window_by_surface(struct wl_surface *surf)
{
for (int i = 0; i < MAX_WINDOW_THREADS; i++)
if (g_slots[i].window.wl_surface == surf)
return &g_slots[i].window;
return NULL;
}