/* * Pruebas sin pantalla de la lógica de entrada: máquina de estados de * teclado/ratón, cola de eventos y consultas públicas. Usa cabeceras internas. */ #include "check.h" #include #include "core/window_internal.h" #include "input/input_internal.h" #include static FnixEvent key_event(FnixEventType type, FnixKey key) { FnixEvent e; memset(&e, 0, sizeof e); e.type = type; e.key = key; return e; } static FnixEvent mouse_event(FnixEventType type, FnixMouseButton button, int x, int y) { FnixEvent e; memset(&e, 0, sizeof e); e.type = type; e.button = button; e.x = x; e.y = y; return e; } static void test_key_lifecycle(void) { FnixInputState s; memset(&s, 0, sizeof s); FnixEvent down = key_event(FNIX_EVENT_KEY_DOWN, FNIX_KEY_A); fnix_input_apply_event(&s, &down); CHECK(s.key_down[FNIX_KEY_A] && s.key_pressed[FNIX_KEY_A]); CHECK(!down.repeat); /* Autorepeat: el sistema repite KEY_DOWN sin KEY_UP. */ FnixEvent again = key_event(FNIX_EVENT_KEY_DOWN, FNIX_KEY_A); fnix_input_apply_event(&s, &again); CHECK(again.repeat); CHECK(s.key_down[FNIX_KEY_A]); fnix_input_begin_frame(&s); CHECK(s.key_down[FNIX_KEY_A]); CHECK(!s.key_pressed[FNIX_KEY_A]); /* Una repetición en un fotograma posterior tampoco vuelve a "pressed". */ FnixEvent repeat2 = key_event(FNIX_EVENT_KEY_DOWN, FNIX_KEY_A); fnix_input_apply_event(&s, &repeat2); CHECK(!s.key_pressed[FNIX_KEY_A]); FnixEvent up = key_event(FNIX_EVENT_KEY_UP, FNIX_KEY_A); fnix_input_apply_event(&s, &up); CHECK(!s.key_down[FNIX_KEY_A] && s.key_released[FNIX_KEY_A]); fnix_input_begin_frame(&s); CHECK(!s.key_released[FNIX_KEY_A]); /* Pulsar y soltar dentro del mismo fotograma no se pierde. */ FnixEvent quick_down = key_event(FNIX_EVENT_KEY_DOWN, FNIX_KEY_B); FnixEvent quick_up = key_event(FNIX_EVENT_KEY_UP, FNIX_KEY_B); fnix_input_apply_event(&s, &quick_down); fnix_input_apply_event(&s, &quick_up); CHECK(s.key_pressed[FNIX_KEY_B] && s.key_released[FNIX_KEY_B]); CHECK(!s.key_down[FNIX_KEY_B]); /* Soltar una tecla que no estaba pulsada no genera "released". */ FnixEvent stray_up = key_event(FNIX_EVENT_KEY_UP, FNIX_KEY_C); fnix_input_apply_event(&s, &stray_up); CHECK(!s.key_released[FNIX_KEY_C]); /* Teclas inválidas se ignoran sin escribir fuera del arreglo. */ FnixEvent bad1 = key_event(FNIX_EVENT_KEY_DOWN, FNIX_KEY_UNKNOWN); FnixEvent bad2 = key_event(FNIX_EVENT_KEY_DOWN, FNIX_KEY_COUNT); FnixEvent bad3 = key_event(FNIX_EVENT_KEY_DOWN, (FnixKey)-5); fnix_input_apply_event(&s, &bad1); fnix_input_apply_event(&s, &bad2); fnix_input_apply_event(&s, &bad3); } static void test_mouse(void) { FnixInputState s; memset(&s, 0, sizeof s); FnixEvent move = mouse_event(FNIX_EVENT_MOUSE_MOVE, FNIX_MOUSE_LEFT, 12, 34); fnix_input_apply_event(&s, &move); CHECK(s.mouse_x == 12 && s.mouse_y == 34); FnixEvent down = mouse_event(FNIX_EVENT_MOUSE_BUTTON_DOWN, FNIX_MOUSE_RIGHT, 50, 60); fnix_input_apply_event(&s, &down); CHECK(s.mouse_down[FNIX_MOUSE_RIGHT] && s.mouse_pressed[FNIX_MOUSE_RIGHT]); CHECK(!s.mouse_down[FNIX_MOUSE_LEFT]); CHECK(s.mouse_x == 50 && s.mouse_y == 60); fnix_input_begin_frame(&s); CHECK(s.mouse_down[FNIX_MOUSE_RIGHT] && !s.mouse_pressed[FNIX_MOUSE_RIGHT]); FnixEvent up = mouse_event(FNIX_EVENT_MOUSE_BUTTON_UP, FNIX_MOUSE_RIGHT, 51, 61); fnix_input_apply_event(&s, &up); CHECK(!s.mouse_down[FNIX_MOUSE_RIGHT] && s.mouse_released[FNIX_MOUSE_RIGHT]); /* Rueda: se acumula dentro del fotograma y se borra al empezar el siguiente. */ fnix_input_begin_frame(&s); FnixEvent w1 = mouse_event(FNIX_EVENT_MOUSE_WHEEL, FNIX_MOUSE_LEFT, 0, 0); FnixEvent w2 = w1; FnixEvent w3 = w1; w1.wheel = 1; w2.wheel = 1; w3.wheel = -1; fnix_input_apply_event(&s, &w1); fnix_input_apply_event(&s, &w2); fnix_input_apply_event(&s, &w3); CHECK(s.wheel == 1); fnix_input_begin_frame(&s); CHECK(s.wheel == 0); /* Botón inválido: no escribe fuera del arreglo. */ FnixEvent bad = mouse_event(FNIX_EVENT_MOUSE_BUTTON_DOWN, (FnixMouseButton)99, 0, 0); fnix_input_apply_event(&s, &bad); } static void test_focus_lost(void) { FnixInputState s; memset(&s, 0, sizeof s); FnixEvent k = key_event(FNIX_EVENT_KEY_DOWN, FNIX_KEY_W); FnixEvent m = mouse_event(FNIX_EVENT_MOUSE_BUTTON_DOWN, FNIX_MOUSE_LEFT, 1, 1); fnix_input_apply_event(&s, &k); fnix_input_apply_event(&s, &m); fnix_input_begin_frame(&s); CHECK(s.key_down[FNIX_KEY_W] && s.mouse_down[FNIX_MOUSE_LEFT]); FnixEvent lost; memset(&lost, 0, sizeof lost); lost.type = FNIX_EVENT_FOCUS_LOST; fnix_input_apply_event(&s, &lost); CHECK(!s.key_down[FNIX_KEY_W] && s.key_released[FNIX_KEY_W]); CHECK(!s.mouse_down[FNIX_MOUSE_LEFT] && s.mouse_released[FNIX_MOUSE_LEFT]); } static void test_event_queue(void) { static FnixEventQueue q; /* es grande: fuera de la pila */ memset(&q, 0, sizeof q); FnixEvent out; CHECK(!fnix_event_queue_pop(&q, &out)); for (int i = 0; i < 5; i++) { FnixEvent e = mouse_event(FNIX_EVENT_MOUSE_MOVE, FNIX_MOUSE_LEFT, i, 0); fnix_event_queue_push(&q, &e); } for (int i = 0; i < 5; i++) { CHECK(fnix_event_queue_pop(&q, &out) && out.x == i); } CHECK(!fnix_event_queue_pop(&q, &out)); /* Desbordamiento: se conservan los FNIX_EVENT_QUEUE_CAPACITY más recientes. */ const int total = FNIX_EVENT_QUEUE_CAPACITY + 44; for (int i = 0; i < total; i++) { FnixEvent e = mouse_event(FNIX_EVENT_MOUSE_MOVE, FNIX_MOUSE_LEFT, i, 0); fnix_event_queue_push(&q, &e); } CHECK(q.count == FNIX_EVENT_QUEUE_CAPACITY); for (int i = total - FNIX_EVENT_QUEUE_CAPACITY; i < total; i++) { CHECK(fnix_event_queue_pop(&q, &out) && out.x == i); } CHECK(!fnix_event_queue_pop(&q, &out)); } static void test_public_queries(void) { /* Una ventana "falsa" (sin plataforma) basta para probar la API pública. */ static FnixWindow w; memset(&w, 0, sizeof w); FnixEvent down = key_event(FNIX_EVENT_KEY_DOWN, FNIX_KEY_SPACE); fnix_window_dispatch_event(&w, &down); CHECK(fnix_key_down(&w, FNIX_KEY_SPACE)); CHECK(fnix_key_pressed(&w, FNIX_KEY_SPACE)); CHECK(!fnix_key_released(&w, FNIX_KEY_SPACE)); CHECK(!fnix_key_down(&w, FNIX_KEY_A)); FnixEvent click = mouse_event(FNIX_EVENT_MOUSE_BUTTON_DOWN, FNIX_MOUSE_MIDDLE, 7, 9); fnix_window_dispatch_event(&w, &click); CHECK(fnix_mouse_down(&w, FNIX_MOUSE_MIDDLE)); CHECK(fnix_mouse_pressed(&w, FNIX_MOUSE_MIDDLE)); CHECK(fnix_mouse_x(&w) == 7 && fnix_mouse_y(&w) == 9); /* Los eventos quedaron en la cola, en orden. */ FnixEvent e; CHECK(fnix_window_poll_event(&w, &e) && e.type == FNIX_EVENT_KEY_DOWN && e.key == FNIX_KEY_SPACE); CHECK(fnix_window_poll_event(&w, &e) && e.type == FNIX_EVENT_MOUSE_BUTTON_DOWN); CHECK(!fnix_window_poll_event(&w, &e)); CHECK(!fnix_window_poll_event(&w, NULL)); /* El cierre marca la ventana. */ CHECK(!fnix_window_should_close(&w)); FnixEvent close_event; memset(&close_event, 0, sizeof close_event); close_event.type = FNIX_EVENT_WINDOW_CLOSE; fnix_window_dispatch_event(&w, &close_event); CHECK(fnix_window_should_close(&w)); /* Argumentos inválidos: nunca fallan, devuelven falso/0. */ CHECK(!fnix_key_down(NULL, FNIX_KEY_A)); CHECK(!fnix_key_pressed(NULL, FNIX_KEY_A)); CHECK(!fnix_key_released(NULL, FNIX_KEY_A)); CHECK(!fnix_key_down(&w, FNIX_KEY_UNKNOWN)); CHECK(!fnix_key_down(&w, FNIX_KEY_COUNT)); CHECK(!fnix_key_down(&w, (FnixKey)-1)); CHECK(!fnix_mouse_down(NULL, FNIX_MOUSE_LEFT)); CHECK(!fnix_mouse_down(&w, FNIX_MOUSE_BUTTON_COUNT)); CHECK(!fnix_mouse_pressed(&w, (FnixMouseButton)-1)); CHECK(!fnix_mouse_released(NULL, FNIX_MOUSE_LEFT)); CHECK(fnix_mouse_x(NULL) == 0 && fnix_mouse_y(NULL) == 0); CHECK(fnix_mouse_wheel(NULL) == 0); } int main(void) { test_key_lifecycle(); test_mouse(); test_focus_lost(); test_event_queue(); test_public_queries(); return CHECK_REPORT("test_input"); }