Page:
ex__gui__multiwin_8c
Pages
Examples
Files
Home
Module AABB3D
Module ACCEPT_POOL
Module ALLEGRO4
Module ALLEGRO5
Module ANIM_LIB
Module ASTAR
Module ATLAS_LIB
Module AVRO
Module CLOCK_SYNC
Module COMMONS
Module CONFIGFILE
Module CYPHER_BASE64
Module CYPHER_VIGENERE
Module DEAD_RECKONING
Module ENUMS
Module EXCEPTIONS
Module GAME_ENV
Module GIT
Module GUI
Module HASH_TABLE
Module ISOMETRIC_ENGINE
Module LIST
Module LOG
Module LOGNODUP
Module LZ4
Module NETWORKING
Module NETWORK_MSG
Module N_COOKIES
Module N_DIFF
Module N_DIGEST
Module N_DISK_CACHE
Module N_DNS
Module N_ECC
Module N_ENTROPY
Module N_FILES
Module N_FLUIDS
Module N_GUI_COLOR
Module N_GUI_COLORWHEEL
Module N_GUI_CURVE
Module N_GUI_GRID
Module N_GUI_POPUP
Module N_HEX
Module N_HTTP3
Module N_HTTP_COOKIE
Module N_HTTP_FORM
Module N_HTTP_MIME
Module N_HTTP_REQ
Module N_HTTP_RESP
Module N_HTTP_ROUTER
Module N_HTTP_SERVER
Module N_ICC
Module N_JSON
Module N_KAFKA
Module N_LINALG
Module N_MODULE
Module N_OPENSSL_COMPAT
Module N_PRETTY
Module N_PTH
Module N_QUERY
Module N_RANDOM
Module N_SHA256
Module N_STR
Module N_TIFF
Module N_TIME
Module N_USER
Module N_X509
Module N_ZIP
Module PARTICLES
Module PCRE
Module PHYSICS
Module SIGNALS
Module STACK
Module THREADS
Module TRAJECTORY
Module TREE
Module ZLIB
Modules
Pages
Struct A4_BMP
Struct A4_TIMER
Struct AABB3D
Struct ANIM_DATA
Struct ANIM_FRAME
Struct ANIM_GFX
Struct ANIM_LIB
Struct ASTAR_BOX
Struct ASTAR_CELL
Struct ASTAR_GRID
Struct ASTAR_HEAP
Struct ASTAR_HEAP_NODE
Struct ASTAR_NODE
Struct ASTAR_PATH
Struct AVRO_FIELD
Struct AVRO_READER
Struct AVRO_SCHEMA
Struct BITMAP
Struct CALLBACK_DATA
Struct CA_CTX
Struct CLIENT_JOB
Struct CLIENT_STATE
Struct CONFIG_FILE
Struct CONFIG_FILE_SECTION
Struct CYPHER_PARAM
Struct DATA
Struct DEMO_PANEL
Struct DICTIONARY_DEFINITION
Struct DICTIONARY_ENTRY
Struct DIFF_OUT
Struct DIFF_TEXT
Struct DRAW_STATE
Struct DR_ENTITY
Struct DR_STATE
Struct DR_VEC3
Struct ExceptionContextList
Struct FONT
Struct GAME_ENV
Struct H2BUF
Struct H2_DYN_ENTRY
Struct H2_STREAM
Struct HASH_NODE
Struct HASH_TABLE
Struct ISO_DRAW_ENTRY
Struct ISO_MAP
Struct ISO_PROJECTION
Struct ISO_TILE_SEGMENT
Struct ISO_TILE_SEGMENTS
Struct ISO_VISIBLE_EDGES
Struct JS_STATE
Struct LEX_ITEM
Struct LIST
Struct LIST_NODE
Struct LOG_LEVELS
Struct LexMenu
Struct MEMBER
Struct MIDI
Struct NESTED
Struct NETWORK
Struct NETWORK_HTTP_INFO
Struct NETWORK_POOL
Struct NETWORK_SSL_THREAD_PARAMS
Struct NETW_ACCEPT_POOL
Struct NETW_ACCEPT_POOL_STATS
Struct NETW_MSG
Struct NETW_SNI_ARG
Struct NODE_DATA
Struct N_ATLAS
Struct N_ATLAS_PAGE
Struct N_CLOCK_SYNC
Struct N_COOKIE
Struct N_COOKIE_JAR
Struct N_DIFF_LINE
Struct N_DIFF_RESULT
Struct N_DISK_CACHE
Struct N_ENTROPY_BIT
Struct N_ENTROPY_POS
Struct N_FILE_INFO
Struct N_FLUID
Struct N_FLUID_THREAD_PARAMS
Struct N_FORM_FIELD
Struct N_GIT_COMMIT_INFO
Struct N_GIT_REMOTE_AUTH
Struct N_GIT_REPO
Struct N_GIT_STATUS_ENTRY
Struct N_GUI_BUTTON_DATA
Struct N_GUI_CHECKBOX_DATA
Struct N_GUI_COLOR_PICKER
Struct N_GUI_COMBOBOX_DATA
Struct N_GUI_CTX
Struct N_GUI_CUSTOM_DATA
Struct N_GUI_DATAGRID_COL
Struct N_GUI_DATAGRID_DATA
Struct N_GUI_DROPMENU_DATA
Struct N_GUI_DROPMENU_ENTRY
Struct N_GUI_GRID_CALLBACKS
Struct N_GUI_HEXVIEW_DATA
Struct N_GUI_IMAGE_DATA
Struct N_GUI_KVTABLE
Struct N_GUI_KV_CONTENT
Struct N_GUI_KV_ROW
Struct N_GUI_LABEL_DATA
Struct N_GUI_LISTBOX_DATA
Struct N_GUI_LISTITEM
Struct N_GUI_MARKDOWNVIEW_DATA
Struct N_GUI_MD_FONTS
Struct N_GUI_MD_STYLE
Struct N_GUI_MEAS_ENTRY
Struct N_GUI_MOUSE_EVENT
Struct N_GUI_PENDING_GEOM
Struct N_GUI_PENDING_WIDGET
Struct N_GUI_POPUP
Struct N_GUI_PROGRESSBAR_DATA
Struct N_GUI_RADIOLIST_DATA
Struct N_GUI_SB_TRACK
Struct N_GUI_SCROLLBAR_DATA
Struct N_GUI_SECTION
Struct N_GUI_SECTIONLIST
Struct N_GUI_SLIDER_DATA
Struct N_GUI_SPLITPANE_DATA
Struct N_GUI_STYLE
Struct N_GUI_SUGGEST_SINK
Struct N_GUI_SYNTAXVIEW_DATA
Struct N_GUI_TAB_PANEL
Struct N_GUI_TB_BUTTONS
Struct N_GUI_TEXTAREA_DATA
Struct N_GUI_TEXT_DIMS
Struct N_GUI_THEME
Struct N_GUI_TREE
Struct N_GUI_TREE_NODE
Struct N_GUI_TREE_STATE
Struct N_GUI_WIDGET
Struct N_GUI_WINDOW
Struct N_H2_FRAME
Struct N_H2_HEADER
Struct N_H2_HPACK
Struct N_H2_REQUEST
Struct N_H2_SERVER
Struct N_H2_SETTING
Struct N_HTML_ATTR_
Struct N_HTML_FORM
Struct N_HTML_TAG_
Struct N_HTTP3_RESPONSE
Struct N_HTTP_FIELD
Struct N_HTTP_FORM
Struct N_HTTP_FORM_MULTIPART
Struct N_HTTP_FORM_PART
Struct N_HTTP_MULTIPART_PART
Struct N_HTTP_REQUEST
Struct N_HTTP_RESP
Struct N_HTTP_RESPONSE
Struct N_HTTP_SERVER
Struct N_HTTP_SERVER_CONFIG
Struct N_HTTP_SERVER_REQUEST
Struct N_HTTP_SERVER_STATS
Struct N_HTTP_SET_COOKIE
Struct N_HTTP_STREAM
Struct N_ISO_CAMERA
Struct N_ISO_OBJECT
Struct N_KAFKA
Struct N_KAFKA_EVENT
Struct N_MOCK_SERVER
Struct N_MODULE
Struct N_PARALLEL_SLICE
Struct N_PARALLEL_TEAM
Struct N_PARALLEL_WORKER
Struct N_PCRE
Struct N_PROXY_CFG
Struct N_PTH
Struct N_PTH_TENSOR
Struct N_QUERY
Struct N_ROUTE
Struct N_ROUTER
Struct N_ROUTE_MATCH
Struct N_SHA256
Struct N_SOCKET
Struct N_SPAWN_PIPE
Struct N_SSE_CONN
Struct N_SSE_EVENT
Struct N_STR
Struct N_TIFF
Struct N_TIFF_ENTRY
Struct N_TIME
Struct N_URL
Struct N_URL_PARAM
Struct N_USER
Struct N_USERLIST
Struct N_WS_CONN
Struct N_WS_FRAME
Struct N_WS_MESSAGE
Struct N_ZIP_MEMBER
Struct OCTREE
Struct OCTREE_NODE
Struct PARSER
Struct PARTICLE
Struct PARTICLE_EMITTER
Struct PARTICLE_SYSTEM
Struct PEER
Struct PHYSICS
Struct PK_OBJ
Struct PK_VM
Struct POINT2D
Struct POINT3D
Struct PRETTY_OUT
Struct QNODE
Struct QUADTREE
Struct QUADTREE_NODE
Struct REC
Struct RGB
Struct SAMPLE
Struct SEEN
Struct SHARER
Struct SRV
Struct SRV_ARG
Struct STACK
Struct STACK_ITEM
Struct SYNTAX_RUN
Struct THREAD_POOL
Struct THREAD_POOL_NODE
Struct THREAD_WAITING_PROC
Struct TRAJECTORY
Struct TRAJECTORY_POINT
Struct TREE
Struct TREE_NODE
Struct TS_LOG
Struct WAYPOINT3D
Struct WORKER_ARG
Struct ZIP_SINK
Struct _N_DC_ENTRY
Struct _N_DC_GROUP_ENTRY
Struct _N_GUI_COLORWHEEL
Struct _N_GUI_COLORWHEEL_BLOCK
Struct _N_GUI_COLORWHEEL_MOUSE
Struct _N_GUI_CURVE
Struct _N_GUI_CURVE_BLOCK
Struct _N_GUI_CURVE_MOUSE
Struct _N_GUI_GRID
Struct _N_GUI_POPUP_ENTRY
Struct _N_GUI_POPUP_LEVEL
Struct _N_GUI_SB_SKIN
Struct _N_GUI_TA_STEP
Struct _N_GUI_THUMB
Struct _N_GUI_WRAP_ACC
Struct _compiled
Struct _conn
Struct _ip_slot
Struct _mp_param
Struct _n_civil
Struct _seg
Struct client
Struct collector
Struct date_vector
Struct md_atom
Struct md_cells
Struct md_hit
Struct md_line_ctx
Struct mline
Struct n_http_req_header
Struct n_http_req_limits
Struct n_http_request
Struct n_markdown_block
Struct n_markdown_doc
Struct n_markdown_inline
Struct n_markdown_span
Struct n_reactor_stats
Struct n_url_safe
Struct n_url_safe_head
Struct n_wrap_item
Struct producer_state
Struct rects
Struct reply
Struct runner
Struct sink
Struct tbuf
Struct wrap_hit
Struct wrap_span
Structures
Union COORD_VALUE
Union HASH_DATA
Union NODE_DATA_TYPES
Union STACK_DATA
dir_310dcdd4703e877a6b99d5dc3fc54d27
dir_68267d1309a1af8e8297ef4c3efbcdba
dir_d28a4824dc47e487b107a5db32ef43c4
dir_d44c64559bbebec7f509842c48db8b23
ex__accept__pool__client_8c
ex__accept__pool__server_8c
ex__allegro__4_8c
ex__atlas_8c
ex__avro_8c
ex__base64_8c
ex__bind__exclusive_8c
ex__clipboard_8c
ex__clock__sync_8c
ex__common_8c
ex__configfile_8c
ex__cookies_8c
ex__crypto_8c
ex__diff_8c
ex__digest_8c
ex__disk__cache_8c
ex__dns_8c
ex__ecc_8c
ex__entropy_8c
ex__exceptions_8c
ex__file_8c
ex__file__copy_8c
ex__fluid_8c
ex__fluid__config_8c
ex__fluid__config_8h
ex__git_8c
ex__gui_8c
ex__gui__bench_8c
ex__gui__button__keys_8c
ex__gui__checkbox__label_8c
ex__gui__checkbox__release_8c
ex__gui__color__picker_8c
ex__gui__colorwheel_8c
ex__gui__combobox__items_8c
ex__gui__combobox__wheel_8c
ex__gui__cursor_8c
ex__gui__curve_8c
ex__gui__custom_8c
ex__gui__custom__mouse_8c
ex__gui__datagrid_8c
ex__gui__datagrid__keys_8c
ex__gui__datagrid__sort_8c
ex__gui__detach_8c
ex__gui__dictionary_8c
ex__gui__display__fallback_8c
ex__gui__double__click_8c
ex__gui__dropmenu_8c
ex__gui__frameless__drag_8c
ex__gui__global__scroll_8c
ex__gui__glyph_8c
ex__gui__grid_8c
ex__gui__hexview_8c
ex__gui__hover_8c
ex__gui__isometric_8c
ex__gui__kvtable_8c
ex__gui__label__fits_8c
ex__gui__label__lines_8c
ex__gui__layout__persist_8c
ex__gui__listbox_8c
ex__gui__md_8c
ex__gui__multiwin_8c
ex__gui__network_8c
ex__gui__particles_8c
ex__gui__placeholder_8c
ex__gui__popup_8c
ex__gui__progressbar_8c
ex__gui__reentrant_8c
ex__gui__round_8c
ex__gui__sectionlist_8c
ex__gui__slider__default_8c
ex__gui__slider__handle_8c
ex__gui__slider__step_8c
ex__gui__splitpane_8c
ex__gui__suggest_8c
ex__gui__syntaxview_8c
ex__gui__text__shorten_8c
ex__gui__textarea__selection_8c
ex__gui__tooltip_8c
ex__gui__tree__lazy_8c
ex__gui__tree__order_8c
ex__gui__tree__state_8c
ex__gui__window__rect_8c
ex__gui__zorder_8c
ex__hash_8c
ex__hex_8c
ex__html_8c
ex__http2_8c
ex__http2__server_8c
ex__http3_8c
ex__http__cookie_8c
ex__http__form_8c
ex__http__mime_8c
ex__http__req_8c
ex__http__response_8c
ex__http__router_8c
ex__http__server_8c
ex__icc_8c
ex__iso__astar_8c
ex__json_8c
ex__kafka_8c
ex__linalg_8c
ex__list_8c
ex__log_8c
ex__markdown_8c
ex__module_8c
ex__monolith_8c
ex__network_8c
ex__network_8h
ex__network__accept_8c
ex__network__alpn_8c
ex__network__connect__abort_8c
ex__network__mock_8c
ex__network__proxy_8c
ex__network__reactor_8c
ex__network__sni_8c
ex__network__sse_8c
ex__network__ssl_8c
ex__network__ssl__hardened_8c
ex__network__ssl__hostname_8c
ex__network__ssl__nonblock_8c
ex__network__tls__defaults_8c
ex__network__ws_8c
ex__network__ws__limits_8c
ex__nstr_8c
ex__parallel__for_8c
ex__parallel__team_8c
ex__pcre_8c
ex__pretty_8c
ex__pth_8c
ex__query_8c
ex__random_8c
ex__sha256_8c
ex__signals_8c
ex__spawn_8c
ex__ssrf_8c
ex__stack_8c
ex__thread__pool__discard_8c
ex__thread__pool__wait_8c
ex__threads_8c
ex__tiff_8c
ex__time__formats_8c
ex__trajectory_8c
ex__trees_8c
ex__url_8c
ex__url__safe_8c
ex__wrap_8c
ex__x509_8c
ex__zip_8c
ex__zlib_8c
ex_accept_pool_client_8c example
ex_accept_pool_server_8c example
ex_allegro_4_8c example
ex_atlas_8c example
ex_base64_8c example
ex_clock_sync_8c example
ex_common_8c example
ex_configfile_8c example
ex_cookies_8c example
ex_crypto_8c example
ex_diff_8c example
ex_digest_8c example
ex_disk_cache_8c example
ex_dns_8c example
ex_ecc_8c example
ex_entropy_8c example
ex_exceptions_8c example
ex_file_8c example
ex_file_copy_8c example
ex_fluid_8c example
ex_fluid_config_8c example
ex_fluid_config_8h example
ex_git_8c example
ex_gui_8c example
ex_gui_bench_8c example
ex_gui_button_keys_8c example
ex_gui_checkbox_label_8c example
ex_gui_checkbox_release_8c example
ex_gui_color_picker_8c example
ex_gui_colorwheel_8c example
ex_gui_combobox_items_8c example
ex_gui_combobox_wheel_8c example
ex_gui_cursor_8c example
ex_gui_curve_8c example
ex_gui_custom_8c example
ex_gui_custom_mouse_8c example
ex_gui_datagrid_8c example
ex_gui_datagrid_keys_8c example
ex_gui_datagrid_sort_8c example
ex_gui_detach_8c example
ex_gui_dictionary_8c example
ex_gui_display_fallback_8c example
ex_gui_double_click_8c example
ex_gui_dropmenu_8c example
ex_gui_frameless_drag_8c example
ex_gui_global_scroll_8c example
ex_gui_glyph_8c example
ex_gui_grid_8c example
ex_gui_hexview_8c example
ex_gui_hover_8c example
ex_gui_isometric_8c example
ex_gui_kvtable_8c example
ex_gui_label_fits_8c example
ex_gui_label_lines_8c example
ex_gui_layout_persist_8c example
ex_gui_listbox_8c example
ex_gui_md_8c example
ex_gui_multiwin_8c example
ex_gui_network_8c example
ex_gui_particles_8c example
ex_gui_placeholder_8c example
ex_gui_popup_8c example
ex_gui_progressbar_8c example
ex_gui_reentrant_8c example
ex_gui_round_8c example
ex_gui_sectionlist_8c example
ex_gui_slider_default_8c example
ex_gui_slider_handle_8c example
ex_gui_slider_step_8c example
ex_gui_splitpane_8c example
ex_gui_suggest_8c example
ex_gui_syntaxview_8c example
ex_gui_text_shorten_8c example
ex_gui_textarea_selection_8c example
ex_gui_tooltip_8c example
ex_gui_tree_lazy_8c example
ex_gui_tree_order_8c example
ex_gui_tree_state_8c example
ex_gui_window_rect_8c example
ex_gui_zorder_8c example
ex_hash_8c example
ex_hex_8c example
ex_html_8c example
ex_http2_8c example
ex_http2_server_8c example
ex_http_cookie_8c example
ex_http_form_8c example
ex_http_mime_8c example
ex_http_req_8c example
ex_http_response_8c example
ex_http_router_8c example
ex_http_server_8c example
ex_icc_8c example
ex_iso_astar_8c example
ex_json_8c example
ex_kafka_8c example
ex_linalg_8c example
ex_list_8c example
ex_log_8c example
ex_markdown_8c example
ex_module_8c example
ex_monolith_8c example
ex_network_8c example
ex_network_8h example
ex_network_accept_8c example
ex_network_alpn_8c example
ex_network_connect_abort_8c example
ex_network_mock_8c example
ex_network_proxy_8c example
ex_network_reactor_8c example
ex_network_sni_8c example
ex_network_sse_8c example
ex_network_ssl_8c example
ex_network_ssl_hardened_8c example
ex_network_ssl_hostname_8c example
ex_network_ssl_nonblock_8c example
ex_network_tls_defaults_8c example
ex_network_ws_8c example
ex_network_ws_limits_8c example
ex_nstr_8c example
ex_parallel_for_8c example
ex_parallel_team_8c example
ex_pcre_8c example
ex_pretty_8c example
ex_pth_8c example
ex_query_8c example
ex_random_8c example
ex_sha256_8c example
ex_signals_8c example
ex_spawn_8c example
ex_ssrf_8c example
ex_stack_8c example
ex_threads_8c example
ex_tiff_8c example
ex_time_formats_8c example
ex_trajectory_8c example
ex_trees_8c example
ex_url_8c example
ex_url_safe_8c example
ex_wrap_8c example
ex_x509_8c example
ex_zip_8c example
ex_zlib_8c example
mainpage_8dox
n__3d_8c
n__3d_8h
n__aabb_8c
n__aabb_8h
n__allegro5_8c
n__allegro5_8h
n__allegro__4_8c
n__allegro__4_8h
n__anim_8c
n__anim_8h
n__astar_8c
n__astar_8h
n__atlas_8c
n__atlas_8h
n__avro_8c
n__avro_8h
n__base64_8c
n__base64_8h
n__clipboard_8c
n__clipboard_8h
n__clock__sync_8c
n__clock__sync_8h
n__common_8c
n__common_8h
n__config__file_8c
n__config__file_8h
n__cookies_8c
n__cookies_8h
n__crypto_8c
n__crypto_8h
n__dead__reckoning_8c
n__dead__reckoning_8h
n__diff_8c
n__diff_8h
n__digest_8c
n__digest_8h
n__disk__cache_8c
n__disk__cache_8h
n__dns_8c
n__dns_8h
n__ecc_8c
n__ecc_8h
n__entropy_8c
n__entropy_8h
n__enum_8h
n__exceptions_8c
n__exceptions_8h
n__files_8c
n__files_8h
n__fluids_8c
n__fluids_8h
n__games_8c
n__games_8h
n__git_8c
n__git_8h
n__gui_8c
n__gui_8h
n__gui__color_8c
n__gui__color_8h
n__gui__colorwheel_8c
n__gui__colorwheel_8h
n__gui__curve_8c
n__gui__curve_8h
n__gui__grid_8c
n__gui__grid_8h
n__gui__md_8c
n__gui__md_8h
n__gui__popup_8c
n__gui__popup_8h
n__hash_8c
n__hash_8h
n__hex_8c
n__hex_8h
n__html_8c
n__html_8h
n__http2_8c
n__http2_8h
n__http2__server_8c
n__http2__server_8h
n__http3_8c
n__http3_8h
n__http__cookie_8c
n__http__cookie_8h
n__http__form_8c
n__http__form_8h
n__http__mime_8c
n__http__mime_8h
n__http__req_8c
n__http__req_8h
n__http__response_8c
n__http__response_8h
n__http__router_8c
n__http__router_8h
n__http__server_8c
n__http__server_8h
n__icc_8c
n__icc_8h
n__iso__engine_8c
n__iso__engine_8h
n__json_8c
n__json_8h
n__kafka_8c
n__kafka_8h
n__linalg_8c
n__linalg_8h
n__list_8c
n__list_8h
n__log_8c
n__log_8h
n__lz4_8c
n__lz4_8h
n__markdown_8c
n__markdown_8h
n__module_8c
n__module_8h
n__network_8c
n__network_8h
n__network__accept__pool_8c
n__network__accept__pool_8h
n__network__msg_8c
n__network__msg_8h
n__nodup__log_8c
n__nodup__log_8h
n__openssl__compat_8h
n__particles_8c
n__particles_8h
n__pcre_8c
n__pcre_8h
n__pretty_8c
n__pretty_8h
n__pth_8c
n__pth_8h
n__query_8c
n__query_8h
n__random_8c
n__random_8h
n__reactor_8c
n__reactor_8h
n__sha256_8c
n__sha256_8h
n__signals_8c
n__signals_8h
n__ssrf_8c
n__ssrf_8h
n__stack_8c
n__stack_8h
n__str_8c
n__str_8h
n__thread__pool_8c
n__thread__pool_8h
n__tiff_8c
n__tiff_8h
n__time_8c
n__time_8h
n__trajectory_8c
n__trajectory_8h
n__trees_8c
n__trees_8h
n__url__safe_8c
n__url__safe_8h
n__user_8c
n__user_8h
n__windows_8h
n__wrap_8c
n__wrap_8h
n__x509_8c
n__x509_8h
n__zip_8c
n__zip_8h
n__zlib_8c
n__zlib_8h
nilorea_8h
No results
6
ex__gui__multiwin_8c
gullradriel edited this page 2026-10-09 00:55:18 +02:00
Table of Contents
- examples/ex_gui_multiwin.c
- Functions
- Attributes
- Defines
- Functions Documentation
- function sample_chrome_titlebar
- function process_args
- function on_popup_close
- function on_popup_click
- function on_main_click
- function main
- function expect_true
- function expect_eq_int
- function expect_eq_float
- function ctrl_key
- function count_pixels_differing
- function click_on
- function check_shared_resources
- Attributes Documentation
- variable log_level
- variable g_popup_clicks
- variable g_main_clicks
- variable g_closed_id
- variable g_chrome_titlebar_lit
- variable failures
- Macros Documentation
- Source code
examples/ex_gui_multiwin.c
Functions
| Name | |
|---|---|
| void | sample_chrome_titlebar(int window_id, void * user_data) |
| int | process_args(int argc, char ** argv) |
| void | on_popup_close(int window_id, void * user_data) |
| void | on_popup_click(int widget_id, void * user_data) |
| void | on_main_click(int widget_id, void * user_data) |
| int | main(int argc, char ** argv) |
| void | expect_true(const char * label, int cond) |
| void | expect_eq_int(const char * label, int got, int want) |
| void | expect_eq_float(const char * label, float got, float want) |
| void | ctrl_key(N_GUI_CTX * gui, ALLEGRO_DISPLAY * from, int keycode) |
| int | count_pixels_differing(ALLEGRO_BITMAP * bmp, int x, int y, int w, int h, ALLEGRO_COLOR ref) |
| void | click_on(N_GUI_CTX * gui, ALLEGRO_DISPLAY * from, int x, int y) |
| void | check_shared_resources(const char * who, ALLEGRO_DISPLAY * d, ALLEGRO_FONT * font, ALLEGRO_BITMAP * bmp) |
Attributes
| Name | |
|---|---|
| int | log_level |
| int | g_popup_clicks |
| int | g_main_clicks |
| int | g_closed_id |
| int | g_chrome_titlebar_lit |
| int | failures |
Defines
| Name | |
|---|---|
| POPUP_W | |
| POPUP_H | |
| MAIN_W | |
| MAIN_H | |
| ALLEGRO_UNSTABLE |
Functions Documentation
function sample_chrome_titlebar
static void sample_chrome_titlebar(
int window_id,
void * user_data
)
function process_args
static int process_args(
int argc,
char ** argv
)
function on_popup_close
static void on_popup_close(
int window_id,
void * user_data
)
function on_popup_click
static void on_popup_click(
int widget_id,
void * user_data
)
function on_main_click
static void on_main_click(
int widget_id,
void * user_data
)
function main
int main(
int argc,
char ** argv
)
function expect_true
static void expect_true(
const char * label,
int cond
)
function expect_eq_int
static void expect_eq_int(
const char * label,
int got,
int want
)
function expect_eq_float
static void expect_eq_float(
const char * label,
float got,
float want
)
function ctrl_key
static void ctrl_key(
N_GUI_CTX * gui,
ALLEGRO_DISPLAY * from,
int keycode
)
function count_pixels_differing
static int count_pixels_differing(
ALLEGRO_BITMAP * bmp,
int x,
int y,
int w,
int h,
ALLEGRO_COLOR ref
)
function click_on
static void click_on(
N_GUI_CTX * gui,
ALLEGRO_DISPLAY * from,
int x,
int y
)
function check_shared_resources
static void check_shared_resources(
const char * who,
ALLEGRO_DISPLAY * d,
ALLEGRO_FONT * font,
ALLEGRO_BITMAP * bmp
)
Attributes Documentation
variable log_level
static int log_level = 3;
variable g_popup_clicks
static int g_popup_clicks = 0;
variable g_main_clicks
static int g_main_clicks = 0;
variable g_closed_id
static int g_closed_id = -1;
variable g_chrome_titlebar_lit
static int g_chrome_titlebar_lit = -1;
variable failures
static int failures = 0;
Macros Documentation
define POPUP_W
#define POPUP_W 240
define POPUP_H
#define POPUP_H 160
define MAIN_W
#define MAIN_W 640
define MAIN_H
#define MAIN_H 480
define ALLEGRO_UNSTABLE
#define ALLEGRO_UNSTABLE 1
Source code
/*
* Nilorea Library
* Copyright (C) 2005-2026 Castagnier Mickael
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define ALLEGRO_UNSTABLE 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "nilorea/n_common.h"
#include "nilorea/n_clipboard.h"
#include "nilorea/n_gui.h"
#include "nilorea/n_log.h"
#define MAIN_W 640
#define MAIN_H 480
#define POPUP_W 240
#define POPUP_H 160
static int log_level = LOG_ERR;
static int failures = 0;
/* which button callback fired last, and how many times */
static int g_main_clicks = 0;
static int g_popup_clicks = 0;
/* window id the close callback was invoked for, or -1 */
static int g_closed_id = -1;
static int process_args(int argc, char** argv) {
int opt;
while ((opt = getopt(argc, argv, "V:")) != -1) {
switch (opt) {
case 'V':
if (!strcmp("LOG_NULL", optarg))
log_level = LOG_NULL;
else if (!strcmp("LOG_NOTICE", optarg))
log_level = LOG_NOTICE;
else if (!strcmp("LOG_INFO", optarg))
log_level = LOG_INFO;
else if (!strcmp("LOG_ERR", optarg))
log_level = LOG_ERR;
else if (!strcmp("LOG_DEBUG", optarg))
log_level = LOG_DEBUG;
else
return -1;
break;
default:
return -1;
}
}
return 0;
}
static void expect_true(const char* label, int cond) {
if (!cond) {
n_log(LOG_ERR, "FAIL %s", label);
failures++;
} else {
n_log(LOG_INFO, "ok %s", label);
}
}
static void expect_eq_int(const char* label, int got, int want) {
if (got != want) {
n_log(LOG_ERR, "FAIL %s: got %d, want %d", label, got, want);
failures++;
} else {
n_log(LOG_INFO, "ok %s", label);
}
}
static void expect_eq_float(const char* label, float got, float want) {
float d = got - want;
if (d < 0.0f) d = -d;
if (d > 0.5f) {
n_log(LOG_ERR, "FAIL %s: got %f, want %f", label, (double)got, (double)want);
failures++;
} else {
n_log(LOG_INFO, "ok %s", label);
}
}
static void on_main_click(int widget_id, void* user_data) {
(void)widget_id;
(void)user_data;
g_main_clicks++;
}
static void on_popup_click(int widget_id, void* user_data) {
(void)widget_id;
(void)user_data;
g_popup_clicks++;
}
static void on_popup_close(int window_id, void* user_data) {
(void)user_data;
g_closed_id = window_id;
}
/* Synthesize a left click at (x,y) coming from a given display and feed it to
* the GUI. Passing the display is the whole point: it is what n_gui_process_event
* routes on, so the same coordinates can mean different windows. */
static void click_on(N_GUI_CTX* gui, ALLEGRO_DISPLAY* from, int x, int y) {
ALLEGRO_EVENT ev;
memset(&ev, 0, sizeof(ev));
ev.mouse.display = from;
ev.mouse.x = x;
ev.mouse.y = y;
ev.mouse.button = 1;
ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
n_gui_process_event(gui, ev);
ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
n_gui_process_event(gui, ev);
}
/* Send a Ctrl+<keycode> KEY_CHAR event, the form the textarea handler expects */
static void ctrl_key(N_GUI_CTX* gui, ALLEGRO_DISPLAY* from, int keycode) {
ALLEGRO_EVENT ev;
memset(&ev, 0, sizeof(ev));
ev.type = ALLEGRO_EVENT_KEY_CHAR;
ev.keyboard.display = from;
ev.keyboard.keycode = keycode;
ev.keyboard.modifiers = ALLEGRO_KEYMOD_CTRL;
ev.keyboard.unichar = 0;
n_gui_process_event(gui, ev);
}
/* Count pixels differing from a reference colour in a w*h box at (x,y).
* Used to prove that something was actually rasterized into a backbuffer. */
static int count_pixels_differing(ALLEGRO_BITMAP* bmp, int x, int y, int w, int h, ALLEGRO_COLOR ref) {
int differing = 0;
unsigned char rr, rg, rb;
al_unmap_rgb(ref, &rr, &rg, &rb);
for (int py = y; py < y + h; py++) {
for (int px = x; px < x + w; px++) {
unsigned char cr, cg, cb;
al_unmap_rgb(al_get_pixel(bmp, px, py), &cr, &cg, &cb);
/* generous tolerance: blending, sRGB conversion and driver rounding
all move a pixel a little without changing what it means here */
if (abs((int)cr - (int)rr) > 24 || abs((int)cg - (int)rg) > 24 || abs((int)cb - (int)rb) > 24)
differing++;
}
}
return differing;
}
/* Pixels sampled by sample_chrome_titlebar, -1 until it has run. */
static int g_chrome_titlebar_lit = -1;
/* Count the painted pixels in the top-left corner of a detached window, where an
* own-chrome title bar lives.
*
* This runs as a content-draw callback, i.e. from inside n_gui_draw_detached,
* after the window and its title bar have been painted into the native
* backbuffer and BEFORE al_flip_display. The sample has to be taken while the
* frame is still the current one: after a flip the backbuffer contents are
* undefined, and a page-flipping driver (Mesa on real hardware) hands back a
* discarded buffer, so an after-the-fact readback only ever worked on a
* copy-swapping one such as llvmpipe under Xvfb.
*
* The frame was cleared to bg_normal and the title bar fills bg_active with the
* title text on top, so "differs from bg_normal" is exactly the title bar. */
static void sample_chrome_titlebar(int window_id, void* user_data) {
N_GUI_CTX* ctx = (N_GUI_CTX*)user_data;
ALLEGRO_BITMAP* target = al_get_target_bitmap();
N_GUI_WINDOW* win = ctx ? n_gui_get_window(ctx, window_id) : NULL;
if (!target || !win) return;
g_chrome_titlebar_lit = count_pixels_differing(target, 2, 2, 60, 12, win->theme.bg_normal);
}
/* Draw a font glyph run and a bitmap into a display's backbuffer, then read the
* pixels back. Proves the resources created against another display are usable
* here, which is the real portability risk of a second ALLEGRO_DISPLAY. */
static void check_shared_resources(const char* who, ALLEGRO_DISPLAY* d, ALLEGRO_FONT* font, ALLEGRO_BITMAP* bmp) {
char label[128];
ALLEGRO_COLOR black = al_map_rgb(0, 0, 0);
ALLEGRO_BITMAP* back;
int lit;
al_set_target_backbuffer(d);
al_clear_to_color(black);
al_draw_text(font, al_map_rgb(255, 255, 255), 4.0f, 4.0f, 0, "NILOREA");
al_draw_bitmap(bmp, 4.0f, 40.0f, 0);
back = al_get_backbuffer(d);
/* the text: at least a few glyph pixels must have been written */
lit = count_pixels_differing(back, 0, 0, 120, 24, black);
snprintf(label, sizeof(label), "%s: shared font rasterizes (%d lit pixels)", who, lit);
expect_true(label, lit > 10);
/* the bitmap: the 32x32 block must be there, in its own colour */
lit = count_pixels_differing(back, 6, 42, 24, 24, black);
snprintf(label, sizeof(label), "%s: shared bitmap draws (%d lit pixels)", who, lit);
expect_true(label, lit > 400);
}
int main(int argc, char** argv) {
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() || !al_init_primitives_addon()) {
n_log(LOG_ERR, "allegro init failed");
return 1;
}
al_install_keyboard();
al_install_mouse();
al_set_new_display_flags(ALLEGRO_WINDOWED);
ALLEGRO_DISPLAY* main_display = al_create_display(MAIN_W, MAIN_H);
if (!main_display) {
/* headless build machine: nothing to test, and nothing broken either */
n_log(LOG_NOTICE, "ex_gui_multiwin: no display available, test skipped");
return 0;
}
ALLEGRO_EVENT_QUEUE* queue = al_create_event_queue();
ALLEGRO_FONT* font = al_create_builtin_font();
if (!queue || !font) {
n_log(LOG_ERR, "queue/font creation failed");
if (queue) al_destroy_event_queue(queue);
if (font) al_destroy_font(font);
al_destroy_display(main_display);
return 1;
}
al_register_event_source(queue, al_get_display_event_source(main_display));
/* A video bitmap created while ONLY the main display exists. Everything it
* is drawn onto later happens after a second display was created, which is
* exactly the resource-sharing case that can bite on some drivers. */
ALLEGRO_BITMAP* shared_bmp = al_create_bitmap(32, 32);
if (!shared_bmp) {
n_log(LOG_ERR, "al_create_bitmap failed");
al_destroy_event_queue(queue);
al_destroy_font(font);
al_destroy_display(main_display);
return 1;
}
al_set_target_bitmap(shared_bmp);
al_clear_to_color(al_map_rgb(255, 0, 255));
al_set_target_backbuffer(main_display);
N_GUI_CTX* gui = n_gui_new_ctx(font);
if (!gui) {
n_log(LOG_ERR, "n_gui_new_ctx failed");
al_destroy_bitmap(shared_bmp);
al_destroy_event_queue(queue);
al_destroy_font(font);
al_destroy_display(main_display);
return 1;
}
n_gui_set_display(gui, main_display);
n_gui_set_display_size(gui, (float)MAIN_W, (float)MAIN_H);
n_gui_set_event_queue(gui, queue);
expect_true("event queue round-trips", n_gui_get_event_queue(gui) == queue);
/* Two frameless windows both anchored at (0,0) with a button at the same
* spot. Once one of them is detached they live on different displays, so
* the identical coordinates are the sharpest possible routing test. */
int main_win = n_gui_add_window(gui, "Main Panel", 0.0f, 0.0f, 300.0f, 200.0f);
int popup_win = n_gui_add_window(gui, "Popup", 0.0f, 0.0f, (float)POPUP_W, (float)POPUP_H);
expect_true("windows created", main_win >= 0 && popup_win >= 0);
n_gui_window_set_flags(gui, main_win, N_GUI_WIN_FRAMELESS);
n_gui_window_set_flags(gui, popup_win, N_GUI_WIN_FRAMELESS);
int main_btn = n_gui_add_button(gui, main_win, "main", 10.0f, 10.0f, 100.0f, 30.0f, N_GUI_SHAPE_RECT, on_main_click, NULL);
int popup_btn = n_gui_add_button(gui, popup_win, "popup", 10.0f, 10.0f, 100.0f, 30.0f, N_GUI_SHAPE_RECT, on_popup_click, NULL);
int popup_txt = n_gui_add_textarea(gui, popup_win, 10.0f, 60.0f, 200.0f, 24.0f, 0, 64, NULL, NULL);
expect_true("widgets created", main_btn >= 0 && popup_btn >= 0 && popup_txt >= 0);
/* baseline: still a pop-up, both windows on the main display */
expect_eq_int("nothing detached initially", n_gui_count_detached(gui), 0);
expect_true("popup not detached initially", !n_gui_window_is_detached(gui, popup_win));
/* DETACH */
expect_eq_int("detach succeeds", n_gui_window_detach(gui, popup_win, N_GUI_DETACH_RESIZABLE), 0);
ALLEGRO_DISPLAY* popup_display = n_gui_window_get_display(gui, popup_win);
expect_true("native display created", popup_display != NULL);
expect_true("native display differs from the main one", popup_display != main_display);
expect_eq_int("one window detached", n_gui_count_detached(gui), 1);
expect_eq_int("detached flag set", n_gui_window_is_detached(gui, popup_win), 1);
expect_eq_int("display maps back to the window", n_gui_window_from_display(gui, popup_display), popup_win);
expect_eq_int("main display maps to no detached window", n_gui_window_from_display(gui, main_display), -1);
if (!popup_display) {
n_log(LOG_ERR, "no native display, cannot continue");
n_gui_destroy_ctx(&gui);
al_destroy_bitmap(shared_bmp);
al_destroy_event_queue(queue);
al_destroy_font(font);
al_destroy_display(main_display);
return 1;
}
/* the detached window fills its display, at the origin, without our chrome */
{
N_GUI_WINDOW* w = n_gui_get_window(gui, popup_win);
expect_true("detached window sits at the origin", w && w->x == 0.0f && w->y == 0.0f);
expect_eq_float("detached window width matches the display", w ? w->w : -1.0f, (float)al_get_display_width(popup_display));
expect_eq_float("detached window height matches the display", w ? w->h : -1.0f, (float)al_get_display_height(popup_display));
expect_true("detached window is frameless", w && (w->flags & N_GUI_WIN_FRAMELESS));
}
/* SHARED RESOURCES: font glyphs and a bitmap created before the second
display existed must still rasterize on both displays. */
check_shared_resources("main display", main_display, font, shared_bmp);
check_shared_resources("native display", popup_display, font, shared_bmp);
/* EVENT ROUTING: identical coordinates, different display, different window */
g_main_clicks = g_popup_clicks = 0;
click_on(gui, main_display, 30, 25);
expect_eq_int("click on the main display hits the main button", g_main_clicks, 1);
expect_eq_int("click on the main display does not reach the detached window", g_popup_clicks, 0);
g_main_clicks = g_popup_clicks = 0;
click_on(gui, popup_display, 30, 25);
expect_eq_int("click on the native display hits the detached button", g_popup_clicks, 1);
expect_eq_int("click on the native display does not reach the main window", g_main_clicks, 0);
/* an event with no display (timers, and the headless tests that synthesize
events with memset) still reaches the main display's windows */
g_main_clicks = g_popup_clicks = 0;
click_on(gui, NULL, 30, 25);
expect_eq_int("display-less event falls back to the main pass", g_main_clicks, 1);
expect_eq_int("display-less event does not reach the detached window", g_popup_clicks, 0);
/* n_gui_wants_mouse answers for the main display only */
expect_eq_int("wants_mouse still answers for the main display", n_gui_wants_mouse(gui), 1);
/* CLIPBOARD: the context follows the display input came from, so a copy
made inside the detached window addresses that display. */
{
const char* payload = "nilorea-multiwin-clipboard";
n_gui_textarea_set_text(gui, popup_txt, payload);
n_gui_set_focus(gui, popup_txt);
/* an event from the native display makes it the active one */
click_on(gui, popup_display, 30, 25);
n_gui_set_focus(gui, popup_txt);
expect_true("active display follows the native window", gui->active_display == popup_display);
ctrl_key(gui, popup_display, ALLEGRO_KEY_A); /* select all */
ctrl_key(gui, popup_display, ALLEGRO_KEY_C); /* copy */
/* read back through whichever backend the GUI wrote with */
char* got = NULL;
if (n_clipboard_available()) {
got = n_clipboard_get(N_CLIPBOARD_CLIPBOARD);
expect_true("clipboard copy from the detached window round-trips",
got != NULL && strcmp(got, payload) == 0);
if (got) {
if (strcmp(got, payload) != 0)
n_log(LOG_ERR, "clipboard: got '%s', want '%s'", got, payload);
free(got);
}
} else {
char* al_got = al_get_clipboard_text(popup_display);
expect_true("clipboard copy from the detached window round-trips",
al_got != NULL && strcmp(al_got, payload) == 0);
if (al_got) {
if (strcmp(al_got, payload) != 0)
n_log(LOG_ERR, "clipboard: got '%s', want '%s'", al_got, payload);
al_free(al_got);
}
}
n_gui_set_focus(gui, -1);
}
/* DRAWING: the main pass must not touch the native display, and the native
pass must not touch the main one. Both are exercised for real here. */
al_set_target_backbuffer(main_display);
al_clear_to_color(al_map_rgb(0, 0, 0));
n_gui_draw(gui);
{
/* the detached window drew nothing on the main display: its button
would have landed in this box if the pass filter were missing */
ALLEGRO_BITMAP* back = al_get_backbuffer(main_display);
int lit = count_pixels_differing(back, 320, 300, 40, 40, al_map_rgb(0, 0, 0));
expect_eq_int("main pass leaves the far corner untouched", lit, 0);
}
n_gui_draw_detached(gui);
expect_eq_int("draw_detached keeps the window alive", n_gui_count_detached(gui), 1);
/* the caller's target survives a detached draw pass */
al_set_target_backbuffer(main_display);
n_gui_draw_detached(gui);
expect_true("draw_detached restores the caller's target", al_get_target_bitmap() == al_get_backbuffer(main_display));
/* CLOSE REQUEST from the window manager: the callback runs, and the display
is only released at the next n_gui_draw_detached. */
{
ALLEGRO_EVENT ev;
n_gui_window_set_close_callback(gui, popup_win, on_popup_close, NULL);
g_closed_id = -1;
memset(&ev, 0, sizeof(ev));
ev.type = ALLEGRO_EVENT_DISPLAY_CLOSE;
ev.display.source = popup_display;
expect_eq_int("DISPLAY_CLOSE is consumed", n_gui_process_event(gui, ev), 1);
expect_eq_int("close callback fired for the right window", g_closed_id, popup_win);
expect_eq_int("display survives until the next draw pass", n_gui_count_detached(gui), 1);
n_gui_draw_detached(gui);
expect_eq_int("display released by the deferred pass", n_gui_count_detached(gui), 0);
expect_eq_int("window is closed", n_gui_window_is_open(gui, popup_win), 0);
/* geometry fell back to the pop-up one */
N_GUI_WINDOW* w = n_gui_get_window(gui, popup_win);
expect_eq_float("pop-up width restored", w ? w->w : -1.0f, (float)POPUP_W);
expect_eq_float("pop-up height restored", w ? w->h : -1.0f, (float)POPUP_H);
}
/* RE-OPEN: the detach preference survived the close, so opening the window
brings the native window back rather than a pop-up. */
n_gui_open_window(gui, popup_win);
expect_eq_int("re-opening restores the native window", n_gui_count_detached(gui), 1);
popup_display = n_gui_window_get_display(gui, popup_win);
expect_true("a fresh native display was created", popup_display != NULL);
/* ATTACH: back to a pop-up, with the original geometry and flags */
expect_eq_int("attach succeeds", n_gui_window_attach(gui, popup_win), 0);
expect_eq_int("nothing detached after attach", n_gui_count_detached(gui), 0);
{
N_GUI_WINDOW* w = n_gui_get_window(gui, popup_win);
expect_eq_float("pop-up x restored", w ? w->x : -1.0f, 0.0f);
expect_eq_float("pop-up y restored", w ? w->y : -1.0f, 0.0f);
expect_eq_float("pop-up w restored", w ? w->w : -1.0f, (float)POPUP_W);
expect_eq_float("pop-up h restored", w ? w->h : -1.0f, (float)POPUP_H);
}
/* and it is hit-testable on the main display again: it is on top of the
main panel, so it takes the click */
g_main_clicks = g_popup_clicks = 0;
click_on(gui, main_display, 30, 25);
expect_eq_int("re-attached pop-up takes clicks on the main display", g_popup_clicks, 1);
/* OWN CHROME (N_GUI_DETACH_OWN_CHROME): the window keeps drawing its own
title bar inside a frameless native window, and that chrome now drives
the OS window instead of x/y/w/h. */
{
int chrome_win = n_gui_add_window(gui, "Chrome", 0.0f, 0.0f, 260.0f, 180.0f);
expect_true("own-chrome window created", chrome_win >= 0);
n_gui_window_set_flags(gui, chrome_win, N_GUI_WIN_RESIZABLE | N_GUI_WIN_BTN_ALL);
expect_eq_int("detach with own chrome succeeds",
n_gui_window_detach(gui, chrome_win, N_GUI_DETACH_OWN_CHROME), 0);
ALLEGRO_DISPLAY* cd = n_gui_window_get_display(gui, chrome_win);
expect_true("own-chrome native display created", cd != NULL);
N_GUI_WINDOW* cw = n_gui_get_window(gui, chrome_win);
/* the key difference from the default: our title bar stays */
expect_true("own-chrome window is NOT forced frameless", cw && !(cw->flags & N_GUI_WIN_FRAMELESS));
/* Allegro documents ALLEGRO_FRAMELESS as varying by platform, and with no
window manager running (bare Xvfb) there are no decorations to remove
in the first place, so the flag does not come back. What N_GUI owns is
the request, not the window manager's answer: report, do not fail. */
if (cd && !(al_get_display_flags(cd) & ALLEGRO_FRAMELESS))
n_log(LOG_NOTICE, "platform did not honour ALLEGRO_FRAMELESS (no window manager?), check skipped");
expect_true("own-chrome native window is resizable (needed by resize/maximize)",
cd && (al_get_display_flags(cd) & ALLEGRO_RESIZABLE));
/* the display covers the WHOLE window, title bar included, unlike the
default where it covers the body only */
expect_eq_float("own-chrome display height covers the title bar too",
cd ? (float)al_get_display_height(cd) : -1.0f, 180.0f);
expect_true("own-chrome window keeps a title bar height", cw && cw->titlebar_h > 0.0f);
/* the OS window carries the window's title */
expect_true("native window title was set before creation", cd != NULL);
/* dragging the title bar moves the OS window, not win->x/y */
if (cd && cw) {
int px0 = 0, py0 = 0, px1 = 0, py1 = 0;
ALLEGRO_EVENT ev;
al_get_window_position(cd, &px0, &py0);
memset(&ev, 0, sizeof(ev));
ev.mouse.display = cd;
ev.mouse.button = 1;
ev.mouse.x = 60;
ev.mouse.y = (int)(cw->titlebar_h / 2.0f);
ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
n_gui_process_event(gui, ev);
expect_true("title bar press starts a drag", (cw->state & N_GUI_WIN_DRAGGING) != 0);
/* An own-chrome drag anchors on the DESKTOP cursor and moves the OS
window by how far the pointer has travelled on the desktop, not by a
display-local delta (see _native_drag_begin: a display-local delta
cannot work for a window that is itself moving). So a synthetic
MOUSE_AXES on its own moves nothing, the real pointer has to travel
with it. Warp it, then assert the window followed by the same amount.
Where the platform will not warp the pointer there is nothing to
measure, so the check is skipped rather than failed. */
int cbx = 0, cby = 0, cax = 0, cay = 0;
int warped = al_get_mouse_cursor_position(&cbx, &cby) &&
al_set_mouse_xy(cd, 90, (int)(cw->titlebar_h / 2.0f)) &&
al_get_mouse_cursor_position(&cax, &cay);
ev.type = ALLEGRO_EVENT_MOUSE_AXES;
ev.mouse.x = 90; /* +30 px to the right */
n_gui_process_event(gui, ev);
al_get_window_position(cd, &px1, &py1);
if (warped && cax != cbx)
expect_eq_int("title bar drag moved the OS window", px1 - px0, cax - cbx);
else
n_log(LOG_NOTICE, "ex_gui_multiwin: pointer cannot be warped here, skipping the OS-window drag check");
expect_eq_float("title bar drag left the window pinned at the origin", cw->x, 0.0f);
ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
n_gui_process_event(gui, ev);
expect_true("drag ends on release", !(cw->state & N_GUI_WIN_DRAGGING));
}
/* minimise shrinks the OS window to the title bar and back.
ALLEGRO_MINIMIZED is read-only, so this is the portable mapping. */
if (cd && cw) {
float full_h = cw->h;
n_gui_minimize_window(gui, chrome_win);
expect_true("minimised flag set", (cw->state & N_GUI_WIN_MINIMISED) != 0);
expect_eq_float("minimise shrank the OS window to the title bar",
(float)al_get_display_height(cd), cw->titlebar_h);
expect_eq_float("minimise kept the full window height for the restore", cw->h, full_h);
n_gui_restore_window(gui, chrome_win);
expect_true("minimised flag cleared", !(cw->state & N_GUI_WIN_MINIMISED));
expect_eq_float("restore grew the OS window back",
(float)al_get_display_height(cd), full_h);
}
/* The same thing again, but driven through the title bar BUTTON and with the
flag set an application actually uses (RESIZABLE alongside OWN_CHROME).
n_gui_minimize_window is the API path; the button goes through
_tb_button_action, and a resizable native window is the configuration a
host detaches with, so neither was covered by the checks above. */
if (cd && cw) {
ALLEGRO_EVENT ev;
float full_h2;
float sz, mid_x;
n_gui_window_set_flags(gui, chrome_win,
n_gui_window_get_flags(gui, chrome_win) | N_GUI_WIN_RESIZABLE);
/* A real minimum size, which is what an application sets so its layout
always has room. It is also what made minimise look broken on a real
desktop: the constraint is handed to the window manager at detach time
and a title bar is far below it, so the shrink was refused and the
window stayed full size while its content hid. Note this check cannot
reproduce that here: window constraints are enforced by the window
manager, and under bare Xvfb there is none, so the shrink succeeds
either way. What it does cover is the title-bar BUTTON path on a
RESIZABLE own-chrome window, which nothing else exercises. */
cw->min_w = 200.0f;
cw->min_h = 150.0f;
if (al_set_window_constraints(cd, 200, 150, 0, 0))
al_apply_window_constraints(cd, true);
n_gui_restore_window(gui, chrome_win);
full_h2 = cw->h;
/* buttons run right to left: close, maximize, minimize */
sz = gui->style.tb_btn_size > 0.0f ? gui->style.tb_btn_size : cw->titlebar_h - 4.0f;
mid_x = cw->x + cw->w - gui->style.tb_btn_right_margin - 2.0f * (sz + gui->style.tb_btn_spacing) - sz / 2.0f;
memset(&ev, 0, sizeof(ev));
ev.mouse.display = cd;
ev.mouse.button = 1;
ev.mouse.x = (int)mid_x;
ev.mouse.y = (int)(cw->y + cw->titlebar_h / 2.0f);
ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
n_gui_process_event(gui, ev);
ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
n_gui_process_event(gui, ev);
expect_true("title bar minimise button set the minimised flag",
(cw->state & N_GUI_WIN_MINIMISED) != 0);
expect_eq_float("title bar minimise shrank the OS window to the title bar",
(float)al_get_display_height(cd), cw->titlebar_h);
n_gui_restore_window(gui, chrome_win);
expect_eq_float("restore grew the resizable OS window back",
(float)al_get_display_height(cd), full_h2);
}
/* the resize grip drives al_resize_display */
if (cd && cw) {
ALLEGRO_EVENT ev;
float grip = gui->style.grip_size + 2.0f;
memset(&ev, 0, sizeof(ev));
ev.mouse.display = cd;
ev.mouse.button = 1;
ev.mouse.x = (int)(cw->w - grip / 2.0f);
ev.mouse.y = (int)(cw->h - grip / 2.0f);
ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
n_gui_process_event(gui, ev);
expect_true("grip press starts a resize", (cw->state & N_GUI_WIN_RESIZING) != 0);
ev.type = ALLEGRO_EVENT_MOUSE_AXES;
ev.mouse.x += 40;
ev.mouse.y += 20;
n_gui_process_event(gui, ev);
expect_eq_float("grip resized the OS window (width)", (float)al_get_display_width(cd), 300.0f);
expect_eq_float("grip resized the OS window (height)", (float)al_get_display_height(cd), 200.0f);
ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
n_gui_process_event(gui, ev);
expect_true("resize ends on release", !(cw->state & N_GUI_WIN_RESIZING));
}
/* window constraints were pushed to the WM so it cannot shrink the
window below what the layout can render */
if (cd && cw) {
int cmin_w = 0, cmin_h = 0, cmax_w = 0, cmax_h = 0;
if (al_get_window_constraints(cd, &cmin_w, &cmin_h, &cmax_w, &cmax_h)) {
expect_eq_int("min width constraint pushed to the window manager", cmin_w, (int)(cw->min_w + 0.5f));
expect_eq_int("min height constraint pushed to the window manager", cmin_h, (int)(cw->min_h + 0.5f));
} else {
n_log(LOG_NOTICE, "window constraints unsupported on this platform, check skipped");
}
}
/* an icon can be given to the OS window (borrowed bitmap) */
{
ALLEGRO_BITMAP* icons[1];
icons[0] = shared_bmp;
int refused;
expect_eq_int("native icon accepted", n_gui_window_set_native_icons(gui, chrome_win, icons, 1), 0);
/* expected to fail, mute the error it logs on the way out */
set_log_level(LOG_NULL);
refused = n_gui_window_set_native_icons(gui, main_win, icons, 1);
set_log_level(log_level);
expect_eq_int("native icon refused for a pop-up", refused, -1);
}
/* HALT_DRAWING is acknowledged and suppresses drawing until RESUME */
if (cd) {
ALLEGRO_EVENT ev;
memset(&ev, 0, sizeof(ev));
ev.type = ALLEGRO_EVENT_DISPLAY_HALT_DRAWING;
ev.display.source = cd;
expect_eq_int("HALT_DRAWING is consumed", n_gui_process_event(gui, ev), 1);
expect_eq_int("halted window reports itself hidden", n_gui_window_native_is_hidden(gui, chrome_win), 1);
n_gui_draw_detached(gui); /* must not touch the halted display */
ev.type = ALLEGRO_EVENT_DISPLAY_RESUME_DRAWING;
expect_eq_int("RESUME_DRAWING is consumed", n_gui_process_event(gui, ev), 1);
expect_eq_int("resumed window is drawable again", n_gui_window_native_is_hidden(gui, chrome_win), 0);
}
/* its own chrome draws into its own display: the title bar is at the
top of the native window, which the default detach mode has no room
for at all. The pixels are counted from inside the frame, see
sample_chrome_titlebar for why a readback after the flip is not
portable. */
if (cd && cw) {
g_chrome_titlebar_lit = -1;
n_gui_window_set_content_draw_callback(gui, chrome_win, sample_chrome_titlebar, gui);
n_gui_draw_detached(gui);
n_gui_window_set_content_draw_callback(gui, chrome_win, NULL, NULL);
expect_true("own chrome drew a title bar into the native window", g_chrome_titlebar_lit > 10);
}
/* maximise drives the OS window through ALLEGRO_MAXIMIZED. Whether the
window manager honours it is its business, so only our bookkeeping is
asserted here, the granted geometry arrives as a DISPLAY_RESIZE. */
if (cw) {
n_gui_maximize_window(gui, chrome_win);
expect_true("maximise sets the maximised flag", (cw->state & N_GUI_WIN_MAXIMISED) != 0);
n_gui_maximize_window(gui, chrome_win);
expect_true("maximise toggles back off", !(cw->state & N_GUI_WIN_MAXIMISED));
}
/* attach restores the pop-up geometry AND the pop-up flags */
expect_eq_int("own-chrome attach succeeds", n_gui_window_attach(gui, chrome_win), 0);
{
N_GUI_WINDOW* w = n_gui_get_window(gui, chrome_win);
expect_eq_float("own-chrome pop-up w restored", w ? w->w : -1.0f, 260.0f);
expect_eq_float("own-chrome pop-up h restored", w ? w->h : -1.0f, 180.0f);
expect_true("own-chrome pop-up flags restored",
w && (w->flags & N_GUI_WIN_RESIZABLE) && !(w->flags & N_GUI_WIN_FRAMELESS));
}
n_gui_close_window(gui, chrome_win);
}
#ifdef HAVE_CJSON
/* LAYOUT PERSISTENCE: the detached state and the native geometry survive a
save/destroy/rebuild/load cycle. */
{
const char* path = "ex_gui_multiwin_layout.json";
expect_eq_int("re-detach for the save", n_gui_window_detach(gui, popup_win, N_GUI_DETACH_RESIZABLE), 0);
expect_eq_int("layout saved", n_gui_save_layout_json(gui, path), 0);
float want_w = 0.0f, want_h = 0.0f;
{
ALLEGRO_DISPLAY* d = n_gui_window_get_display(gui, popup_win);
want_w = (float)al_get_display_width(d);
want_h = (float)al_get_display_height(d);
}
n_gui_destroy_ctx(&gui);
al_set_target_backbuffer(main_display);
gui = n_gui_new_ctx(font);
expect_true("second context created", gui != NULL);
if (gui) {
n_gui_set_display(gui, main_display);
n_gui_set_display_size(gui, (float)MAIN_W, (float)MAIN_H);
n_gui_set_event_queue(gui, queue);
int w2_main = n_gui_add_window(gui, "Main Panel", 0.0f, 0.0f, 300.0f, 200.0f);
int w2_popup = n_gui_add_window(gui, "Popup", 0.0f, 0.0f, (float)POPUP_W, (float)POPUP_H);
expect_true("rebuilt windows", w2_main >= 0 && w2_popup >= 0);
expect_eq_int("layout loaded", n_gui_load_layout_json(gui, path), 0);
expect_eq_int("loaded layout re-created the native window", n_gui_window_is_detached(gui, w2_popup), 1);
ALLEGRO_DISPLAY* d2 = n_gui_window_get_display(gui, w2_popup);
expect_true("restored native display exists", d2 != NULL);
if (d2) {
expect_eq_float("restored native width", (float)al_get_display_width(d2), want_w);
expect_eq_float("restored native height", (float)al_get_display_height(d2), want_h);
}
/* the pop-up fallback geometry came back too, not the native size */
N_GUI_WINDOW* w = n_gui_get_window(gui, w2_popup);
expect_eq_float("restored pop-up fallback width", w ? w->saved_w : -1.0f, (float)POPUP_W);
expect_eq_float("restored pop-up fallback height", w ? w->saved_h : -1.0f, (float)POPUP_H);
}
remove(path);
}
#endif
/* the context owns any display still attached: this must not leak one */
if (gui) n_gui_destroy_ctx(&gui);
al_set_target_backbuffer(main_display);
al_destroy_bitmap(shared_bmp);
al_destroy_event_queue(queue);
al_destroy_font(font);
al_destroy_display(main_display);
if (failures) {
n_log(LOG_ERR, "ex_gui_multiwin: %d failure(s)", failures);
return 1;
}
n_log(LOG_NOTICE, "ex_gui_multiwin: all checks passed");
return 0;
}
Modules
- AABB3D
- ACCEPT_POOL
- ALLEGRO4
- ALLEGRO5
- ANIM_LIB
- ASTAR
- ATLAS_LIB
- AVRO
- CLOCK_SYNC
- COMMONS
- CONFIGFILE
- CYPHER_BASE64
- CYPHER_VIGENERE
- DEAD_RECKONING
- ENUMS
- EXCEPTIONS
- GAME_ENV
- GIT
- GUI
- HASH_TABLE
- ISOMETRIC_ENGINE
- LIST
- LOG
- LOGNODUP
- LZ4
- NETWORKING
- NETWORK_MSG
- N_COOKIES
- N_DIFF
- N_DIGEST
- N_DISK_CACHE
- N_DNS
- N_ECC
- N_ENTROPY
- N_FILES
- N_FLUIDS
- N_GUI_COLOR
- N_GUI_COLORWHEEL
- N_GUI_CURVE
- N_GUI_GRID
- N_GUI_POPUP
- N_HEX
- N_HTTP3
- N_HTTP_COOKIE
- N_HTTP_FORM
- N_HTTP_MIME
- N_HTTP_REQ
- N_HTTP_RESP
- N_HTTP_ROUTER
- N_HTTP_SERVER
- N_ICC
- N_JSON
- N_KAFKA
- N_LINALG
- N_MODULE
- N_OPENSSL_COMPAT
- N_PRETTY
- N_PTH
- N_QUERY
- N_RANDOM
- N_SHA256
- N_STR
- N_TIFF
- N_TIME
- N_USER
- N_X509
- N_ZIP
- PARTICLES
- PCRE
- PHYSICS
- SIGNALS
- STACK
- THREADS
- TRAJECTORY
- TREE
- ZLIB