6 ex_gui_custom_mouse_8c example
gullradriel edited this page 2026-10-09 00:55:18 +02:00

ex_gui_custom_mouse.c

a custom widget that takes the mouse, and one that owns its state Headless: events are made by hand and fed to n_gui_process_event(). A custom widget with n_gui_custom_set_mouse() is told of a move over it, a press of either button, a drag that leaves it, the release, and the wheel, in its own coordinates; a press elsewhere, or on a widget drawn over it, tells it nothing. n_gui_custom_set_destroy() hands it a heap pointer that n_gui_destroy_ctx() frees. ```c

#define ALLEGRO_UNSTABLE 1

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h>

#include "nilorea/n_common.h" #include "nilorea/n_log.h" #include "nilorea/n_gui.h"

static int log_level = LOG_ERR; static int failures = 0;

static N_GUI_MOUSE_EVENT last; static int told = 0; static int wheel_answer = 1; static int destroyed = 0;

static void expect_true(const char* what, int ok) { if (!ok) { n_log(LOG_ERR, "FAIL: %s", what); failures++; } else { n_log(LOG_INFO, "ok: %s", what); } }

static int process_args(int argc, char** argv) { int opt = 0;

while ((opt = getopt(argc, argv, "V:")) != -1) {
    if (opt == 'V') {
        log_level = atoi(optarg);
    } else {
        return -1;
    }
}
return 0;

}

static int on_mouse(int widget_id, const N_GUI_MOUSE_EVENT* ev, void* user_data) { (void)widget_id; (void)user_data; last = *ev; told++; return ev->type == N_GUI_MOUSE_WHEEL ? wheel_answer : 1; }

static void destroy_state(void* state) { destroyed++; free(state); }

static int feed(N_GUI_CTX* gui, int type, int x, int y, int button, int dz) { ALLEGRO_EVENT ev;

memset(&ev, 0, sizeof(ev));
ev.type = (ALLEGRO_EVENT_TYPE)type;
ev.mouse.x = x;
ev.mouse.y = y;
ev.mouse.dx = (type == ALLEGRO_EVENT_MOUSE_AXES && dz == 0) ? 1 : 0;
ev.mouse.dz = dz;
ev.mouse.button = (unsigned int)button;
return n_gui_process_event(gui, ev);

}

static int was(int type, int button, float x, float y) { return last.type == type && last.button == button && last.x == x && last.y == y; }

int main(int argc, char** argv) { int before = 0;

set_log_level(LOG_ERR);
if (process_args(argc, argv) != 0) {
    fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
    return 2;
}
set_log_level(log_level);
if (!al_init() || !al_init_font_addon()) {
    n_log(LOG_ERR, "allegro init failed");
    return 1;
}
ALLEGRO_FONT* font = al_create_builtin_font();
if (!font) return 1;
N_GUI_CTX* gui = n_gui_new_ctx(font);
if (!gui) {
    al_destroy_font(font);
    return 1;
}
n_gui_set_display_size(gui, 400.0f, 300.0f);
int win = n_gui_add_window(gui, "W", 0.0f, 0.0f, 400.0f, 300.0f);
n_gui_window_set_flags(gui, win, N_GUI_WIN_FRAMELESS);
n_gui_open_window(gui, win);

int area = n_gui_add_custom(gui, win, 50.0f, 50.0f, 100.0f, 80.0f, NULL, NULL);
n_gui_custom_set_mouse(gui, area, on_mouse, NULL);

feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 60, 70, 0, 0);
expect_true("a move over it is a MOVE, in its coordinates", told == 1 && was(N_GUI_MOUSE_MOVE, 0, 10.0f, 20.0f));
feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 60, 70, 1, 0);
expect_true("a left press is a DOWN with button 1", was(N_GUI_MOUSE_DOWN, 1, 10.0f, 20.0f));
feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 200, 200, 0, 0);
expect_true("a drag out of it is still reported, as a DRAG", was(N_GUI_MOUSE_DRAG, 1, 150.0f, 150.0f));
/* Allegro reports a move before a release somewhere new, so the pointer
   is where the last move left it */
feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 210, 190, 0, 0);
expect_true("so is the next move of the drag", was(N_GUI_MOUSE_DRAG, 1, 160.0f, 140.0f));
feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 210, 190, 1, 0);
expect_true("the release is an UP where the pointer is", was(N_GUI_MOUSE_UP, 1, 160.0f, 140.0f));
before = told;
feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 220, 220, 0, 0);
expect_true("a move elsewhere after it is not reported", told == before);

feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 70, 60, 2, 0);
expect_true("a right press is a DOWN with button 2", was(N_GUI_MOUSE_DOWN, 2, 20.0f, 10.0f));
feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 70, 60, 2, 0);

before = told;
expect_true("a wheel it takes is consumed", feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 60, 70, 0, 1) == 1);
expect_true("and reported with its steps", told == before + 1 && last.type == N_GUI_MOUSE_WHEEL && last.dz == 1);
wheel_answer = 0;
before = told;
feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 60, 70, 0, -1);
expect_true("a wheel it declines is still reported", told == before + 1 && last.dz == -1);

before = told;
feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 300, 250, 1, 0);
feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 300, 250, 1, 0);
expect_true("a press elsewhere tells it nothing", told == before);

/* a widget drawn over part of it takes the press there */
n_gui_add_button(gui, win, "over", 50.0f, 50.0f, 30.0f, 20.0f, N_GUI_SHAPE_RECT, NULL, NULL);
before = told;
feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 55, 55, 1, 0);
feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 55, 55, 1, 0);
expect_true("a press on a widget over it does not reach it", told == before);

n_gui_custom_set_mouse(gui, area, NULL, NULL);
before = told;
feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 100, 100, 1, 0);
feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 100, 100, 1, 0);
expect_true("with the callback taken away it is told nothing", told == before);

/* ownership */
int* state = malloc(sizeof(int));
int owner = n_gui_add_custom(gui, win, 200.0f, 10.0f, 20.0f, 20.0f, NULL, state);
n_gui_custom_set_destroy(gui, owner, destroy_state);
n_gui_custom_set_destroy(gui, area, NULL);
n_gui_destroy_ctx(&gui);
expect_true("owned state is destroyed with the widget, once", destroyed == 1);

al_destroy_font(font);
if (failures) {
    fprintf(stderr, "ex_gui_custom_mouse: %d failure(s)\n", failures);
    return 1;
}
printf("ex_gui_custom_mouse: ok\n");
return 0;

}


_Filename: ex_gui_custom_mouse.c_