#include #include #include #include #include #include #include #include #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; }