33#define ALLEGRO_UNSTABLE 1
61 while ((opt = getopt(argc, argv,
"V:")) != -1) {
64 if (!strcmp(
"LOG_NULL", optarg))
66 else if (!strcmp(
"LOG_NOTICE", optarg))
68 else if (!strcmp(
"LOG_INFO", optarg))
70 else if (!strcmp(
"LOG_ERR", optarg))
72 else if (!strcmp(
"LOG_DEBUG", optarg))
95 n_log(
LOG_ERR,
"FAIL %s: got %d, want %d", label, got, want);
103 float d = got - want;
104 if (d < 0.0f) d = -d;
106 n_log(
LOG_ERR,
"FAIL %s: got %f, want %f", label, (
double)got, (
double)want);
135 memset(&ev, 0,
sizeof(ev));
136 ev.mouse.display = from;
140 ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
142 ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
149 memset(&ev, 0,
sizeof(ev));
150 ev.type = ALLEGRO_EVENT_KEY_CHAR;
151 ev.keyboard.display = from;
152 ev.keyboard.keycode = keycode;
153 ev.keyboard.modifiers = ALLEGRO_KEYMOD_CTRL;
154 ev.keyboard.unichar = 0;
162 unsigned char rr, rg, rb;
163 al_unmap_rgb(ref, &rr, &rg, &rb);
164 for (
int py = y; py < y + h; py++) {
165 for (
int px = x; px < x + w; px++) {
166 unsigned char cr, cg, cb;
167 al_unmap_rgb(al_get_pixel(bmp, px, py), &cr, &cg, &cb);
170 if (abs((
int)cr - (
int)rr) > 24 || abs((
int)cg - (
int)rg) > 24 || abs((
int)cb - (
int)rb) > 24)
195 ALLEGRO_BITMAP* target = al_get_target_bitmap();
197 if (!target || !win)
return;
206 ALLEGRO_COLOR black = al_map_rgb(0, 0, 0);
207 ALLEGRO_BITMAP* back;
210 al_set_target_backbuffer(d);
211 al_clear_to_color(black);
212 al_draw_text(font, al_map_rgb(255, 255, 255), 4.0f, 4.0f, 0,
"NILOREA");
213 al_draw_bitmap(bmp, 4.0f, 40.0f, 0);
214 back = al_get_backbuffer(d);
218 snprintf(label,
sizeof(label),
"%s: shared font rasterizes (%d lit pixels)", who, lit);
223 snprintf(label,
sizeof(label),
"%s: shared bitmap draws (%d lit pixels)", who, lit);
227int main(
int argc,
char** argv) {
230 fprintf(stderr,
"Usage: %s [-V LOG_LEVEL]\n", argv[0]);
235 if (!al_init() || !al_init_font_addon() || !al_init_primitives_addon()) {
239 al_install_keyboard();
242 al_set_new_display_flags(ALLEGRO_WINDOWED);
243 ALLEGRO_DISPLAY* main_display = al_create_display(
MAIN_W,
MAIN_H);
246 n_log(
LOG_NOTICE,
"ex_gui_multiwin: no display available, test skipped");
250 ALLEGRO_EVENT_QUEUE* queue = al_create_event_queue();
251 ALLEGRO_FONT* font = al_create_builtin_font();
252 if (!queue || !font) {
254 if (queue) al_destroy_event_queue(queue);
255 if (font) al_destroy_font(font);
256 al_destroy_display(main_display);
259 al_register_event_source(queue, al_get_display_event_source(main_display));
264 ALLEGRO_BITMAP* shared_bmp = al_create_bitmap(32, 32);
267 al_destroy_event_queue(queue);
268 al_destroy_font(font);
269 al_destroy_display(main_display);
272 al_set_target_bitmap(shared_bmp);
273 al_clear_to_color(al_map_rgb(255, 0, 255));
274 al_set_target_backbuffer(main_display);
279 al_destroy_bitmap(shared_bmp);
280 al_destroy_event_queue(queue);
281 al_destroy_font(font);
282 al_destroy_display(main_display);
295 expect_true(
"windows created", main_win >= 0 && popup_win >= 0);
301 expect_true(
"widgets created", main_btn >= 0 && popup_btn >= 0 && popup_txt >= 0);
310 expect_true(
"native display created", popup_display != NULL);
311 expect_true(
"native display differs from the main one", popup_display != main_display);
317 if (!popup_display) {
320 al_destroy_bitmap(shared_bmp);
321 al_destroy_event_queue(queue);
322 al_destroy_font(font);
323 al_destroy_display(main_display);
330 expect_true(
"detached window sits at the origin", w && w->
x == 0.0f && w->
y == 0.0f);
331 expect_eq_float(
"detached window width matches the display", w ? w->
w : -1.0f, (float)al_get_display_width(popup_display));
332 expect_eq_float(
"detached window height matches the display", w ? w->
h : -1.0f, (float)al_get_display_height(popup_display));
365 const char* payload =
"nilorea-multiwin-clipboard";
381 expect_true(
"clipboard copy from the detached window round-trips",
382 got != NULL && strcmp(got, payload) == 0);
384 if (strcmp(got, payload) != 0)
385 n_log(
LOG_ERR,
"clipboard: got '%s', want '%s'", got, payload);
389 char* al_got = al_get_clipboard_text(popup_display);
390 expect_true(
"clipboard copy from the detached window round-trips",
391 al_got != NULL && strcmp(al_got, payload) == 0);
393 if (strcmp(al_got, payload) != 0)
394 n_log(
LOG_ERR,
"clipboard: got '%s', want '%s'", al_got, payload);
403 al_set_target_backbuffer(main_display);
404 al_clear_to_color(al_map_rgb(0, 0, 0));
409 ALLEGRO_BITMAP* back = al_get_backbuffer(main_display);
411 expect_eq_int(
"main pass leaves the far corner untouched", lit, 0);
416 al_set_target_backbuffer(main_display);
418 expect_true(
"draw_detached restores the caller's target", al_get_target_bitmap() == al_get_backbuffer(main_display));
426 memset(&ev, 0,
sizeof(ev));
427 ev.type = ALLEGRO_EVENT_DISPLAY_CLOSE;
428 ev.display.source = popup_display;
446 expect_true(
"a fresh native display was created", popup_display != NULL);
469 expect_true(
"own-chrome window created", chrome_win >= 0);
474 expect_true(
"own-chrome native display created", cd != NULL);
483 if (cd && !(al_get_display_flags(cd) & ALLEGRO_FRAMELESS))
484 n_log(
LOG_NOTICE,
"platform did not honour ALLEGRO_FRAMELESS (no window manager?), check skipped");
485 expect_true(
"own-chrome native window is resizable (needed by resize/maximize)",
486 cd && (al_get_display_flags(cd) & ALLEGRO_RESIZABLE));
490 cd ? (
float)al_get_display_height(cd) : -1.0f, 180.0f);
494 expect_true(
"native window title was set before creation", cd != NULL);
498 int px0 = 0, py0 = 0, px1 = 0, py1 = 0;
500 al_get_window_position(cd, &px0, &py0);
502 memset(&ev, 0,
sizeof(ev));
503 ev.mouse.display = cd;
507 ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
519 int cbx = 0, cby = 0, cax = 0, cay = 0;
520 int warped = al_get_mouse_cursor_position(&cbx, &cby) &&
521 al_set_mouse_xy(cd, 90, (
int)(cw->
titlebar_h / 2.0f)) &&
522 al_get_mouse_cursor_position(&cax, &cay);
524 ev.type = ALLEGRO_EVENT_MOUSE_AXES;
527 al_get_window_position(cd, &px1, &py1);
528 if (warped && cax != cbx)
529 expect_eq_int(
"title bar drag moved the OS window", px1 - px0, cax - cbx);
531 n_log(
LOG_NOTICE,
"ex_gui_multiwin: pointer cannot be warped here, skipping the OS-window drag check");
532 expect_eq_float(
"title bar drag left the window pinned at the origin", cw->
x, 0.0f);
534 ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
542 float full_h = cw->
h;
546 (
float)al_get_display_height(cd), cw->
titlebar_h);
547 expect_eq_float(
"minimise kept the full window height for the restore", cw->
h, full_h);
551 (
float)al_get_display_height(cd), full_h);
576 if (al_set_window_constraints(cd, 200, 150, 0, 0))
577 al_apply_window_constraints(cd,
true);
584 memset(&ev, 0,
sizeof(ev));
585 ev.mouse.display = cd;
587 ev.mouse.x = (int)mid_x;
589 ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
591 ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
594 expect_true(
"title bar minimise button set the minimised flag",
596 expect_eq_float(
"title bar minimise shrank the OS window to the title bar",
597 (
float)al_get_display_height(cd), cw->
titlebar_h);
600 (
float)al_get_display_height(cd), full_h2);
607 memset(&ev, 0,
sizeof(ev));
608 ev.mouse.display = cd;
610 ev.mouse.x = (int)(cw->
w - grip / 2.0f);
611 ev.mouse.y = (int)(cw->
h - grip / 2.0f);
612 ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
616 ev.type = ALLEGRO_EVENT_MOUSE_AXES;
620 expect_eq_float(
"grip resized the OS window (width)", (
float)al_get_display_width(cd), 300.0f);
621 expect_eq_float(
"grip resized the OS window (height)", (
float)al_get_display_height(cd), 200.0f);
623 ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
631 int cmin_w = 0, cmin_h = 0, cmax_w = 0, cmax_h = 0;
632 if (al_get_window_constraints(cd, &cmin_w, &cmin_h, &cmax_w, &cmax_h)) {
633 expect_eq_int(
"min width constraint pushed to the window manager", cmin_w, (
int)(cw->
min_w + 0.5f));
634 expect_eq_int(
"min height constraint pushed to the window manager", cmin_h, (
int)(cw->
min_h + 0.5f));
636 n_log(
LOG_NOTICE,
"window constraints unsupported on this platform, check skipped");
642 ALLEGRO_BITMAP* icons[1];
643 icons[0] = shared_bmp;
650 expect_eq_int(
"native icon refused for a pop-up", refused, -1);
656 memset(&ev, 0,
sizeof(ev));
657 ev.type = ALLEGRO_EVENT_DISPLAY_HALT_DRAWING;
658 ev.display.source = cd;
662 ev.type = ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING;
706 const char* path =
"ex_gui_multiwin_layout.json";
710 float want_w = 0.0f, want_h = 0.0f;
713 want_w = (float)al_get_display_width(d);
714 want_h = (float)al_get_display_height(d);
718 al_set_target_backbuffer(main_display);
728 expect_true(
"rebuilt windows", w2_main >= 0 && w2_popup >= 0);
732 expect_true(
"restored native display exists", d2 != NULL);
734 expect_eq_float(
"restored native width", (
float)al_get_display_width(d2), want_w);
735 expect_eq_float(
"restored native height", (
float)al_get_display_height(d2), want_h);
748 al_set_target_backbuffer(main_display);
749 al_destroy_bitmap(shared_bmp);
750 al_destroy_event_queue(queue);
751 al_destroy_font(font);
752 al_destroy_display(main_display);
void process_args(int argc, char **argv)
static void expect_true(const char *label, int cond)
static void on_main_click(int widget_id, void *user_data)
static int g_chrome_titlebar_lit
static void expect_eq_float(const char *label, float got, float want)
static void on_popup_click(int widget_id, void *user_data)
static void ctrl_key(N_GUI_CTX *gui, ALLEGRO_DISPLAY *from, int keycode)
static int count_pixels_differing(ALLEGRO_BITMAP *bmp, int x, int y, int w, int h, ALLEGRO_COLOR ref)
static void expect_eq_int(const char *label, int got, int want)
static void check_shared_resources(const char *who, ALLEGRO_DISPLAY *d, ALLEGRO_FONT *font, ALLEGRO_BITMAP *bmp)
static int g_popup_clicks
static void sample_chrome_titlebar(int window_id, void *user_data)
static void on_popup_close(int window_id, void *user_data)
static void click_on(N_GUI_CTX *gui, ALLEGRO_DISPLAY *from, int x, int y)
float x
position x on screen
float titlebar_h
title bar height
float tb_btn_size
titlebar button size (0 = auto: titlebar_h - 4)
N_GUI_THEME theme
color theme for this window chrome
float saved_w
embedded width saved by n_gui_window_detach, restored by n_gui_window_attach
float saved_h
embedded height saved by n_gui_window_detach, restored by n_gui_window_attach
float grip_size
grip area size
int state
state flags (N_GUI_WIN_*)
int flags
feature flags (N_GUI_WIN_AUTO_SCROLLBAR, N_GUI_WIN_RESIZABLE, etc.)
N_GUI_STYLE style
configurable style (sizes, colours, paddings)
float min_h
minimum height
float tb_btn_spacing
gap between titlebar buttons
ALLEGRO_COLOR bg_normal
background normal
float tb_btn_right_margin
right margin from window edge to rightmost button
float y
position y on screen
ALLEGRO_DISPLAY * active_display
display that last received input, used to pick the display for clipboard and mouse-cursor calls.
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
Set the display (viewport) size for global scrollbar computation.
void n_gui_textarea_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set the text content of a textarea widget
int n_gui_save_layout_json(N_GUI_CTX *ctx, const char *filepath)
save the geometry + persistent state of every window to a JSON file
int n_gui_window_detach(N_GUI_CTX *ctx, int window_id, int detach_flags)
Promote a pop-up window to a native OS window.
void n_gui_set_display(N_GUI_CTX *ctx, ALLEGRO_DISPLAY *display)
Set the display pointer for clipboard operations (copy/paste).
int n_gui_wants_mouse(N_GUI_CTX *ctx)
Check if the mouse is currently over any open GUI window.
int n_gui_window_native_is_hidden(N_GUI_CTX *ctx, int window_id)
Check whether a detached window is currently not drawable.
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
int n_gui_window_get_flags(N_GUI_CTX *ctx, int window_id)
Get feature flags of a window.
void n_gui_minimize_window(N_GUI_CTX *ctx, int window_id)
Minimise a window (show title bar only)
void n_gui_close_window(N_GUI_CTX *ctx, int window_id)
Close (hide) a window.
void n_gui_maximize_window(N_GUI_CTX *ctx, int window_id)
Toggle maximised state (full display or restore to previous size).
ALLEGRO_EVENT_QUEUE * n_gui_get_event_queue(N_GUI_CTX *ctx)
Get the event queue previously given to n_gui_set_event_queue.
void n_gui_set_focus(N_GUI_CTX *ctx, int widget_id)
Set keyboard focus to a specific widget.
#define N_GUI_WIN_FRAMELESS
frameless window: no title bar drawn, drag via window body unless N_GUI_WIN_FIXED_POSITION is also se...
#define N_GUI_DETACH_RESIZABLE
the native window can be resized by the user through the OS window manager
int n_gui_window_is_detached(N_GUI_CTX *ctx, int window_id)
Check whether a window currently lives in a native OS window.
void n_gui_draw(N_GUI_CTX *ctx)
Draw all visible windows and their widgets.
#define N_GUI_WIN_MINIMISED
window is minimised (title bar only)
int n_gui_window_is_open(N_GUI_CTX *ctx, int window_id)
Check if a window is currently visible.
N_GUI_WINDOW * n_gui_get_window(N_GUI_CTX *ctx, int window_id)
Get a window pointer by id.
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.
int n_gui_window_attach(N_GUI_CTX *ctx, int window_id)
Return a detached window to being an in-display pop-up.
void n_gui_draw_detached(N_GUI_CTX *ctx)
Render and flip every detached window, and release the ones whose native window was closed.
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
#define N_GUI_WIN_MAXIMISED
window is maximised (full display size)
void n_gui_window_set_close_callback(N_GUI_CTX *ctx, int window_id, void(*on_close)(int, void *), void *user_data)
Set the close button callback for a window.
#define N_GUI_DETACH_OWN_CHROME
keep N_GUI's own window chrome instead of the window manager's: the native window is created frameles...
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
void n_gui_restore_window(N_GUI_CTX *ctx, int window_id)
Restore a minimised window to its full size.
void n_gui_open_window(N_GUI_CTX *ctx, int window_id)
Open (show) a window.
ALLEGRO_DISPLAY * n_gui_window_get_display(N_GUI_CTX *ctx, int window_id)
Get the native display backing a window, or NULL when it is a pop-up.
void n_gui_window_set_content_draw_callback(N_GUI_CTX *ctx, int window_id, void(*on_content_draw)(int, void *), void *user_data)
Set the content-draw callback for a window.
int n_gui_window_set_native_icons(N_GUI_CTX *ctx, int window_id, ALLEGRO_BITMAP **icons, int num_icons)
Give a detached window's OS window a taskbar / title bar icon.
int n_gui_count_detached(N_GUI_CTX *ctx)
Count the windows currently detached into native OS windows.
int n_gui_load_layout_json(N_GUI_CTX *ctx, const char *filepath)
load a JSON window layout file and apply it to matching windows
void n_gui_window_set_flags(N_GUI_CTX *ctx, int window_id, int flags)
Set feature flags on a window.
#define N_GUI_WIN_BTN_ALL
enable all three title bar buttons (minimize + maximize + close)
#define N_GUI_WIN_RESIZING
window is being resized
int n_gui_window_from_display(N_GUI_CTX *ctx, ALLEGRO_DISPLAY *display)
Find the window detached into a given display.
int n_gui_add_textarea(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int multiline, size_t char_limit, void(*on_change)(int, const char *, void *), void *user_data)
Add a text area widget.
#define N_GUI_SHAPE_RECT
rectangle shape (default)
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.
void n_gui_set_event_queue(N_GUI_CTX *ctx, ALLEGRO_EVENT_QUEUE *queue)
Set the event queue used to register native window event sources.
#define N_GUI_WIN_RESIZABLE
enable user-resizable window with a drag handle at bottom-right
#define N_GUI_WIN_DRAGGING
window is being dragged
The top-level GUI context that holds all windows.
A pseudo window that contains widgets.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
#define LOG_NOTICE
normal but significant condition
#define LOG_NULL
no log output
#define LOG_INFO
informational
int n_clipboard_available(void)
Whether a real clipboard backend (X11 or Win32) is active on this platform.
char * n_clipboard_get(int which)
Fetch the current text of which selection from its owner.
System clipboard: full X11 selection support on Linux, Win32 clipboard on Windows/MinGW.
#define N_CLIPBOARD_CLIPBOARD
the Ctrl+C / Ctrl+V clipboard selection
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.