Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_custom_mouse.c

a custom widget that takes the mouse, and one that owns its state

a custom widget that takes the mouse, and one that owns its state

Author
Castagnier Mickael
Version
1.0
Date
24/09/2026

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.

#define ALLEGRO_UNSTABLE 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "nilorea/n_log.h"
#include "nilorea/n_gui.h"
static int log_level = LOG_ERR;
static int failures = 0;
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);
} 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) {
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;
if (process_args(argc, argv) != 0) {
fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
return 2;
}
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;
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);
int area = n_gui_add_custom(gui, win, 50.0f, 50.0f, 100.0f, 80.0f, NULL, 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);
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);
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;
}
void process_args(int argc, char **argv)
static int failures
Definition ex_atlas.c:51
int main(void)
Definition ex_atlas.c:117
static void expect_true(const char *label, int cond)
Definition ex_atlas.c:53
static int log_level
static void feed(N_GUI_CTX *gui, int type, int x, int y, int button)
feed one mouse event at display coordinates
static int told
static int was(int type, int button, float x, float y)
was the last thing told this
static int destroyed
how many times the owned state was destroyed
static int on_mouse(int widget_id, const N_GUI_MOUSE_EVENT *ev, void *user_data)
the widget's mouse callback
static N_GUI_MOUSE_EVENT last
the last event the widget was told of, and how many
static int wheel_answer
what the widget answers to a wheel
static void destroy_state(void *state)
the owned state's destructor
static N_GUI_CTX * gui
static void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
FONT * font
the default 8x8 font, valid after allegro_init()
int type
N_GUI_MOUSE_DOWN, _UP, _MOVE, _DRAG or _WHEEL.
Definition n_gui.h:1202
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
set the display (viewport) size so global scrollbars can be computed.
Definition n_gui.c:12348
#define N_GUI_MOUSE_DOWN
custom widget mouse event: a press inside it, button 1 or 2
Definition n_gui.h:1187
#define N_GUI_MOUSE_WHEEL
custom widget mouse event: the wheel turned over it
Definition n_gui.h:1197
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
Definition n_gui.c:13611
#define N_GUI_MOUSE_DRAG
custom widget mouse event: the pointer moved with button 1 held after a press in it; reported outside...
Definition n_gui.h:1195
#define N_GUI_WIN_FRAMELESS
frameless window: no title bar drawn, drag via window body unless N_GUI_WIN_FIXED_POSITION is also se...
Definition n_gui.h:276
void n_gui_custom_set_mouse(N_GUI_CTX *ctx, int widget_id, N_GUI_CUSTOM_MOUSE on_mouse, void *user_data)
give a custom widget mouse input
Definition n_gui.c:5730
int n_gui_add_window(N_GUI_CTX *ctx, const char *title, float x, float y, float w, float h)
Add a new pseudo-window to the context.
Definition n_gui.c:1277
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
Definition n_gui.c:1103
#define N_GUI_MOUSE_MOVE
custom widget mouse event: the pointer moved over it with no button held
Definition n_gui.h:1192
#define N_GUI_MOUSE_UP
custom widget mouse event: the release of a button 1 press that began in it, wherever the pointer is ...
Definition n_gui.h:1190
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
Definition n_gui.c:1209
void n_gui_open_window(N_GUI_CTX *ctx, int window_id)
Open (show) a window.
Definition n_gui.c:1436
void n_gui_custom_set_destroy(N_GUI_CTX *ctx, int widget_id, void(*destroy)(void *user_data))
hand a custom widget's paint user pointer to the widget
Definition n_gui.c:5745
void n_gui_window_set_flags(N_GUI_CTX *ctx, int window_id, int flags)
Set feature flags on a window.
Definition n_gui.c:2515
#define N_GUI_SHAPE_RECT
rectangle shape (default)
Definition n_gui.h:170
int n_gui_add_button(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, int shape, void(*on_click)(int, void *), void *user_data)
Add a button widget to a window.
Definition n_gui.c:2720
int n_gui_add_custom(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, N_GUI_CUSTOM_DRAW draw, void *user_data)
add an owner-draw custom widget; returns the widget id or -1
Definition n_gui.c:5698
The top-level GUI context that holds all windows.
Definition n_gui.h:1669
A mouse event handed to a custom widget, in the widget's own coordinates.
Definition n_gui.h:1200
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_ERR
error conditions
Definition n_log.h:100
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
#define LOG_INFO
informational
Definition n_log.h:106
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.
Generic log system.