259 lines
6.6 KiB
C
259 lines
6.6 KiB
C
#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);
|
||
}
|
||
|
||
#include <stdio.h>
|
||
#include <math.h>
|
||
#include <stdint.h>
|
||
#include <stdlib.h>
|
||
|
||
// Прототип ASM-функции
|
||
void place_points_on_circle(
|
||
float pos_x,
|
||
float pos_y,
|
||
float offset_rad,
|
||
float radius,
|
||
float *points,
|
||
size_t count);
|
||
|
||
struct Point
|
||
{
|
||
float x;
|
||
float y;
|
||
};
|
||
|
||
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);
|
||
|
||
// const size_t n = 8;
|
||
// struct vec2 *pts = malloc(sizeof(struct vec2) * n);
|
||
// if (!pts)
|
||
// {
|
||
// printf("malloc failed\n");
|
||
// }
|
||
|
||
// float center_x = 0.0f;
|
||
// float center_y = 0.0f;
|
||
// float offset = 0.78f;
|
||
// float radius = 1.0f;
|
||
|
||
// // Вызов ASM-функции
|
||
// place_points_on_circle(
|
||
// center_x,
|
||
// center_y,
|
||
// offset,
|
||
// radius,
|
||
// (float *)pts, // адрес выходного массива
|
||
// n);
|
||
|
||
// // Вывод для проверки (и удобной точки останова)
|
||
// for (size_t i = 0; i < n; i++)
|
||
// {
|
||
// printf("%zu: x = %f, y = %f\n", i, pts[i].x, pts[i].y);
|
||
// }
|
||
|
||
// free(pts);
|
||
pthread_mutex_unlock(&draw_info->figure_mutex);
|
||
|
||
usleep(33 * 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;
|
||
}
|