Page:
n__allegro__4_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
n__allegro__4_8c
gullradriel edited this page 2026-10-29 20:54:40 +01:00
Table of Contents
- src/n_allegro_4.c
- Structures
- Types
- Functions
- Attributes
- Defines
- Detailed Description
- Types Documentation
- typedef fs_write_float_f
- typedef fs_sfload_f
- typedef fs_settings_setstr_f
- typedef fs_settings_setnum_f
- typedef fs_set_log_function_f
- typedef fs_player_set_loop_f
- typedef fs_player_int_f
- typedef fs_player_add_f
- typedef fs_new_synth_f
- typedef fs_new_settings_f
- typedef fs_new_player_f
- typedef fs_delete_f
- Functions Documentation
- function _a4_wrap
- function _a4_vertex
- function _a4_unlock
- function _a4_timer_proc
- function _a4_text
- function _a4_target
- function _a4_take_click
- function _a4_strndup
- function _a4_sprite
- function _a4_source
- function _a4_soundfont
- function _a4_set_blend
- function _a4_rotated
- function _a4_rgba
- function _a4_resolve
- function _a4_reserve
- function _a4_quad
- function _a4_prim_color
- function _a4_prim
- function _a4_present
- function _a4_pan
- function _a4_midi_stop_locked
- function _a4_midi_shutdown
- function _a4_midi_proc
- function _a4_midi_init
- function _a4_lock
- function _a4_item_rect
- function _a4_item_at
- function _a4_input_proc
- function _a4_input_handle
- function _a4_highlight
- function _a4_gain
- function _a4_font
- function _a4_flush
- function _a4_fluidsynth_sym
- function _a4_fluidsynth_open
- function _a4_draw_region
- function _a4_done
- function _a4_destroy_display
- function _a4_compute_scale
- function _a4_color_from_pixel
- function _a4_box
- function _a4_blend_copy
- function _a4_blend_alpha
- function _a4_align
- Attributes Documentation
- variable a4_trans_alpha
- variable a4_timers
- variable a4_timer_thread
- variable a4_timer_mutex
- variable a4_sound_ok
- variable a4_screen_dirty
- variable a4_scale
- variable a4_off_y
- variable a4_off_x
- variable a4_midi_volume
- variable a4_mickey_y
- variable a4_mickey_x
- variable a4_keybuf_start
- variable a4_keybuf_count
- variable a4_keybuf
- variable a4_input_thread
- variable a4_input_queue
- variable a4_input_mutex
- variable a4_inited
- variable a4_gl_mode
- variable a4_fs
- variable a4_draw_mode
- variable a4_display
- variable a4_digi_volume
- variable a4_cursor_shown
- variable a4_close_requested
- variable a4_close_cb
- variable a4_click_y
- variable a4_click_x
- variable a4_click_pending
- variable a4_batch_target
- variable a4_batch_n
- variable a4_batch_blend
- variable a4_batch
- variable a4_alpha_blender
- Macros Documentation
- define ALLEGRO_UNSTABLE
- define A4_TEXTPRINTF
- define A4_SYM
- define A4_SAMPLE_IDS
- define A4_MIDI_RATE
- define A4_MIDI_FRAG
- define A4_MAX_TIMERS
- define A4_MASK_COLOR
- define A4_KEYBUF_SIZE
- define A4_BLEND_FADE
- define A4_BLEND_COPY
- define A4_BLEND_ALPHA
- define A4_BATCH_MAX
- define A4P
- Source code
src/n_allegro_4.c
Allegro 4 compatibility layer on top of Allegro 5. More...
Structures
| Name | |
|---|---|
| struct | SAMPLE a sound sample |
| struct | MIDI a MIDI song |
| struct | LexMenu an INI driven menu |
| struct | LEX_ITEM |
| struct | FONT a font: the built in one, or a TTF font loaded with load_ttf_font() |
| struct | A4_TIMER |
| struct | A4_BMP |
Types
| Name | |
|---|---|
| typedef int(*)(void *, int, void *, int, int, void *, int, int) | fs_write_float_f |
| typedef int(*)(void *, const char *, int) | fs_sfload_f |
| typedef int(*)(void *, const char *, const char *) | fs_settings_setstr_f |
| typedef int(*)(void *, const char *, double) | fs_settings_setnum_f |
| typedef void ()(int, void *, void *) | fs_set_log_function_f |
| typedef int(*)(void *, int) | fs_player_set_loop_f |
| typedef int(*)(void *) | fs_player_int_f |
| typedef int(*)(void *, const char *) | fs_player_add_f |
| typedef void ()(void *) | fs_new_synth_f |
| typedef void ()(void) | fs_new_settings_f |
| typedef void ()(void *) | fs_new_player_f |
| typedef void(*)(void *) | fs_delete_f |
Functions
| Name | |
|---|---|
| BITMAP * | _a4_wrap(ALLEGRO_BITMAP * abmp) |
| void | _a4_vertex(ALLEGRO_VERTEX * v, float x, float y, ALLEGRO_COLOR c) |
| void | _a4_unlock(BITMAP * b) |
| void * | _a4_timer_proc(ALLEGRO_THREAD * thr, void * arg) |
| void | _a4_text(BITMAP * bmp, const FONT * f, const char * s, int x, int y, int color, int bg, int align) |
| int | _a4_target(BITMAP * b) |
| int | _a4_take_click(int * x, int * y) |
| char * | _a4_strndup(const char * s, size_t n) |
| void | _a4_sprite(BITMAP * bmp, BITMAP * sprite, int x, int y, int flags) |
| ALLEGRO_BITMAP * | _a4_source(BITMAP * b, int masked) |
| const char * | _a4_soundfont(void ) |
| void | _a4_set_blend(int blend) |
| void | _a4_rotated(BITMAP * bmp, BITMAP * sprite, float x, float y, float cx, float cy, fixed angle, fixed scale) |
| ALLEGRO_COLOR | _a4_rgba(int color, int alpha) |
| char * | _a4_resolve(const char * path) |
| ALLEGRO_VERTEX * | _a4_reserve(BITMAP * b, int blend, int n) |
| void | _a4_quad(BITMAP * b, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, int color) |
| ALLEGRO_COLOR | _a4_prim_color(int color) |
| ALLEGRO_COLOR | _a4_prim(int color, int * blend) |
| void | _a4_present(void ) |
| float | _a4_pan(int pan) |
| void | _a4_midi_stop_locked(void ) |
| void | _a4_midi_shutdown(void ) |
| void * | _a4_midi_proc(ALLEGRO_THREAD * thr, void * arg) |
| int | _a4_midi_init(void ) |
| ALLEGRO_LOCKED_REGION * | _a4_lock(BITMAP * b) |
| void | _a4_item_rect(const LexMenu * menu, const BITMAP * buf, int i, float * x, float * y, float * w, float * h) |
| int | _a4_item_at(const LexMenu * menu, const BITMAP * buf, int mx, int my) |
| void * | _a4_input_proc(ALLEGRO_THREAD * thr, void * arg) |
| void | _a4_input_handle(const ALLEGRO_EVENT * ev) |
| BITMAP * | _a4_highlight(BITMAP * src) |
| float | _a4_gain(int vol) |
| ALLEGRO_FONT * | _a4_font(const FONT * f) |
| void | _a4_flush(void ) |
| int | _a4_fluidsynth_sym(N_MODULE * lib, const char * name, void * fptr, size_t fsize) |
| N_MODULE * | _a4_fluidsynth_open(void ) |
| void | _a4_draw_region(BITMAP * source, BITMAP * dest, int masked, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh, int flags) |
| void | _a4_done(const BITMAP * b) |
| void | _a4_destroy_display(void ) |
| void | _a4_compute_scale(void ) |
| int | _a4_color_from_pixel(uint32_t p) |
| void | _a4_box(BITMAP * b, float l, float t, float r, float bt, int color) |
| void | _a4_blend_copy(void ) |
| void | _a4_blend_alpha(void ) |
| int | _a4_align(const char * v, int is_h) |
Attributes
| Name | |
|---|---|
| int | a4_trans_alpha |
| A4_TIMER[16] | a4_timers |
| ALLEGRO_THREAD * | a4_timer_thread |
| ALLEGRO_MUTEX * | a4_timer_mutex |
| int | a4_sound_ok |
| int | a4_screen_dirty |
| float | a4_scale |
| float | a4_off_y |
| float | a4_off_x |
| int | a4_midi_volume |
| int | a4_mickey_y |
| int | a4_mickey_x |
| int | a4_keybuf_start |
| int | a4_keybuf_count |
| int[64] | a4_keybuf |
| ALLEGRO_THREAD * | a4_input_thread |
| ALLEGRO_EVENT_QUEUE * | a4_input_queue |
| ALLEGRO_MUTEX * | a4_input_mutex |
| int | a4_inited |
| int | a4_gl_mode |
| struct @346262156126311217342151060122117020361320217156 | a4_fs |
| int | a4_draw_mode |
| ALLEGRO_DISPLAY * | a4_display |
| int | a4_digi_volume |
| int | a4_cursor_shown |
| volatile int | a4_close_requested |
| void(*)(void) | a4_close_cb |
| int | a4_click_y |
| int | a4_click_x |
| int | a4_click_pending |
| BITMAP * | a4_batch_target |
| int | a4_batch_n |
| int | a4_batch_blend |
| ALLEGRO_VERTEX[6144] | a4_batch |
| int | a4_alpha_blender |
Defines
Detailed Description
Allegro 4 compatibility layer on top of Allegro 5.
Author: Castagnier Mickael
Version: 1.0
Date: 08/10/2026
Types Documentation
typedef fs_write_float_f
typedef int(* fs_write_float_f) (void *, int, void *, int, int, void *, int, int);
typedef fs_sfload_f
typedef int(* fs_sfload_f) (void *, const char *, int);
typedef fs_settings_setstr_f
typedef int(* fs_settings_setstr_f) (void *, const char *, const char *);
typedef fs_settings_setnum_f
typedef int(* fs_settings_setnum_f) (void *, const char *, double);
typedef fs_set_log_function_f
typedef void *(* fs_set_log_function_f) (int, void *, void *);
typedef fs_player_set_loop_f
typedef int(* fs_player_set_loop_f) (void *, int);
typedef fs_player_int_f
typedef int(* fs_player_int_f) (void *);
typedef fs_player_add_f
typedef int(* fs_player_add_f) (void *, const char *);
typedef fs_new_synth_f
typedef void *(* fs_new_synth_f) (void *);
typedef fs_new_settings_f
typedef void *(* fs_new_settings_f) (void);
typedef fs_new_player_f
typedef void *(* fs_new_player_f) (void *);
typedef fs_delete_f
typedef void(* fs_delete_f) (void *);
Functions Documentation
function _a4_wrap
static BITMAP * _a4_wrap(
ALLEGRO_BITMAP * abmp
)
function _a4_vertex
static void _a4_vertex(
ALLEGRO_VERTEX * v,
float x,
float y,
ALLEGRO_COLOR c
)
function _a4_unlock
static void _a4_unlock(
BITMAP * b
)
function _a4_timer_proc
static void * _a4_timer_proc(
ALLEGRO_THREAD * thr,
void * arg
)
function _a4_text
static void _a4_text(
BITMAP * bmp,
const FONT * f,
const char * s,
int x,
int y,
int color,
int bg,
int align
)
function _a4_target
static int _a4_target(
BITMAP * b
)
function _a4_take_click
static int _a4_take_click(
int * x,
int * y
)
function _a4_strndup
static char * _a4_strndup(
const char * s,
size_t n
)
function _a4_sprite
static void _a4_sprite(
BITMAP * bmp,
BITMAP * sprite,
int x,
int y,
int flags
)
function _a4_source
static ALLEGRO_BITMAP * _a4_source(
BITMAP * b,
int masked
)
function _a4_soundfont
static const char * _a4_soundfont(
void
)
function _a4_set_blend
static void _a4_set_blend(
int blend
)
function _a4_rotated
static void _a4_rotated(
BITMAP * bmp,
BITMAP * sprite,
float x,
float y,
float cx,
float cy,
fixed angle,
fixed scale
)
function _a4_rgba
static ALLEGRO_COLOR _a4_rgba(
int color,
int alpha
)
function _a4_resolve
static char * _a4_resolve(
const char * path
)
function _a4_reserve
static ALLEGRO_VERTEX * _a4_reserve(
BITMAP * b,
int blend,
int n
)
function _a4_quad
static void _a4_quad(
BITMAP * b,
float x1,
float y1,
float x2,
float y2,
float x3,
float y3,
float x4,
float y4,
int color
)
function _a4_prim_color
static ALLEGRO_COLOR _a4_prim_color(
int color
)
function _a4_prim
static ALLEGRO_COLOR _a4_prim(
int color,
int * blend
)
function _a4_present
static void _a4_present(
void
)
function _a4_pan
static float _a4_pan(
int pan
)
function _a4_midi_stop_locked
static void _a4_midi_stop_locked(
void
)
function _a4_midi_shutdown
static void _a4_midi_shutdown(
void
)
function _a4_midi_proc
static void * _a4_midi_proc(
ALLEGRO_THREAD * thr,
void * arg
)
function _a4_midi_init
static int _a4_midi_init(
void
)
function _a4_lock
static ALLEGRO_LOCKED_REGION * _a4_lock(
BITMAP * b
)
function _a4_item_rect
static void _a4_item_rect(
const LexMenu * menu,
const BITMAP * buf,
int i,
float * x,
float * y,
float * w,
float * h
)
function _a4_item_at
static int _a4_item_at(
const LexMenu * menu,
const BITMAP * buf,
int mx,
int my
)
function _a4_input_proc
static void * _a4_input_proc(
ALLEGRO_THREAD * thr,
void * arg
)
function _a4_input_handle
static void _a4_input_handle(
const ALLEGRO_EVENT * ev
)
function _a4_highlight
static BITMAP * _a4_highlight(
BITMAP * src
)
function _a4_gain
static float _a4_gain(
int vol
)
function _a4_font
static ALLEGRO_FONT * _a4_font(
const FONT * f
)
function _a4_flush
static void _a4_flush(
void
)
function _a4_fluidsynth_sym
static int _a4_fluidsynth_sym(
N_MODULE * lib,
const char * name,
void * fptr,
size_t fsize
)
function _a4_fluidsynth_open
static N_MODULE * _a4_fluidsynth_open(
void
)
function _a4_draw_region
static void _a4_draw_region(
BITMAP * source,
BITMAP * dest,
int masked,
float sx,
float sy,
float sw,
float sh,
float dx,
float dy,
float dw,
float dh,
int flags
)
function _a4_done
static void _a4_done(
const BITMAP * b
)
function _a4_destroy_display
static void _a4_destroy_display(
void
)
function _a4_compute_scale
static void _a4_compute_scale(
void
)
function _a4_color_from_pixel
static int _a4_color_from_pixel(
uint32_t p
)
function _a4_box
static void _a4_box(
BITMAP * b,
float l,
float t,
float r,
float bt,
int color
)
function _a4_blend_copy
static void _a4_blend_copy(
void
)
function _a4_blend_alpha
static void _a4_blend_alpha(
void
)
function _a4_align
static int _a4_align(
const char * v,
int is_h
)
Attributes Documentation
variable a4_trans_alpha
static int a4_trans_alpha = 255;
variable a4_timers
static A4_TIMER[16] a4_timers;
variable a4_timer_thread
static ALLEGRO_THREAD * a4_timer_thread = NULL;
variable a4_timer_mutex
static ALLEGRO_MUTEX * a4_timer_mutex = NULL;
variable a4_sound_ok
static int a4_sound_ok = 0;
variable a4_screen_dirty
static int a4_screen_dirty = 0;
variable a4_scale
static float a4_scale = 1.0f;
variable a4_off_y
static float a4_off_y = 0.0f;
variable a4_off_x
static float a4_off_x = 0.0f;
variable a4_midi_volume
static int a4_midi_volume = 255;
variable a4_mickey_y
static int a4_mickey_y = 0;
variable a4_mickey_x
static int a4_mickey_x = 0;
variable a4_keybuf_start
static int a4_keybuf_start = 0;
variable a4_keybuf_count
static int a4_keybuf_count = 0;
variable a4_keybuf
static int[64] a4_keybuf;
variable a4_input_thread
static ALLEGRO_THREAD * a4_input_thread = NULL;
variable a4_input_queue
static ALLEGRO_EVENT_QUEUE * a4_input_queue = NULL;
variable a4_input_mutex
static ALLEGRO_MUTEX * a4_input_mutex = NULL;
variable a4_inited
static int a4_inited = 0;
variable a4_gl_mode
static int a4_gl_mode = 0;
variable a4_fs
static struct @346262156126311217342151060122117020361320217156 a4_fs;
variable a4_draw_mode
static int a4_draw_mode = 0;
variable a4_display
static ALLEGRO_DISPLAY * a4_display = NULL;
variable a4_digi_volume
static int a4_digi_volume = 255;
variable a4_cursor_shown
static int a4_cursor_shown = 0;
variable a4_close_requested
static volatile int a4_close_requested = 0;
variable a4_close_cb
static void(*)(void) a4_close_cb = NULL;
variable a4_click_y
static int a4_click_y = 0;
variable a4_click_x
static int a4_click_x = 0;
variable a4_click_pending
static int a4_click_pending = 0;
variable a4_batch_target
static BITMAP * a4_batch_target = NULL;
variable a4_batch_n
static int a4_batch_n = 0;
variable a4_batch_blend
static int a4_batch_blend = 0;
variable a4_batch
static ALLEGRO_VERTEX[6144] a4_batch;
variable a4_alpha_blender
static int a4_alpha_blender = 0;
Macros Documentation
define ALLEGRO_UNSTABLE
#define ALLEGRO_UNSTABLE
define A4_TEXTPRINTF
#define A4_TEXTPRINTF(
align
)
char buf[1024]; \
va_list ap; \
va_start(ap, format); \
vsnprintf(buf, sizeof(buf), format, ap); \
va_end(ap); \
_a4_text(bmp, f, buf, x, y, color, bg, align);
define A4_SYM
#define A4_SYM(
field,
name
)
ok = ok && _a4_fluidsynth_sym(a4_fs.lib, name, &a4_fs.field, sizeof(a4_fs.field))
define A4_SAMPLE_IDS
#define A4_SAMPLE_IDS 16
define A4_MIDI_RATE
#define A4_MIDI_RATE 44100
define A4_MIDI_FRAG
#define A4_MIDI_FRAG 1024
define A4_MAX_TIMERS
#define A4_MAX_TIMERS 16
define A4_MASK_COLOR
#define A4_MASK_COLOR 0x00FF00FF
define A4_KEYBUF_SIZE
#define A4_KEYBUF_SIZE 64
define A4_BLEND_FADE
#define A4_BLEND_FADE 2
define A4_BLEND_COPY
#define A4_BLEND_COPY 0
define A4_BLEND_ALPHA
#define A4_BLEND_ALPHA 1
define A4_BATCH_MAX
#define A4_BATCH_MAX 6144
define A4P
#define A4P(
b
)
((A4_BMP*)((b)->priv))
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.
*/
/* al_lock_sample_id() is part of Allegro 5's unstable API */
#ifndef ALLEGRO_UNSTABLE
#define ALLEGRO_UNSTABLE
#endif
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <math.h>
#include <allegro5/allegro.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include <allegro5/allegro_audio.h>
#include <allegro5/allegro_acodec.h>
#include "nilorea/n_common.h"
#include "nilorea/n_log.h"
#include "nilorea/n_module.h"
#include "nilorea/n_allegro_4.h"
#ifndef __windows__
#include <dirent.h>
#include <unistd.h>
#endif
/* The KEY_* values of the header are Allegro 5's key codes. */
_Static_assert(KEY_A == ALLEGRO_KEY_A, "KEY_A");
_Static_assert(KEY_0 == ALLEGRO_KEY_0, "KEY_0");
_Static_assert(KEY_F1 == ALLEGRO_KEY_F1, "KEY_F1");
_Static_assert(KEY_ESC == ALLEGRO_KEY_ESCAPE, "KEY_ESC");
_Static_assert(KEY_BACKSPACE == ALLEGRO_KEY_BACKSPACE, "KEY_BACKSPACE");
_Static_assert(KEY_TAB == ALLEGRO_KEY_TAB, "KEY_TAB");
_Static_assert(KEY_ENTER == ALLEGRO_KEY_ENTER, "KEY_ENTER");
_Static_assert(KEY_SPACE == ALLEGRO_KEY_SPACE, "KEY_SPACE");
_Static_assert(KEY_INSERT == ALLEGRO_KEY_INSERT, "KEY_INSERT");
_Static_assert(KEY_DEL == ALLEGRO_KEY_DELETE, "KEY_DEL");
_Static_assert(KEY_HOME == ALLEGRO_KEY_HOME, "KEY_HOME");
_Static_assert(KEY_END == ALLEGRO_KEY_END, "KEY_END");
_Static_assert(KEY_PGUP == ALLEGRO_KEY_PGUP, "KEY_PGUP");
_Static_assert(KEY_PGDN == ALLEGRO_KEY_PGDN, "KEY_PGDN");
_Static_assert(KEY_LEFT == ALLEGRO_KEY_LEFT, "KEY_LEFT");
_Static_assert(KEY_RIGHT == ALLEGRO_KEY_RIGHT, "KEY_RIGHT");
_Static_assert(KEY_UP == ALLEGRO_KEY_UP, "KEY_UP");
_Static_assert(KEY_DOWN == ALLEGRO_KEY_DOWN, "KEY_DOWN");
_Static_assert(KEY_ENTER_PAD == ALLEGRO_KEY_PAD_ENTER, "KEY_ENTER_PAD");
_Static_assert(KEY_LSHIFT == ALLEGRO_KEY_LSHIFT, "KEY_LSHIFT");
_Static_assert(KEY_ALTGR == ALLEGRO_KEY_ALTGR, "KEY_ALTGR");
_Static_assert(KEY_MAX == ALLEGRO_KEY_MAX, "KEY_MAX");
/* Mask colour of 32 bit Allegro 4 bitmaps. */
#define A4_MASK_COLOR 0x00FF00FF
/* Private state of a BITMAP. */
typedef struct A4_BMP {
/* the pixels */
ALLEGRO_BITMAP* bmp;
/* copy with the mask colour turned transparent, for masked draws */
ALLEGRO_BITMAP* masked;
/* TRUE when bmp changed since masked was built */
int masked_dirty;
/* non NULL while bmp is locked for direct pixel access */
ALLEGRO_LOCKED_REGION* lock;
/* TRUE for the screen bitmap */
int is_screen;
} A4_BMP;
/* Private state of a FONT. */
struct FONT {
ALLEGRO_FONT* f;
};
/* Private state of a SAMPLE: the instances it was last played on. */
#define A4_SAMPLE_IDS 16
struct SAMPLE {
ALLEGRO_SAMPLE* spl;
ALLEGRO_SAMPLE_ID ids[A4_SAMPLE_IDS];
int used[A4_SAMPLE_IDS];
int next;
};
/* Private state of a MIDI: the file to hand to fluidsynth. */
struct MIDI {
char* path;
};
#define A4P(b) ((A4_BMP*)((b)->priv))
char allegro_error[ALLEGRO_ERROR_SIZE] = "";
BITMAP* screen = NULL;
FONT* font = NULL;
volatile char key[KEY_MAX];
volatile int key_shifts = 0;
volatile int mouse_x = 0;
volatile int mouse_y = 0;
volatile int mouse_z = 0;
volatile int mouse_b = 0;
static int a4_inited = 0;
static ALLEGRO_DISPLAY* a4_display = NULL;
static int a4_gl_mode = 0;
static int a4_screen_dirty = 0;
static int a4_cursor_shown = 0;
static void (*a4_close_cb)(void) = NULL;
/* screen -> display mapping (letterboxed uniform scale) */
static float a4_scale = 1.0f;
static float a4_off_x = 0.0f;
static float a4_off_y = 0.0f;
/* drawing state */
static int a4_draw_mode = DRAW_MODE_SOLID;
static int a4_alpha_blender = 0;
static int a4_trans_alpha = 255;
/* input thread */
static ALLEGRO_EVENT_QUEUE* a4_input_queue = NULL;
static ALLEGRO_THREAD* a4_input_thread = NULL;
static ALLEGRO_MUTEX* a4_input_mutex = NULL;
#define A4_KEYBUF_SIZE 64
static int a4_keybuf[A4_KEYBUF_SIZE];
static int a4_keybuf_start = 0;
static int a4_keybuf_count = 0;
static int a4_mickey_x = 0;
static int a4_mickey_y = 0;
static volatile int a4_close_requested = 0;
/* the last left click, latched by the input thread so that the layer's own
* menus and boxes never miss a click shorter than a frame */
static int a4_click_pending = 0;
static int a4_click_x = 0;
static int a4_click_y = 0;
/* timer thread */
#define A4_MAX_TIMERS 16
typedef struct A4_TIMER {
void (*proc)(void);
double period;
double next;
} A4_TIMER;
static A4_TIMER a4_timers[A4_MAX_TIMERS];
static ALLEGRO_THREAD* a4_timer_thread = NULL;
static ALLEGRO_MUTEX* a4_timer_mutex = NULL;
/* sound */
static int a4_sound_ok = 0;
static int a4_digi_volume = 255;
static int a4_midi_volume = 255;
static void _a4_midi_shutdown(void);
static void _a4_present(void);
static void _a4_flush(void);
/* File names. */
/* Return a malloc'd copy of path with '\' turned into '/' and, when the
* name does not exist as written, each component matched case
* insensitively against the directory content. Windows file systems are
* case insensitive already, there only the separators are fixed. */
static char* _a4_resolve(const char* path) {
if (!path) return NULL;
char* p = strdup(path);
if (!p) return NULL;
#ifndef __windows__
for (char* c = p; *c; c++)
if (*c == '\\') *c = '/';
if (access(p, F_OK) == 0) return p;
size_t len = strlen(p);
char* out = malloc(len + 3);
if (!out) return p;
out[0] = '\0';
char* save = NULL;
char* work = strdup(p);
if (!work) {
free(out);
return p;
}
int absolute = (p[0] == '/');
if (absolute) strcpy(out, "/");
for (char* comp = strtok_r(work, "/", &save); comp; comp = strtok_r(NULL, "/", &save)) {
const char* dir = out[0] ? out : ".";
char candidate[4096];
snprintf(candidate, sizeof(candidate), "%s%s%s", out, (out[0] && out[strlen(out) - 1] != '/') ? "/" : "", comp);
const char* chosen = comp;
char found[256] = "";
if (access(candidate, F_OK) != 0) {
DIR* d = opendir(dir);
if (d) {
struct dirent* ent = NULL;
while ((ent = readdir(d)) != NULL) {
if (strcasecmp(ent->d_name, comp) == 0) {
snprintf(found, sizeof(found), "%s", ent->d_name);
break;
}
}
closedir(d);
}
if (found[0]) chosen = found;
}
size_t need = strlen(out) + strlen(chosen) + 2;
if (need > len + 3) {
char* bigger = realloc(out, need + 16);
if (!bigger) break;
out = bigger;
len = need + 13;
}
if (out[0] && out[strlen(out) - 1] != '/') strcat(out, "/");
strcat(out, chosen);
}
free(work);
free(p);
return out;
#else
for (char* c = p; *c; c++)
if (*c == '/') *c = '\\';
return p;
#endif
}
/* strndup() is not available everywhere (MinGW). */
static char* _a4_strndup(const char* s, size_t n) {
char* r = malloc(n + 1);
if (!r) return NULL;
memcpy(r, s, n);
r[n] = '\0';
return r;
}
/* Colours. */
uint32_t _a4_pixel_from_color(int color) {
uint32_t c = (uint32_t)color;
uint32_t a = c >> 24;
if (a == 0) a = 255;
return (a << 24) | (c & 0x00FFFFFFu);
}
static int _a4_color_from_pixel(uint32_t p) {
if ((p >> 24) == 255) return (int)(p & 0x00FFFFFFu);
return (int)p;
}
static ALLEGRO_COLOR _a4_rgba(int color, int alpha) {
uint32_t c = (uint32_t)color;
return al_map_rgba((unsigned char)((c >> 16) & 0xFF), (unsigned char)((c >> 8) & 0xFF), (unsigned char)(c & 0xFF), (unsigned char)alpha);
}
/* How a primitive is blended onto its target. */
#define A4_BLEND_COPY 0
#define A4_BLEND_ALPHA 1
#define A4_BLEND_FADE 2
/* The colour a primitive is drawn with, and in *blend how to blend it.
*
* Allegro 4's 32 bit blenders work in integers: an opacity n mixes with
* (n + 1) / 256 and the result is truncated, so drawing black at a tiny
* opacity over the whole buffer every frame (the classic fade / motion
* blur trick) always takes at least one step off each channel and trails
* fade out. GPU blending rounds to nearest instead, and 100 * (1 - 1/255)
* rounds back to 100: dark trails would never go away. So a translucent
* black is drawn with A4_BLEND_FADE, dest * (1 - n / 256) - 1 / 255,
* which lands on Allegro 4's dest - ceil(dest * (n + 1) / 256) (100 -> 99,
* 60 -> 59, 1 -> 0 for n = 1). The bias is a whole step because vertex
* colours are quantized to 8 bits, where half a step would round to 0. */
static ALLEGRO_COLOR _a4_prim(int color, int* blend) {
if (a4_draw_mode == DRAW_MODE_TRANS) {
int n = a4_alpha_blender ? (int)(((uint32_t)color) >> 24) : a4_trans_alpha;
float a = n > 0 ? (float)(n + 1) / 256.0f : 0.0f;
if (a > 1.0f) a = 1.0f;
if ((((uint32_t)color) & 0x00FFFFFFu) == 0 && n > 0 && n < 255) {
*blend = A4_BLEND_FADE;
float bias = 1.0f / 255.0f;
return al_map_rgba_f(bias, bias, bias, (float)n / 256.0f);
}
*blend = A4_BLEND_ALPHA;
uint32_t c = (uint32_t)color;
return al_map_rgba_f((float)((c >> 16) & 0xFF) / 255.0f, (float)((c >> 8) & 0xFF) / 255.0f, (float)(c & 0xFF) / 255.0f, a);
}
*blend = A4_BLEND_COPY;
return _a4_rgba(color, (int)(_a4_pixel_from_color(color) >> 24));
}
int makecol(int r, int g, int b) {
return ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF);
}
int makecol32(int r, int g, int b) {
return makecol(r, g, b);
}
int makeacol(int r, int g, int b, int a) {
return (int)(((uint32_t)(a & 0xFF) << 24) | (uint32_t)makecol(r, g, b));
}
int makeacol32(int r, int g, int b, int a) {
return makeacol(r, g, b, a);
}
int getr(int c) {
return (int)((((uint32_t)c) >> 16) & 0xFF);
}
int getg(int c) {
return (int)((((uint32_t)c) >> 8) & 0xFF);
}
int getb(int c) {
return (int)(((uint32_t)c) & 0xFF);
}
int geta(int c) {
return (int)(_a4_pixel_from_color(c) >> 24);
}
/* Bitmap state helpers. */
static void _a4_unlock(BITMAP* b) {
if (!b || !b->priv) return;
A4_BMP* p = A4P(b);
if (p->lock) {
al_unlock_bitmap(p->bmp);
p->lock = NULL;
}
}
static ALLEGRO_LOCKED_REGION* _a4_lock(BITMAP* b) {
A4_BMP* p = A4P(b);
if (!p->lock) {
_a4_flush();
p->lock = al_lock_bitmap(p->bmp, ALLEGRO_PIXEL_FORMAT_ARGB_8888, ALLEGRO_LOCK_READWRITE);
}
return p->lock;
}
/* Make b the drawing target. The caller fetched its sources first. */
static int _a4_target(BITMAP* b) {
if (!b || !b->priv) return FALSE;
A4_BMP* p = A4P(b);
_a4_flush();
_a4_unlock(b);
if (al_get_target_bitmap() != p->bmp) al_set_target_bitmap(p->bmp);
p->masked_dirty = 1;
if (p->is_screen) a4_screen_dirty = 1;
return TRUE;
}
/* After drawing on b: Allegro 4 showed screen drawing immediately. */
static void _a4_done(const BITMAP* b) {
if (b && b->priv && A4P(b)->is_screen && !a4_gl_mode) _a4_present();
}
/* The Allegro 5 bitmap to draw b from, masked or not. */
static ALLEGRO_BITMAP* _a4_source(BITMAP* b, int masked) {
if (!b || !b->priv) return NULL;
A4_BMP* p = A4P(b);
_a4_flush();
_a4_unlock(b);
if (!masked) return p->bmp;
if (!p->masked || p->masked_dirty) {
if (!p->masked) {
p->masked = al_create_bitmap(b->w, b->h);
if (!p->masked) return p->bmp;
}
ALLEGRO_STATE state;
al_store_state(&state, ALLEGRO_STATE_TARGET_BITMAP | ALLEGRO_STATE_BLENDER);
al_set_target_bitmap(p->masked);
al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
al_draw_bitmap(p->bmp, 0, 0, 0);
al_convert_mask_to_alpha(p->masked, al_map_rgb(255, 0, 255));
al_restore_state(&state);
p->masked_dirty = 0;
}
return p->masked;
}
static BITMAP* _a4_wrap(ALLEGRO_BITMAP* abmp) {
if (!abmp) return NULL;
BITMAP* b = NULL;
A4_BMP* p = NULL;
Malloc(b, BITMAP, 1);
Malloc(p, A4_BMP, 1);
if (!b || !p) {
Free(b);
Free(p);
al_destroy_bitmap(abmp);
return NULL;
}
p->bmp = abmp;
p->masked_dirty = 1;
b->w = al_get_bitmap_width(abmp);
b->h = al_get_bitmap_height(abmp);
b->line = NULL;
b->priv = p;
return b;
}
/* Blender for masked and alpha draws of non premultiplied pixels. */
static void _a4_blend_alpha(void) {
al_set_separate_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA, ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA);
}
/* Blender for plain copies. */
static void _a4_blend_copy(void) {
al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
}
static void _a4_set_blend(int blend) {
if (blend == A4_BLEND_FADE)
/* dest * (1 - src alpha) - src, alpha channel left alone */
al_set_separate_blender(ALLEGRO_DEST_MINUS_SRC, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA, ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ONE);
else if (blend == A4_BLEND_ALPHA)
_a4_blend_alpha();
else
_a4_blend_copy();
}
/* The colour of a primitive drawn right away, blender set accordingly. */
static ALLEGRO_COLOR _a4_prim_color(int color) {
int blend = A4_BLEND_COPY;
ALLEGRO_COLOR c = _a4_prim(color, &blend);
_a4_set_blend(blend);
return c;
}
/* Primitive batching. Allegro 4 programs draw thousands of lines and
* rectangles per frame, one call each, which Allegro 4 rasterized in
* software. Issuing one GPU draw per call is far too slow, so line(),
* rectfill(), rect(), triangle() and translucent putpixel() append
* triangles here, drawn in one al_draw_prim() call. The batch is flushed
* before anything else is drawn, before a bitmap is used as a source or
* locked, and before the screen is shown, so drawing order is unchanged. */
#define A4_BATCH_MAX 6144
static ALLEGRO_VERTEX a4_batch[A4_BATCH_MAX];
static int a4_batch_n = 0;
static BITMAP* a4_batch_target = NULL;
static int a4_batch_blend = A4_BLEND_COPY;
static void _a4_flush(void) {
if (a4_batch_n > 0 && a4_batch_target && a4_batch_target->priv) {
ALLEGRO_BITMAP* t = A4P(a4_batch_target)->bmp;
if (al_get_target_bitmap() != t) al_set_target_bitmap(t);
_a4_set_blend(a4_batch_blend);
al_draw_prim(a4_batch, NULL, NULL, 0, a4_batch_n, ALLEGRO_PRIM_TRIANGLE_LIST);
}
a4_batch_n = 0;
a4_batch_target = NULL;
}
/* Room for n vertices drawn on b with the given blend mode. */
static ALLEGRO_VERTEX* _a4_reserve(BITMAP* b, int blend, int n) {
if (a4_batch_target != b || a4_batch_blend != blend || a4_batch_n + n > A4_BATCH_MAX) {
_a4_flush();
_a4_unlock(b);
a4_batch_target = b;
a4_batch_blend = blend;
}
A4_BMP* p = A4P(b);
p->masked_dirty = 1;
if (p->is_screen) a4_screen_dirty = 1;
ALLEGRO_VERTEX* v = &a4_batch[a4_batch_n];
a4_batch_n += n;
return v;
}
static void _a4_vertex(ALLEGRO_VERTEX* v, float x, float y, ALLEGRO_COLOR c) {
v->x = x;
v->y = y;
v->z = 0.0f;
v->u = 0.0f;
v->v = 0.0f;
v->color = c;
}
/* A quad given by its four corners in order. */
static void _a4_quad(BITMAP* b, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, int color) {
int blend = A4_BLEND_COPY;
ALLEGRO_COLOR c = _a4_prim(color, &blend);
ALLEGRO_VERTEX* v = _a4_reserve(b, blend, 6);
_a4_vertex(&v[0], x1, y1, c);
_a4_vertex(&v[1], x2, y2, c);
_a4_vertex(&v[2], x3, y3, c);
_a4_vertex(&v[3], x1, y1, c);
_a4_vertex(&v[4], x3, y3, c);
_a4_vertex(&v[5], x4, y4, c);
}
/* An axis aligned box covering pixels [l, r) x [t, bt). */
static void _a4_box(BITMAP* b, float l, float t, float r, float bt, int color) {
_a4_quad(b, l, t, r, t, r, bt, l, bt, color);
}
/* System. */
static void _a4_input_handle(const ALLEGRO_EVENT* ev) {
switch (ev->type) {
case ALLEGRO_EVENT_KEY_DOWN:
if (ev->keyboard.keycode > 0 && ev->keyboard.keycode < KEY_MAX) key[ev->keyboard.keycode] = 1;
break;
case ALLEGRO_EVENT_KEY_UP:
if (ev->keyboard.keycode > 0 && ev->keyboard.keycode < KEY_MAX) key[ev->keyboard.keycode] = 0;
break;
case ALLEGRO_EVENT_KEY_CHAR: {
int ascii = (ev->keyboard.unichar > 0 && ev->keyboard.unichar < 256) ? ev->keyboard.unichar : 0;
int k = (ev->keyboard.keycode << 8) | ascii;
al_lock_mutex(a4_input_mutex);
if (a4_keybuf_count < A4_KEYBUF_SIZE) {
a4_keybuf[(a4_keybuf_start + a4_keybuf_count) % A4_KEYBUF_SIZE] = k;
a4_keybuf_count++;
}
al_unlock_mutex(a4_input_mutex);
break;
}
case ALLEGRO_EVENT_MOUSE_AXES:
case ALLEGRO_EVENT_MOUSE_WARPED:
mouse_x = (int)(((float)ev->mouse.x - a4_off_x) / a4_scale);
mouse_y = (int)(((float)ev->mouse.y - a4_off_y) / a4_scale);
mouse_z = ev->mouse.z;
al_lock_mutex(a4_input_mutex);
a4_mickey_x += (int)((float)ev->mouse.dx / a4_scale);
a4_mickey_y += (int)((float)ev->mouse.dy / a4_scale);
al_unlock_mutex(a4_input_mutex);
break;
case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
if (ev->mouse.button >= 1 && ev->mouse.button <= 3) mouse_b |= (1 << (ev->mouse.button - 1));
if (ev->mouse.button == 1) {
al_lock_mutex(a4_input_mutex);
a4_click_pending = 1;
a4_click_x = (int)(((float)ev->mouse.x - a4_off_x) / a4_scale);
a4_click_y = (int)(((float)ev->mouse.y - a4_off_y) / a4_scale);
al_unlock_mutex(a4_input_mutex);
}
break;
case ALLEGRO_EVENT_MOUSE_BUTTON_UP:
if (ev->mouse.button >= 1 && ev->mouse.button <= 3) mouse_b &= ~(1 << (ev->mouse.button - 1));
break;
case ALLEGRO_EVENT_DISPLAY_SWITCH_OUT:
memset((void*)key, 0, sizeof(key));
mouse_b = 0;
break;
case ALLEGRO_EVENT_DISPLAY_CLOSE:
a4_close_requested = 1;
if (a4_close_cb) a4_close_cb();
break;
default:
break;
}
}
static void* _a4_input_proc(ALLEGRO_THREAD* thr, void* arg) {
(void)arg;
while (!al_get_thread_should_stop(thr)) {
ALLEGRO_EVENT ev;
if (al_wait_for_event_timed(a4_input_queue, &ev, 0.05f)) _a4_input_handle(&ev);
}
return NULL;
}
static void* _a4_timer_proc(ALLEGRO_THREAD* thr, void* arg) {
(void)arg;
while (!al_get_thread_should_stop(thr)) {
double now = al_get_time();
double wait = 0.01;
void (*due[A4_MAX_TIMERS])(void);
int nb_due = 0;
al_lock_mutex(a4_timer_mutex);
for (int i = 0; i < A4_MAX_TIMERS; i++) {
if (!a4_timers[i].proc) continue;
if (now >= a4_timers[i].next) {
due[nb_due++] = a4_timers[i].proc;
a4_timers[i].next += a4_timers[i].period;
if (a4_timers[i].next < now) a4_timers[i].next = now + a4_timers[i].period;
}
double left = a4_timers[i].next - now;
if (left < wait) wait = left;
}
al_unlock_mutex(a4_timer_mutex);
for (int i = 0; i < nb_due; i++) due[i]();
if (wait > 0.0) al_rest(wait);
}
return NULL;
}
int allegro_init(void) {
if (a4_inited) return 0;
if (!al_init()) {
snprintf(allegro_error, sizeof(allegro_error), "al_init failed");
return -1;
}
al_init_primitives_addon();
al_init_image_addon();
al_init_font_addon();
al_init_ttf_addon();
memset((void*)key, 0, sizeof(key));
memset(a4_timers, 0, sizeof(a4_timers));
a4_input_mutex = al_create_mutex();
a4_timer_mutex = al_create_mutex();
a4_input_queue = al_create_event_queue();
if (!a4_input_mutex || !a4_timer_mutex || !a4_input_queue) {
snprintf(allegro_error, sizeof(allegro_error), "could not create the input queue");
return -1;
}
a4_input_thread = al_create_thread(_a4_input_proc, NULL);
if (a4_input_thread) al_start_thread(a4_input_thread);
a4_timer_thread = al_create_thread(_a4_timer_proc, NULL);
if (a4_timer_thread) al_start_thread(a4_timer_thread);
FONT* f = NULL;
Malloc(f, FONT, 1);
if (f) {
f->f = al_create_builtin_font();
font = f;
}
a4_inited = 1;
atexit(allegro_exit);
return 0;
}
static void _a4_destroy_display(void) {
if (screen) {
destroy_bitmap(screen);
screen = NULL;
}
if (a4_display) {
if (a4_input_queue) al_unregister_event_source(a4_input_queue, al_get_display_event_source(a4_display));
al_destroy_display(a4_display);
a4_display = NULL;
}
}
void allegro_exit(void) {
if (!a4_inited) return;
a4_inited = 0;
if (a4_timer_thread) {
al_destroy_thread(a4_timer_thread);
a4_timer_thread = NULL;
}
if (a4_input_thread) {
al_destroy_thread(a4_input_thread);
a4_input_thread = NULL;
}
remove_sound();
_a4_destroy_display();
if (font) {
if (font->f) al_destroy_font(font->f);
Free(font);
font = NULL;
}
if (a4_input_queue) {
al_destroy_event_queue(a4_input_queue);
a4_input_queue = NULL;
}
if (a4_input_mutex) {
al_destroy_mutex(a4_input_mutex);
a4_input_mutex = NULL;
}
if (a4_timer_mutex) {
al_destroy_mutex(a4_timer_mutex);
a4_timer_mutex = NULL;
}
}
void set_uformat(int type) {
(void)type;
}
int ustricmp(const char* s1, const char* s2) {
return strcasecmp(s1, s2);
}
void set_color_depth(int depth) {
if (depth != 32) n_log(LOG_DEBUG, "color depth %d requested, using 32", depth);
}
int get_color_depth(void) {
return 32;
}
static void _a4_compute_scale(void) {
if (!a4_display || !screen) return;
float dw = (float)al_get_display_width(a4_display);
float dh = (float)al_get_display_height(a4_display);
float sx = dw / (float)screen->w;
float sy = dh / (float)screen->h;
a4_scale = sx < sy ? sx : sy;
if (a4_scale <= 0.0f) a4_scale = 1.0f;
a4_off_x = (dw - (float)screen->w * a4_scale) / 2.0f;
a4_off_y = (dh - (float)screen->h * a4_scale) / 2.0f;
}
int set_gfx_mode(int card, int w, int h, int v_w, int v_h) {
(void)v_w;
(void)v_h;
if (!a4_inited && allegro_init() != 0) return -1;
_a4_destroy_display();
if (card == GFX_TEXT) return 0;
int flags = ALLEGRO_WINDOWED;
if (card == GFX_AUTODETECT_FULLSCREEN || card == GFX_DIRECTX || card == GFX_OPENGL_FULLSCREEN) flags = ALLEGRO_FULLSCREEN_WINDOW;
al_set_new_display_flags(flags);
a4_display = al_create_display(w, h);
if (!a4_display) {
snprintf(allegro_error, sizeof(allegro_error), "could not create a %dx%d display", w, h);
return -1;
}
a4_gl_mode = (card == GFX_OPENGL_WINDOWED || card == GFX_OPENGL_FULLSCREEN || card == GFX_OPENGL);
al_register_event_source(a4_input_queue, al_get_display_event_source(a4_display));
if (a4_cursor_shown)
al_show_mouse_cursor(a4_display);
else
al_hide_mouse_cursor(a4_display);
screen = create_bitmap(w, h);
if (!screen) {
snprintf(allegro_error, sizeof(allegro_error), "could not create the screen bitmap");
_a4_destroy_display();
return -1;
}
A4P(screen)->is_screen = 1;
_a4_compute_scale();
_a4_present();
return 0;
}
void set_window_title(const char* name) {
if (a4_display && name) al_set_window_title(a4_display, name);
}
int set_close_button_callback(void (*proc)(void)) {
a4_close_cb = proc;
return 0;
}
/* Draw screen on the display, letterboxed, and flip. */
static void _a4_present(void) {
if (!a4_display || !screen) return;
_a4_flush();
ALLEGRO_BITMAP* src = _a4_source(screen, FALSE);
al_set_target_backbuffer(a4_display);
al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
if (a4_off_x > 0.0f || a4_off_y > 0.0f) al_clear_to_color(al_map_rgb(0, 0, 0));
al_draw_scaled_bitmap(src, 0, 0, (float)screen->w, (float)screen->h, a4_off_x, a4_off_y, (float)screen->w * a4_scale, (float)screen->h * a4_scale, 0);
al_flip_display();
a4_screen_dirty = 0;
}
int install_allegro_gl(void) {
return 0;
}
void allegro_gl_set_allegro_mode(void) {
}
void allegro_gl_unset_allegro_mode(void) {
}
void allegro_gl_flip(void) {
if (a4_gl_mode || a4_screen_dirty) _a4_present();
}
/* Keyboard and mouse. */
int install_keyboard(void) {
if (!a4_inited && allegro_init() != 0) return -1;
if (!al_is_keyboard_installed() && !al_install_keyboard()) return -1;
al_register_event_source(a4_input_queue, al_get_keyboard_event_source());
return 0;
}
int poll_keyboard(void) {
return 0;
}
/* Take the latched left click, TRUE and its position if there was one. */
static int _a4_take_click(int* x, int* y) {
if (!a4_input_mutex) return FALSE;
al_lock_mutex(a4_input_mutex);
int r = a4_click_pending;
if (r) {
*x = a4_click_x;
*y = a4_click_y;
a4_click_pending = 0;
}
al_unlock_mutex(a4_input_mutex);
return r;
}
int keypressed(void) {
if (!a4_input_mutex) return FALSE;
al_lock_mutex(a4_input_mutex);
int r = a4_keybuf_count > 0;
al_unlock_mutex(a4_input_mutex);
return r;
}
int readkey(void) {
if (!a4_input_mutex) return 0;
for (;;) {
al_lock_mutex(a4_input_mutex);
if (a4_keybuf_count > 0) {
int k = a4_keybuf[a4_keybuf_start];
a4_keybuf_start = (a4_keybuf_start + 1) % A4_KEYBUF_SIZE;
a4_keybuf_count--;
al_unlock_mutex(a4_input_mutex);
return k;
}
al_unlock_mutex(a4_input_mutex);
al_rest(0.005);
}
}
void clear_keybuf(void) {
if (!a4_input_mutex) return;
al_lock_mutex(a4_input_mutex);
a4_keybuf_start = 0;
a4_keybuf_count = 0;
al_unlock_mutex(a4_input_mutex);
}
int install_mouse(void) {
if (!a4_inited && allegro_init() != 0) return -1;
if (!al_is_mouse_installed() && !al_install_mouse()) return -1;
al_register_event_source(a4_input_queue, al_get_mouse_event_source());
return 3;
}
int poll_mouse(void) {
return 0;
}
// The Allegro 4 signature takes a non const BITMAP*, kept for source compatibility.
// cppcheck-suppress constParameterPointer
// cppcheck-suppress constParameter
void show_mouse(BITMAP* bmp) {
a4_cursor_shown = (bmp != NULL);
if (!a4_display) return;
if (a4_cursor_shown)
al_show_mouse_cursor(a4_display);
else
al_hide_mouse_cursor(a4_display);
}
void get_mouse_mickeys(int* mickeyx, int* mickeyy) {
if (!a4_input_mutex) return;
al_lock_mutex(a4_input_mutex);
if (mickeyx) *mickeyx = a4_mickey_x;
if (mickeyy) *mickeyy = a4_mickey_y;
a4_mickey_x = 0;
a4_mickey_y = 0;
al_unlock_mutex(a4_input_mutex);
}
void position_mouse(int x, int y) {
if (!a4_display) return;
al_set_mouse_xy(a4_display, (int)((float)x * a4_scale + a4_off_x), (int)((float)y * a4_scale + a4_off_y));
mouse_x = x;
mouse_y = y;
}
/* Timers. */
int install_timer(void) {
return a4_inited ? 0 : allegro_init();
}
int install_int_ex(void (*proc)(void), long speed) {
if (!proc || speed <= 0) return -1;
if (!a4_inited && allegro_init() != 0) return -1;
double period = (double)speed / (double)TIMERS_PER_SECOND;
int ret = -1;
al_lock_mutex(a4_timer_mutex);
for (int i = 0; i < A4_MAX_TIMERS; i++) {
if (a4_timers[i].proc == proc) {
a4_timers[i].period = period;
ret = 0;
break;
}
}
for (int i = 0; ret != 0 && i < A4_MAX_TIMERS; i++) {
if (!a4_timers[i].proc) {
a4_timers[i].proc = proc;
a4_timers[i].period = period;
a4_timers[i].next = al_get_time() + period;
ret = 0;
}
}
al_unlock_mutex(a4_timer_mutex);
return ret;
}
int install_int(void (*proc)(void), int speed) {
return install_int_ex(proc, MSEC_TO_TIMER(speed));
}
// cppcheck-suppress constParameter
void remove_int(void (*proc)(void)) {
if (!a4_timer_mutex) return;
al_lock_mutex(a4_timer_mutex);
for (int i = 0; i < A4_MAX_TIMERS; i++)
if (a4_timers[i].proc == proc) a4_timers[i].proc = NULL;
al_unlock_mutex(a4_timer_mutex);
}
void rest(unsigned int ms) {
al_rest((double)ms / 1000.0);
}
/* Bitmaps. */
BITMAP* create_bitmap(int width, int height) {
if (width <= 0 || height <= 0) return NULL;
al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ANY_32_WITH_ALPHA);
ALLEGRO_BITMAP* abmp = al_create_bitmap(width, height);
if (!abmp) {
snprintf(allegro_error, sizeof(allegro_error), "could not create a %dx%d bitmap", width, height);
return NULL;
}
ALLEGRO_STATE state;
al_store_state(&state, ALLEGRO_STATE_TARGET_BITMAP);
al_set_target_bitmap(abmp);
al_clear_to_color(al_map_rgb(0, 0, 0));
al_restore_state(&state);
return _a4_wrap(abmp);
}
BITMAP* create_video_bitmap(int width, int height) {
return create_bitmap(width, height);
}
BITMAP* create_bitmap_ex(int depth, int width, int height) {
(void)depth;
return create_bitmap(width, height);
}
void destroy_bitmap(BITMAP* bmp) {
if (!bmp) return;
A4_BMP* p = A4P(bmp);
if (a4_batch_target == bmp) {
a4_batch_n = 0;
a4_batch_target = NULL;
}
if (p) {
_a4_unlock(bmp);
if (p->masked) al_destroy_bitmap(p->masked);
if (p->bmp) al_destroy_bitmap(p->bmp);
Free(p);
}
if (bmp == screen) screen = NULL;
Free(bmp);
}
BITMAP* load_bitmap(const char* filename, RGB* pal) {
(void)pal;
char* path = _a4_resolve(filename);
if (!path) return NULL;
al_set_new_bitmap_format(ALLEGRO_PIXEL_FORMAT_ANY_32_WITH_ALPHA);
ALLEGRO_BITMAP* abmp = al_load_bitmap_flags(path, ALLEGRO_NO_PREMULTIPLIED_ALPHA);
if (!abmp) {
snprintf(allegro_error, sizeof(allegro_error), "could not load %s", path);
n_log(LOG_ERR, "could not load bitmap %s", path);
}
free(path);
return _a4_wrap(abmp);
}
int save_bitmap(const char* filename, BITMAP* bmp, const RGB* pal) {
(void)pal;
if (!bmp || !filename) return -1;
_a4_unlock(bmp);
return al_save_bitmap(filename, A4P(bmp)->bmp) ? 0 : -1;
}
int bitmap_color_depth(BITMAP* bmp) {
(void)bmp;
return 32;
}
int bitmap_mask_color(BITMAP* bmp) {
(void)bmp;
return A4_MASK_COLOR;
}
int is_memory_bitmap(BITMAP* bmp) {
(void)bmp;
return FALSE;
}
void clear_to_color(BITMAP* bmp, int color) {
if (!_a4_target(bmp)) return;
al_clear_to_color(_a4_rgba(color, (int)(_a4_pixel_from_color(color) >> 24)));
_a4_done(bmp);
}
void clear(BITMAP* bmp) {
clear_to_color(bmp, 0);
}
void clear_bitmap(BITMAP* bmp) {
clear_to_color(bmp, 0);
}
/* Draw a region of source onto dest, through a temporary copy when they
* are the same bitmap (Allegro 4 allowed overlapping blits). */
static void _a4_draw_region(BITMAP* source, BITMAP* dest, int masked, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh, int flags) {
if (!source || !dest || sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) return;
ALLEGRO_BITMAP* src = _a4_source(source, masked);
ALLEGRO_BITMAP* tmp = NULL;
if (source == dest) {
tmp = al_create_bitmap((int)sw, (int)sh);
if (!tmp) return;
al_set_target_bitmap(tmp);
al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
al_draw_bitmap_region(src, sx, sy, sw, sh, 0, 0, 0);
src = tmp;
sx = 0;
sy = 0;
}
if (_a4_target(dest)) {
if (masked)
_a4_blend_alpha();
else
_a4_blend_copy();
al_draw_scaled_bitmap(src, sx, sy, sw, sh, dx, dy, dw, dh, flags);
}
if (tmp) al_destroy_bitmap(tmp);
_a4_done(dest);
}
void blit(BITMAP* source, BITMAP* dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height) {
_a4_draw_region(source, dest, FALSE, (float)source_x, (float)source_y, (float)width, (float)height, (float)dest_x, (float)dest_y, (float)width, (float)height, 0);
}
void masked_blit(BITMAP* source, BITMAP* dest, int source_x, int source_y, int dest_x, int dest_y, int width, int height) {
_a4_draw_region(source, dest, TRUE, (float)source_x, (float)source_y, (float)width, (float)height, (float)dest_x, (float)dest_y, (float)width, (float)height, 0);
}
void stretch_blit(BITMAP* source, BITMAP* dest, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int dest_width, int dest_height) {
_a4_draw_region(source, dest, FALSE, (float)source_x, (float)source_y, (float)source_width, (float)source_height, (float)dest_x, (float)dest_y, (float)dest_width, (float)dest_height, 0);
}
void masked_stretch_blit(BITMAP* source, BITMAP* dest, int source_x, int source_y, int source_w, int source_h, int dest_x, int dest_y, int dest_w, int dest_h) {
_a4_draw_region(source, dest, TRUE, (float)source_x, (float)source_y, (float)source_w, (float)source_h, (float)dest_x, (float)dest_y, (float)dest_w, (float)dest_h, 0);
}
static void _a4_sprite(BITMAP* bmp, BITMAP* sprite, int x, int y, int flags) {
if (!sprite) return;
_a4_draw_region(sprite, bmp, TRUE, 0, 0, (float)sprite->w, (float)sprite->h, (float)x, (float)y, (float)sprite->w, (float)sprite->h, flags);
}
void draw_sprite(BITMAP* bmp, BITMAP* sprite, int x, int y) {
_a4_sprite(bmp, sprite, x, y, 0);
}
void draw_sprite_h_flip(BITMAP* bmp, BITMAP* sprite, int x, int y) {
_a4_sprite(bmp, sprite, x, y, ALLEGRO_FLIP_HORIZONTAL);
}
void draw_sprite_v_flip(BITMAP* bmp, BITMAP* sprite, int x, int y) {
_a4_sprite(bmp, sprite, x, y, ALLEGRO_FLIP_VERTICAL);
}
void draw_sprite_vh_flip(BITMAP* bmp, BITMAP* sprite, int x, int y) {
_a4_sprite(bmp, sprite, x, y, ALLEGRO_FLIP_HORIZONTAL | ALLEGRO_FLIP_VERTICAL);
}
void stretch_sprite(BITMAP* bmp, BITMAP* sprite, int x, int y, int w, int h) {
if (!sprite) return;
_a4_draw_region(sprite, bmp, TRUE, 0, 0, (float)sprite->w, (float)sprite->h, (float)x, (float)y, (float)w, (float)h, 0);
}
void draw_trans_sprite(BITMAP* bmp, BITMAP* sprite, int x, int y) {
if (!sprite || !bmp || sprite == bmp) return;
/* the alpha blender uses the sprite's own alpha channel, the trans
* blender a constant opacity over the masked sprite */
ALLEGRO_BITMAP* src = _a4_source(sprite, !a4_alpha_blender);
if (!_a4_target(bmp)) return;
_a4_blend_alpha();
if (a4_alpha_blender)
al_draw_bitmap(src, (float)x, (float)y, 0);
else
al_draw_tinted_bitmap(src, al_map_rgba_f(1, 1, 1, (float)a4_trans_alpha / 255.0f), (float)x, (float)y, 0);
_a4_done(bmp);
}
static void _a4_rotated(BITMAP* bmp, BITMAP* sprite, float x, float y, float cx, float cy, fixed angle, fixed scale) {
if (!sprite || !bmp || sprite == bmp) return;
ALLEGRO_BITMAP* src = _a4_source(sprite, TRUE);
if (!_a4_target(bmp)) return;
_a4_blend_alpha();
float radians = (float)(fixtof(angle) * ALLEGRO_PI / 128.0);
float s = (float)fixtof(scale);
al_draw_scaled_rotated_bitmap(src, cx, cy, x, y, s, s, radians, 0);
_a4_done(bmp);
}
void rotate_sprite(BITMAP* bmp, BITMAP* sprite, int x, int y, fixed angle) {
if (!sprite) return;
_a4_rotated(bmp, sprite, (float)x + (float)sprite->w / 2.0f, (float)y + (float)sprite->h / 2.0f, (float)sprite->w / 2.0f, (float)sprite->h / 2.0f, angle, itofix(1));
}
void rotate_scaled_sprite(BITMAP* bmp, BITMAP* sprite, int x, int y, fixed angle, fixed scale) {
if (!sprite) return;
float s = (float)fixtof(scale);
_a4_rotated(bmp, sprite, (float)x + (float)sprite->w * s / 2.0f, (float)y + (float)sprite->h * s / 2.0f, (float)sprite->w / 2.0f, (float)sprite->h / 2.0f, angle, scale);
}
void pivot_sprite(BITMAP* bmp, BITMAP* sprite, int x, int y, int cx, int cy, fixed angle) {
_a4_rotated(bmp, sprite, (float)x, (float)y, (float)cx, (float)cy, angle, itofix(1));
}
void pivot_scaled_sprite(BITMAP* bmp, BITMAP* sprite, int x, int y, int cx, int cy, fixed angle, fixed scale) {
_a4_rotated(bmp, sprite, (float)x, (float)y, (float)cx, (float)cy, angle, scale);
}
/* Primitives. Allegro 4 coordinates are inclusive pixel indexes. */
void drawing_mode(int mode, BITMAP* pattern, int x_anchor, int y_anchor) {
(void)pattern;
(void)x_anchor;
(void)y_anchor;
a4_draw_mode = (mode == DRAW_MODE_TRANS) ? DRAW_MODE_TRANS : DRAW_MODE_SOLID;
}
void solid_mode(void) {
a4_draw_mode = DRAW_MODE_SOLID;
}
void set_trans_blender(int r, int g, int b, int a) {
(void)r;
(void)g;
(void)b;
a4_alpha_blender = 0;
a4_trans_alpha = a < 0 ? 0 : (a > 255 ? 255 : a);
}
void set_alpha_blender(void) {
a4_alpha_blender = 1;
}
void putpixel(BITMAP* bmp, int x, int y, int color) {
if (!bmp || x < 0 || y < 0 || x >= bmp->w || y >= bmp->h) return;
if (a4_draw_mode == DRAW_MODE_TRANS) {
if (!bmp->priv) return;
_a4_box(bmp, (float)x, (float)y, (float)x + 1.0f, (float)y + 1.0f, color);
_a4_done(bmp);
return;
}
ALLEGRO_LOCKED_REGION* lr = _a4_lock(bmp);
if (!lr) return;
uint32_t* row = (uint32_t*)((char*)lr->data + (ptrdiff_t)y * lr->pitch);
row[x] = _a4_pixel_from_color(color);
A4P(bmp)->masked_dirty = 1;
if (A4P(bmp)->is_screen) a4_screen_dirty = 1;
}
int getpixel(BITMAP* bmp, int x, int y) {
if (!bmp || x < 0 || y < 0 || x >= bmp->w || y >= bmp->h) return -1;
const ALLEGRO_LOCKED_REGION* lr = _a4_lock(bmp);
if (!lr) return -1;
const uint32_t* row = (const uint32_t*)((const char*)lr->data + (ptrdiff_t)y * lr->pitch);
return _a4_color_from_pixel(row[x]);
}
void line(BITMAP* bmp, int x1, int y1, int x2, int y2, int color) {
if (!bmp || !bmp->priv) return;
/* a one pixel wide quad through the pixel centres, reaching half a
* pixel past both ends: both end points are drawn, as in Allegro 4 */
float ax = (float)x1 + 0.5f, ay = (float)y1 + 0.5f;
float bx = (float)x2 + 0.5f, by = (float)y2 + 0.5f;
float dx = bx - ax, dy = by - ay;
float len = sqrtf(dx * dx + dy * dy);
if (len < 0.5f) {
_a4_box(bmp, (float)x1, (float)y1, (float)x1 + 1.0f, (float)y1 + 1.0f, color);
} else {
float ux = dx / len * 0.5f, uy = dy / len * 0.5f;
ax -= ux;
ay -= uy;
bx += ux;
by += uy;
_a4_quad(bmp, ax - uy, ay + ux, bx - uy, by + ux, bx + uy, by - ux, ax + uy, ay - ux, color);
}
_a4_done(bmp);
}
void hline(BITMAP* bmp, int x1, int y, int x2, int color) {
rectfill(bmp, x1, y, x2, y, color);
}
void vline(BITMAP* bmp, int x, int y1, int y2, int color) {
rectfill(bmp, x, y1, x, y2, color);
}
void rect(BITMAP* bmp, int x1, int y1, int x2, int y2, int color) {
if (!bmp || !bmp->priv) return;
if (x1 > x2) {
int t = x1;
x1 = x2;
x2 = t;
}
if (y1 > y2) {
int t = y1;
y1 = y2;
y2 = t;
}
_a4_box(bmp, (float)x1, (float)y1, (float)x2 + 1.0f, (float)y1 + 1.0f, color);
if (y2 > y1) _a4_box(bmp, (float)x1, (float)y2, (float)x2 + 1.0f, (float)y2 + 1.0f, color);
if (y2 - y1 > 1) {
_a4_box(bmp, (float)x1, (float)y1 + 1.0f, (float)x1 + 1.0f, (float)y2, color);
if (x2 > x1) _a4_box(bmp, (float)x2, (float)y1 + 1.0f, (float)x2 + 1.0f, (float)y2, color);
}
_a4_done(bmp);
}
void rectfill(BITMAP* bmp, int x1, int y1, int x2, int y2, int color) {
if (!bmp || !bmp->priv) return;
if (x1 > x2) {
int t = x1;
x1 = x2;
x2 = t;
}
if (y1 > y2) {
int t = y1;
y1 = y2;
y2 = t;
}
_a4_box(bmp, (float)x1, (float)y1, (float)x2 + 1.0f, (float)y2 + 1.0f, color);
_a4_done(bmp);
}
void circle(BITMAP* bmp, int x, int y, int radius, int color) {
if (!_a4_target(bmp)) return;
al_draw_circle((float)x + 0.5f, (float)y + 0.5f, (float)radius, _a4_prim_color(color), 1.0f);
_a4_done(bmp);
}
void circlefill(BITMAP* bmp, int x, int y, int radius, int color) {
if (!_a4_target(bmp)) return;
al_draw_filled_circle((float)x + 0.5f, (float)y + 0.5f, (float)radius + 0.5f, _a4_prim_color(color));
_a4_done(bmp);
}
void ellipse(BITMAP* bmp, int x, int y, int rx, int ry, int color) {
if (!_a4_target(bmp)) return;
al_draw_ellipse((float)x + 0.5f, (float)y + 0.5f, (float)rx, (float)ry, _a4_prim_color(color), 1.0f);
_a4_done(bmp);
}
void ellipsefill(BITMAP* bmp, int x, int y, int rx, int ry, int color) {
if (!_a4_target(bmp)) return;
al_draw_filled_ellipse((float)x + 0.5f, (float)y + 0.5f, (float)rx + 0.5f, (float)ry + 0.5f, _a4_prim_color(color));
_a4_done(bmp);
}
void triangle(BITMAP* bmp, int x1, int y1, int x2, int y2, int x3, int y3, int color) {
if (!bmp || !bmp->priv) return;
int blend = A4_BLEND_COPY;
ALLEGRO_COLOR c = _a4_prim(color, &blend);
ALLEGRO_VERTEX* v = _a4_reserve(bmp, blend, 3);
_a4_vertex(&v[0], (float)x1 + 0.5f, (float)y1 + 0.5f, c);
_a4_vertex(&v[1], (float)x2 + 0.5f, (float)y2 + 0.5f, c);
_a4_vertex(&v[2], (float)x3 + 0.5f, (float)y3 + 0.5f, c);
_a4_done(bmp);
}
/* Direct pixel access. */
void bmp_select(BITMAP* bmp) {
(void)bmp;
}
uintptr_t bmp_write_line(BITMAP* bmp, int y) {
if (!bmp || y < 0 || y >= bmp->h) return 0;
ALLEGRO_LOCKED_REGION* lr = _a4_lock(bmp);
if (!lr) return 0;
A4P(bmp)->masked_dirty = 1;
if (A4P(bmp)->is_screen) a4_screen_dirty = 1;
return (uintptr_t)((char*)lr->data + (ptrdiff_t)y * lr->pitch);
}
uintptr_t bmp_read_line(BITMAP* bmp, int y) {
if (!bmp || y < 0 || y >= bmp->h) return 0;
ALLEGRO_LOCKED_REGION* lr = _a4_lock(bmp);
if (!lr) return 0;
return (uintptr_t)((char*)lr->data + (ptrdiff_t)y * lr->pitch);
}
void bmp_unwrite_line(BITMAP* bmp) {
(void)bmp;
}
/* Text. */
static ALLEGRO_FONT* _a4_font(const FONT* f) {
if (f && f->f) return f->f;
return (font && font->f) ? font->f : NULL;
}
int text_length(const FONT* f, const char* str) {
ALLEGRO_FONT* af = _a4_font(f);
return (af && str) ? al_get_text_width(af, str) : 0;
}
int text_height(const FONT* f) {
ALLEGRO_FONT* af = _a4_font(f);
return af ? al_get_font_line_height(af) : 0;
}
/* align: 0 left, 1 centre, 2 right */
static void _a4_text(BITMAP* bmp, const FONT* f, const char* s, int x, int y, int color, int bg, int align) {
ALLEGRO_FONT* af = _a4_font(f);
if (!af || !s) return;
int w = al_get_text_width(af, s);
int left = align == 1 ? x - w / 2 : (align == 2 ? x - w : x);
if (bg != -1) {
int mode = a4_draw_mode;
a4_draw_mode = DRAW_MODE_SOLID;
rectfill(bmp, left, y, left + w - 1, y + al_get_font_line_height(af) - 1, bg);
a4_draw_mode = mode;
}
if (!_a4_target(bmp)) return;
/* font glyphs are premultiplied */
al_set_separate_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA, ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA);
ALLEGRO_COLOR c = (color == -1) ? al_map_rgb(255, 255, 255) : _a4_rgba(color, 255);
al_draw_text(af, c, (float)left, (float)y, ALLEGRO_ALIGN_LEFT, s);
_a4_done(bmp);
}
void textout_ex(BITMAP* bmp, const FONT* f, const char* s, int x, int y, int color, int bg) {
_a4_text(bmp, f, s, x, y, color, bg, 0);
}
void textout_centre_ex(BITMAP* bmp, const FONT* f, const char* s, int x, int y, int color, int bg) {
_a4_text(bmp, f, s, x, y, color, bg, 1);
}
void textout_right_ex(BITMAP* bmp, const FONT* f, const char* s, int x, int y, int color, int bg) {
_a4_text(bmp, f, s, x, y, color, bg, 2);
}
/* the format arguments come straight from Allegro 4 callers */
#define A4_TEXTPRINTF(align) \
char buf[1024]; \
va_list ap; \
va_start(ap, format); \
vsnprintf(buf, sizeof(buf), format, ap); \
va_end(ap); \
_a4_text(bmp, f, buf, x, y, color, bg, align);
void textprintf_ex(BITMAP* bmp, const FONT* f, int x, int y, int color, int bg, const char* format, ...) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
A4_TEXTPRINTF(0)
#pragma GCC diagnostic pop
}
void textprintf_centre_ex(BITMAP* bmp, const FONT* f, int x, int y, int color, int bg, const char* format, ...) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
A4_TEXTPRINTF(1)
#pragma GCC diagnostic pop
}
void textprintf_right_ex(BITMAP* bmp, const FONT* f, int x, int y, int color, int bg, const char* format, ...){
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
A4_TEXTPRINTF(2)
#pragma GCC diagnostic pop
}
FONT* load_ttf_font(const char* filename, int points, int smooth) {
char* path = _a4_resolve(filename);
if (!path) return NULL;
ALLEGRO_FONT* af = al_load_ttf_font(path, points, smooth == ALLEGTTF_NOSMOOTH ? ALLEGRO_TTF_MONOCHROME : 0);
if (!af) {
snprintf(allegro_error, sizeof(allegro_error), "could not load font %s", path);
n_log(LOG_ERR, "could not load font %s", path);
free(path);
return NULL;
}
free(path);
FONT* f = NULL;
Malloc(f, FONT, 1);
if (!f) {
al_destroy_font(af);
return NULL;
}
f->f = af;
return f;
}
FONT* load_ttf_font_mem(const char* filename, int points, int smooth) {
return load_ttf_font(filename, points, smooth);
}
void destroy_font(FONT* f) {
if (!f || f == font) return;
if (f->f) al_destroy_font(f->f);
Free(f);
}
/* Message box. */
void allegro_message(const char* format, ...) {
char msg[4096];
va_list ap;
va_start(ap, format);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
vsnprintf(msg, sizeof(msg), format, ap);
#pragma GCC diagnostic pop
va_end(ap);
fprintf(stderr, "%s\n", msg);
if (!a4_display || !screen || !font) return;
/* split into lines, wrapping long ones */
ALLEGRO_FONT* af = font->f;
int lh = al_get_font_line_height(af) + 4;
int maxw = screen->w - 60;
char* lines[128];
int nb = 0;
for (char* p = msg; *p && nb < 128;) {
char* end = strchr(p, '\n');
size_t n = end ? (size_t)(end - p) : strlen(p);
while (n > 0 && nb < 128) {
size_t take = n;
char tmp[1024];
for (;;) {
size_t c = take < sizeof(tmp) - 1 ? take : sizeof(tmp) - 1;
memcpy(tmp, p, c);
tmp[c] = '\0';
if (al_get_text_width(af, tmp) <= maxw || take <= 1) break;
take--;
}
lines[nb] = _a4_strndup(p, take);
if (!lines[nb]) break;
nb++;
p += take;
n -= take;
}
if (!end) break;
if (n == 0 && end == p) {
if (nb < 128) lines[nb++] = _a4_strndup("", 0);
}
p = end + 1;
}
int bw = 0;
for (int i = 0; i < nb; i++) {
int w = lines[i] ? al_get_text_width(af, lines[i]) : 0;
if (w > bw) bw = w;
}
const char* hint = "Press a key or click to continue";
if (al_get_text_width(af, hint) > bw) bw = al_get_text_width(af, hint);
bw += 40;
int bh = (nb + 2) * lh + 30;
int bx = (screen->w - bw) / 2;
int by = (screen->h - bh) / 2;
BITMAP* saved = create_bitmap(screen->w, screen->h);
if (saved) blit(screen, saved, 0, 0, 0, 0, screen->w, screen->h);
int mode = a4_draw_mode;
a4_draw_mode = DRAW_MODE_SOLID;
int gl = a4_gl_mode;
a4_gl_mode = 1;
rectfill(screen, bx, by, bx + bw - 1, by + bh - 1, makecol(32, 32, 48));
rect(screen, bx, by, bx + bw - 1, by + bh - 1, makecol(200, 200, 220));
for (int i = 0; i < nb; i++) {
if (lines[i]) textout_ex(screen, font, lines[i], bx + 20, by + 15 + i * lh, makecol(255, 255, 255), -1);
}
textout_centre_ex(screen, font, hint, bx + bw / 2, by + bh - 15 - lh, makecol(160, 160, 190), -1);
_a4_present();
/* wait for everything to be released, then for a new key or click */
while ((mouse_b || keypressed()) && !a4_close_requested) {
clear_keybuf();
al_rest(0.01);
}
int mx = 0, my = 0;
_a4_take_click(&mx, &my);
while (!keypressed() && !mouse_b && !_a4_take_click(&mx, &my) && !a4_close_requested) al_rest(0.01);
while (mouse_b && !a4_close_requested) al_rest(0.01);
clear_keybuf();
for (int i = 0; i < KEY_MAX; i++) key[i] = 0;
if (saved) {
blit(saved, screen, 0, 0, 0, 0, screen->w, screen->h);
destroy_bitmap(saved);
}
_a4_present();
a4_gl_mode = gl;
a4_draw_mode = mode;
for (int i = 0; i < nb; i++) free(lines[i]);
}
int alert(const char* s1, const char* s2, const char* s3, const char* b1, const char* b2, int c1, int c2) {
const char* lines[3] = {s1, s2, s3};
if (!a4_display || !screen || !font) {
for (int i = 0; i < 3; i++)
if (lines[i]) fprintf(stderr, "%s\n", lines[i]);
return 1;
}
ALLEGRO_FONT* af = font->f;
int lh = al_get_font_line_height(af) + 6;
int nb_buttons = b2 ? 2 : 1;
const char* labels[2] = {b1 ? b1 : "OK", b2};
int bw = 0;
for (int i = 0; i < 3; i++)
if (lines[i] && al_get_text_width(af, lines[i]) > bw) bw = al_get_text_width(af, lines[i]);
int btn_w = 0;
for (int i = 0; i < nb_buttons; i++)
if (al_get_text_width(af, labels[i]) + 24 > btn_w) btn_w = al_get_text_width(af, labels[i]) + 24;
if (nb_buttons * btn_w + 20 > bw) bw = nb_buttons * btn_w + 20;
bw += 40;
int bh = 5 * lh + 30;
int bx = (screen->w - bw) / 2, by = (screen->h - bh) / 2;
int btn_y = by + bh - lh - 20;
int btn_x[2];
for (int i = 0; i < nb_buttons; i++) btn_x[i] = bx + (bw - nb_buttons * btn_w - (nb_buttons - 1) * 20) / 2 + i * (btn_w + 20);
BITMAP* saved = create_bitmap(screen->w, screen->h);
if (saved) blit(screen, saved, 0, 0, 0, 0, screen->w, screen->h);
int mode = a4_draw_mode, gl = a4_gl_mode, cursor = a4_cursor_shown;
a4_draw_mode = DRAW_MODE_SOLID;
a4_gl_mode = 1;
show_mouse(screen);
while ((mouse_b || keypressed()) && !a4_close_requested) {
clear_keybuf();
al_rest(0.01);
}
int cx = 0, cy = 0;
_a4_take_click(&cx, &cy);
int choice = 0;
while (!choice) {
if (a4_close_requested) {
choice = nb_buttons;
break;
}
int over = -1;
for (int i = 0; i < nb_buttons; i++)
if (mouse_x >= btn_x[i] && mouse_x < btn_x[i] + btn_w && mouse_y >= btn_y && mouse_y < btn_y + lh + 6) over = i;
rectfill(screen, bx, by, bx + bw - 1, by + bh - 1, makecol(32, 32, 48));
rect(screen, bx, by, bx + bw - 1, by + bh - 1, makecol(200, 200, 220));
for (int i = 0; i < 3; i++)
if (lines[i]) textout_centre_ex(screen, font, lines[i], bx + bw / 2, by + 15 + i * lh, makecol(255, 255, 255), -1);
for (int i = 0; i < nb_buttons; i++) {
rectfill(screen, btn_x[i], btn_y, btn_x[i] + btn_w - 1, btn_y + lh + 5, i == over ? makecol(90, 90, 140) : makecol(60, 60, 90));
rect(screen, btn_x[i], btn_y, btn_x[i] + btn_w - 1, btn_y + lh + 5, makecol(200, 200, 220));
textout_centre_ex(screen, font, labels[i], btn_x[i] + btn_w / 2, btn_y + 6, makecol(255, 255, 255), -1);
}
_a4_present();
while (keypressed()) {
int k = readkey();
int ascii = k & 0xFF;
if (c1 && ascii && (ascii == c1 || ascii == (c1 ^ 0x20))) choice = 1;
if (nb_buttons > 1 && c2 && ascii && (ascii == c2 || ascii == (c2 ^ 0x20))) choice = 2;
if ((k >> 8) == KEY_ENTER || (k >> 8) == KEY_ENTER_PAD) choice = 1;
if ((k >> 8) == KEY_ESC) choice = nb_buttons;
}
if (_a4_take_click(&cx, &cy)) {
for (int i = 0; i < nb_buttons; i++)
if (cx >= btn_x[i] && cx < btn_x[i] + btn_w && cy >= btn_y && cy < btn_y + lh + 6) choice = i + 1;
if (choice)
while ((mouse_b & 1) && !a4_close_requested) al_rest(0.01);
}
al_rest(0.01);
}
while ((key[KEY_ESC] || key[KEY_ENTER]) && !a4_close_requested) al_rest(0.01);
clear_keybuf();
if (saved) {
blit(saved, screen, 0, 0, 0, 0, screen->w, screen->h);
destroy_bitmap(saved);
}
_a4_present();
if (!cursor) show_mouse(NULL);
a4_gl_mode = gl;
a4_draw_mode = mode;
return choice;
}
/* Samples. */
int install_sound(int digi, int midi, const char* cfg_path) {
(void)midi;
(void)cfg_path;
if (digi == DIGI_NONE) return 0;
if (!a4_inited && allegro_init() != 0) return -1;
if (a4_sound_ok) return 0;
if (!al_install_audio()) {
snprintf(allegro_error, sizeof(allegro_error), "could not start the audio");
return -1;
}
al_init_acodec_addon();
if (!al_reserve_samples(32)) {
snprintf(allegro_error, sizeof(allegro_error), "could not reserve audio samples");
return -1;
}
a4_sound_ok = 1;
return 0;
}
void remove_sound(void) {
if (!a4_sound_ok) return;
_a4_midi_shutdown();
al_stop_samples();
al_uninstall_audio();
a4_sound_ok = 0;
}
SAMPLE* load_sample(const char* filename) {
if (!a4_sound_ok) return NULL;
char* path = _a4_resolve(filename);
if (!path) return NULL;
ALLEGRO_SAMPLE* s = al_load_sample(path);
if (!s) {
n_log(LOG_ERR, "could not load sample %s", path);
free(path);
return NULL;
}
free(path);
SAMPLE* spl = NULL;
Malloc(spl, SAMPLE, 1);
if (!spl) {
al_destroy_sample(s);
return NULL;
}
spl->spl = s;
return spl;
}
void stop_sample(const SAMPLE* spl) {
if (!spl || !a4_sound_ok) return;
SAMPLE* s = (SAMPLE*)spl;
for (int i = 0; i < A4_SAMPLE_IDS; i++) {
if (s->used[i]) {
al_stop_sample(&s->ids[i]);
s->used[i] = 0;
}
}
}
void destroy_sample(SAMPLE* spl) {
if (!spl) return;
stop_sample(spl);
if (spl->spl) al_destroy_sample(spl->spl);
Free(spl);
}
static float _a4_gain(int vol) {
return ((float)vol / 255.0f) * ((float)a4_digi_volume / 255.0f);
}
static float _a4_pan(int pan) {
float p = ((float)pan - 128.0f) / 127.0f;
return p < -1.0f ? -1.0f : (p > 1.0f ? 1.0f : p);
}
int play_sample(const SAMPLE* spl, int vol, int pan, int freq, int loop) {
if (!spl || !a4_sound_ok) return -1;
SAMPLE* s = (SAMPLE*)spl;
ALLEGRO_SAMPLE_ID id;
if (!al_play_sample(s->spl, _a4_gain(vol), _a4_pan(pan), (float)freq / 1000.0f, loop ? ALLEGRO_PLAYMODE_LOOP : ALLEGRO_PLAYMODE_ONCE, &id)) return -1;
int slot = s->next;
s->next = (s->next + 1) % A4_SAMPLE_IDS;
s->ids[slot] = id;
s->used[slot] = 1;
return slot;
}
void adjust_sample(const SAMPLE* spl, int vol, int pan, int freq, int loop) {
if (!spl || !a4_sound_ok) return;
SAMPLE* s = (SAMPLE*)spl;
for (int i = 0; i < A4_SAMPLE_IDS; i++) {
if (!s->used[i]) continue;
ALLEGRO_SAMPLE_INSTANCE* inst = al_lock_sample_id(&s->ids[i]);
if (inst) {
al_set_sample_instance_gain(inst, _a4_gain(vol));
al_set_sample_instance_pan(inst, _a4_pan(pan));
al_set_sample_instance_speed(inst, (float)freq / 1000.0f);
al_set_sample_instance_playmode(inst, loop ? ALLEGRO_PLAYMODE_LOOP : ALLEGRO_PLAYMODE_ONCE);
}
al_unlock_sample_id(&s->ids[i]);
}
}
/* MIDI through libfluidsynth, loaded at run time. */
typedef void* (*fs_new_settings_f)(void);
typedef int (*fs_settings_setnum_f)(void*, const char*, double);
typedef int (*fs_settings_setstr_f)(void*, const char*, const char*);
typedef void* (*fs_new_synth_f)(void*);
typedef int (*fs_sfload_f)(void*, const char*, int);
typedef void* (*fs_new_player_f)(void*);
typedef int (*fs_player_add_f)(void*, const char*);
typedef int (*fs_player_int_f)(void*);
typedef int (*fs_player_set_loop_f)(void*, int);
typedef int (*fs_write_float_f)(void*, int, void*, int, int, void*, int, int);
typedef void (*fs_delete_f)(void*);
typedef void* (*fs_set_log_function_f)(int, void*, void*);
static struct {
int tried;
int ok;
N_MODULE* lib;
fs_new_settings_f new_settings;
fs_settings_setnum_f setnum;
fs_settings_setstr_f setstr;
fs_new_synth_f new_synth;
fs_sfload_f sfload;
fs_new_player_f new_player;
fs_player_add_f player_add;
fs_player_int_f player_play;
fs_player_int_f player_stop;
fs_player_set_loop_f player_set_loop;
fs_write_float_f write_float;
fs_player_int_f system_reset;
fs_delete_f delete_player;
fs_delete_f delete_synth;
fs_delete_f delete_settings;
fs_set_log_function_f set_log_function;
void* settings;
void* synth;
void* player;
MIDI* current;
int paused;
ALLEGRO_AUDIO_STREAM* stream;
ALLEGRO_EVENT_QUEUE* queue;
ALLEGRO_THREAD* thread;
ALLEGRO_MUTEX* mutex;
} a4_fs;
#define A4_MIDI_RATE 44100
#define A4_MIDI_FRAG 1024
static N_MODULE* _a4_fluidsynth_open(void) {
static const char* names[] = {
#if defined(__windows__)
"libfluidsynth-3.dll", "libfluidsynth-2.dll", "fluidsynth.dll"
#elif defined(__APPLE__)
"libfluidsynth.3.dylib", "libfluidsynth.dylib"
#else
"libfluidsynth.so.3", "libfluidsynth.so.2", "libfluidsynth.so"
#endif
};
for (size_t i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
N_MODULE* m = n_module_open(names[i]);
if (m) return m;
}
return NULL;
}
/* A symbol into a function pointer: memcpy, as n_module_symbol() advises. */
static int _a4_fluidsynth_sym(N_MODULE* lib, const char* name, void* fptr, size_t fsize) {
void* p = n_module_symbol(lib, name);
if (!p || fsize != sizeof(p)) return FALSE;
memcpy(fptr, &p, fsize);
return TRUE;
}
#define A4_SYM(field, name) ok = ok && _a4_fluidsynth_sym(a4_fs.lib, name, &a4_fs.field, sizeof(a4_fs.field))
static const char* _a4_soundfont(void) {
static const char* candidates[] = {
"/usr/share/sounds/sf2/default-GM.sf2",
"/usr/share/sounds/sf2/FluidR3_GM.sf2",
"/usr/share/sounds/sf2/TimGM6mb.sf2",
"/usr/share/soundfonts/default.sf2",
"/usr/share/soundfonts/FluidR3_GM.sf2",
"/usr/local/share/soundfonts/default.sf2",
"default.sf2"};
const char* env = getenv("NILOREA_SOUNDFONT");
if (env && env[0]) return env;
for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); i++) {
FILE* f = fopen(candidates[i], "rb");
if (f) {
fclose(f);
return candidates[i];
}
}
return NULL;
}
static void* _a4_midi_proc(ALLEGRO_THREAD* thr, void* arg) {
(void)arg;
while (!al_get_thread_should_stop(thr)) {
ALLEGRO_EVENT ev;
if (!al_wait_for_event_timed(a4_fs.queue, &ev, 0.05f)) continue;
if (ev.type != ALLEGRO_EVENT_AUDIO_STREAM_FRAGMENT) continue;
float* buf = al_get_audio_stream_fragment(a4_fs.stream);
if (!buf) continue;
al_lock_mutex(a4_fs.mutex);
if (a4_fs.paused)
memset(buf, 0, sizeof(float) * 2 * A4_MIDI_FRAG);
else
a4_fs.write_float(a4_fs.synth, A4_MIDI_FRAG, buf, 0, 2, buf, 1, 2);
al_unlock_mutex(a4_fs.mutex);
al_set_audio_stream_fragment(a4_fs.stream, buf);
}
return NULL;
}
/* Bring fluidsynth up on first use. FALSE leaves MIDI silent. */
static int _a4_midi_init(void) {
if (a4_fs.tried) return a4_fs.ok;
a4_fs.tried = 1;
if (!a4_sound_ok) return FALSE;
const char* sf = _a4_soundfont();
if (!sf) {
n_log(LOG_NOTICE, "no General MIDI soundfont found (set NILOREA_SOUNDFONT), MIDI is silent");
return FALSE;
}
a4_fs.lib = _a4_fluidsynth_open();
if (!a4_fs.lib) {
n_log(LOG_NOTICE, "libfluidsynth not found, MIDI is silent");
return FALSE;
}
int ok = TRUE;
A4_SYM(new_settings, "new_fluid_settings");
A4_SYM(setnum, "fluid_settings_setnum");
A4_SYM(setstr, "fluid_settings_setstr");
A4_SYM(new_synth, "new_fluid_synth");
A4_SYM(sfload, "fluid_synth_sfload");
A4_SYM(new_player, "new_fluid_player");
A4_SYM(player_add, "fluid_player_add");
A4_SYM(player_play, "fluid_player_play");
A4_SYM(player_stop, "fluid_player_stop");
A4_SYM(player_set_loop, "fluid_player_set_loop");
A4_SYM(write_float, "fluid_synth_write_float");
A4_SYM(system_reset, "fluid_synth_system_reset");
A4_SYM(delete_player, "delete_fluid_player");
A4_SYM(delete_synth, "delete_fluid_synth");
A4_SYM(delete_settings, "delete_fluid_settings");
if (!ok) {
n_log(LOG_NOTICE, "libfluidsynth lacks an expected symbol, MIDI is silent");
return FALSE;
}
/* optional: keep fluidsynth's warnings about audio drivers it is not
* asked to use (FLUID_WARN 2, FLUID_INFO 3, FLUID_DBG 4) off stderr */
if (_a4_fluidsynth_sym(a4_fs.lib, "fluid_set_log_function", &a4_fs.set_log_function, sizeof(a4_fs.set_log_function))) {
for (int level = 2; level <= 4; level++) a4_fs.set_log_function(level, NULL, NULL);
}
a4_fs.settings = a4_fs.new_settings();
if (!a4_fs.settings) return FALSE;
a4_fs.setnum(a4_fs.settings, "synth.sample-rate", (double)A4_MIDI_RATE);
a4_fs.setnum(a4_fs.settings, "synth.gain", 0.5);
/* advance the player with the rendered samples, not a system timer */
a4_fs.setstr(a4_fs.settings, "player.timing-source", "sample");
a4_fs.synth = a4_fs.new_synth(a4_fs.settings);
if (!a4_fs.synth) return FALSE;
if (a4_fs.sfload(a4_fs.synth, sf, 1) < 0) {
n_log(LOG_NOTICE, "could not load soundfont %s, MIDI is silent", sf);
return FALSE;
}
a4_fs.mutex = al_create_mutex();
a4_fs.queue = al_create_event_queue();
a4_fs.stream = al_create_audio_stream(4, A4_MIDI_FRAG, A4_MIDI_RATE, ALLEGRO_AUDIO_DEPTH_FLOAT32, ALLEGRO_CHANNEL_CONF_2);
if (!a4_fs.mutex || !a4_fs.queue || !a4_fs.stream) return FALSE;
al_set_audio_stream_gain(a4_fs.stream, (float)a4_midi_volume / 255.0f);
if (!al_attach_audio_stream_to_mixer(a4_fs.stream, al_get_default_mixer())) return FALSE;
al_register_event_source(a4_fs.queue, al_get_audio_stream_event_source(a4_fs.stream));
a4_fs.thread = al_create_thread(_a4_midi_proc, NULL);
if (!a4_fs.thread) return FALSE;
al_start_thread(a4_fs.thread);
n_log(LOG_DEBUG, "MIDI through fluidsynth with soundfont %s", sf);
a4_fs.ok = TRUE;
return TRUE;
}
static void _a4_midi_stop_locked(void) {
if (a4_fs.player) {
a4_fs.player_stop(a4_fs.player);
a4_fs.delete_player(a4_fs.player);
a4_fs.player = NULL;
}
if (a4_fs.synth && a4_fs.ok) a4_fs.system_reset(a4_fs.synth);
a4_fs.current = NULL;
a4_fs.paused = 0;
}
static void _a4_midi_shutdown(void) {
if (a4_fs.thread) {
al_destroy_thread(a4_fs.thread);
a4_fs.thread = NULL;
}
if (a4_fs.mutex) _a4_midi_stop_locked();
if (a4_fs.stream) {
al_destroy_audio_stream(a4_fs.stream);
a4_fs.stream = NULL;
}
if (a4_fs.queue) {
al_destroy_event_queue(a4_fs.queue);
a4_fs.queue = NULL;
}
if (a4_fs.mutex) {
al_destroy_mutex(a4_fs.mutex);
a4_fs.mutex = NULL;
}
if (a4_fs.synth) a4_fs.delete_synth(a4_fs.synth);
if (a4_fs.settings) a4_fs.delete_settings(a4_fs.settings);
a4_fs.synth = NULL;
a4_fs.settings = NULL;
n_module_close(&a4_fs.lib);
memset(&a4_fs, 0, sizeof(a4_fs));
}
MIDI* load_midi(const char* filename) {
char* path = _a4_resolve(filename);
if (!path) return NULL;
FILE* f = fopen(path, "rb");
if (!f) {
n_log(LOG_ERR, "could not open MIDI file %s", path);
free(path);
return NULL;
}
fclose(f);
MIDI* m = NULL;
Malloc(m, MIDI, 1);
if (!m) {
free(path);
return NULL;
}
m->path = path;
return m;
}
int play_midi(MIDI* midi, int loop) {
if (!midi) {
stop_midi();
return 0;
}
if (!_a4_midi_init()) return 0;
al_lock_mutex(a4_fs.mutex);
_a4_midi_stop_locked();
a4_fs.player = a4_fs.new_player(a4_fs.synth);
if (a4_fs.player) {
a4_fs.player_add(a4_fs.player, midi->path);
a4_fs.player_set_loop(a4_fs.player, loop ? -1 : 1);
a4_fs.player_play(a4_fs.player);
a4_fs.current = midi;
}
al_unlock_mutex(a4_fs.mutex);
return 0;
}
void stop_midi(void) {
if (!a4_fs.ok) return;
al_lock_mutex(a4_fs.mutex);
_a4_midi_stop_locked();
al_unlock_mutex(a4_fs.mutex);
}
void destroy_midi(MIDI* midi) {
if (!midi) return;
if (a4_fs.ok && a4_fs.current == midi) stop_midi();
free(midi->path);
Free(midi);
}
void midi_pause(void) {
if (!a4_fs.ok) return;
al_lock_mutex(a4_fs.mutex);
a4_fs.paused = 1;
al_unlock_mutex(a4_fs.mutex);
}
void midi_resume(void) {
if (!a4_fs.ok) return;
al_lock_mutex(a4_fs.mutex);
a4_fs.paused = 0;
al_unlock_mutex(a4_fs.mutex);
}
void set_volume(int digi_volume, int midi_volume) {
if (digi_volume >= 0) a4_digi_volume = digi_volume > 255 ? 255 : digi_volume;
if (midi_volume >= 0) a4_midi_volume = midi_volume > 255 ? 255 : midi_volume;
if (a4_fs.stream) al_set_audio_stream_gain(a4_fs.stream, (float)a4_midi_volume / 255.0f);
}
/* Old nilorea Allegro 4 helpers. */
int gfx_mode(int mode, int w, int h, int v_w, int v_h, int depth, double r, double g, double b, double a) {
(void)a;
set_color_depth(depth);
if (set_gfx_mode(mode, w, h, v_w, v_h) != 0) {
allegro_message("Could not set a %dx%d graphics mode: %s", w, h, allegro_error);
return FALSE;
}
/* components were given either as 0-1 or as 0-255 */
double scale = (r > 1.0 || g > 1.0 || b > 1.0) ? 1.0 : 255.0;
int gl = a4_gl_mode;
a4_gl_mode = 1;
clear_to_color(screen, makecol((int)(r * scale), (int)(g * scale), (int)(b * scale)));
a4_gl_mode = gl;
_a4_present();
return TRUE;
}
int gfx_status(int action, int value) {
if (action == SET) a4_gl_mode = (value == MODE_OGL);
return a4_gl_mode ? MODE_OGL : MODE_STD;
}
void fill_str(char* str, int c, size_t len) {
if (str) memset(str, c, len);
}
int a4_get_keyboard(char* keybuf, int* cur, int flags, int max) {
(void)flags;
if (!keybuf || !cur || max <= 0) return 0;
if (*cur < 0) *cur = 0;
if (*cur > max - 1) *cur = max - 1;
int taken = 0;
while (keypressed()) {
int k = readkey();
int ascii = k & 0xFF;
taken++;
if ((k >> 8) == KEY_BACKSPACE) {
if (*cur > 0) (*cur)--;
keybuf[*cur] = '\0';
} else if (ascii >= 32 && ascii < 127 && *cur < max - 1) {
keybuf[(*cur)++] = (char)ascii;
keybuf[*cur] = '\0';
}
}
return taken;
}
/* Old nilorea INI driven menu. */
typedef struct LEX_ITEM {
BITMAP* image;
BITMAP* over;
SAMPLE* sound;
int x;
int y;
int h_align; /* 0 left, 1 centre, 2 right: which part of the image is at x */
int v_align; /* 0 top, 1 middle, 2 bottom: which side of y the image hangs */
} LEX_ITEM;
struct LexMenu {
BITMAP* bg;
LEX_ITEM* items;
int nb_items;
int selected;
};
static int _a4_align(const char* v, int is_h) {
if (!v) return 0;
if (!strcasecmp(v, "center") || !strcasecmp(v, "centre") || !strcasecmp(v, "middle")) return 1;
if (is_h && !strcasecmp(v, "right")) return 2;
if (!is_h && !strcasecmp(v, "bottom")) return 2;
return 0;
}
/* A brighter copy of an item image, shown under the pointer. */
static BITMAP* _a4_highlight(BITMAP* src) {
BITMAP* b = create_bitmap(src->w, src->h);
if (!b) return NULL;
ALLEGRO_BITMAP* s = _a4_source(src, FALSE);
_a4_target(b);
al_clear_to_color(al_map_rgba(0, 0, 0, 0));
_a4_blend_copy();
al_draw_bitmap(s, 0, 0, 0);
/* add 35% white where the image is not transparent */
al_set_separate_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ONE, ALLEGRO_ADD, ALLEGRO_ZERO, ALLEGRO_ONE);
al_draw_tinted_bitmap(s, al_map_rgba_f(0.35f, 0.35f, 0.35f, 1.0f), 0, 0, 0);
return b;
}
LexMenu* lexLoadMenuFromFile(const char* file, const char* section) {
if (!file || !section) return NULL;
char* path = _a4_resolve(file);
if (!path) return NULL;
ALLEGRO_CONFIG* cfg = al_load_config_file(path);
if (!cfg) {
n_log(LOG_ERR, "could not load menu file %s", path);
free(path);
return NULL;
}
free(path);
LexMenu* menu = NULL;
Malloc(menu, LexMenu, 1);
if (!menu) {
al_destroy_config(cfg);
return NULL;
}
const char* bg = al_get_config_value(cfg, section, "bg");
if (bg) menu->bg = load_bitmap(bg, NULL);
int count = 0;
for (;;) {
char k[32];
snprintf(k, sizeof(k), "item%d", count);
if (!al_get_config_value(cfg, section, k)) break;
count++;
}
if (count > 0) Malloc(menu->items, LEX_ITEM, (size_t)count);
if (!menu->items) count = 0;
for (int i = 0; i < count; i++) {
char k[32];
char sec[512];
snprintf(k, sizeof(k), "item%d", i);
snprintf(sec, sizeof(sec), "%s.%s", section, al_get_config_value(cfg, section, k));
LEX_ITEM* it = &menu->items[i];
const char* v = al_get_config_value(cfg, sec, "x");
it->x = v ? atoi(v) : 0;
v = al_get_config_value(cfg, sec, "y");
it->y = v ? atoi(v) : 0;
it->h_align = _a4_align(al_get_config_value(cfg, sec, "h_align"), TRUE);
it->v_align = _a4_align(al_get_config_value(cfg, sec, "v_align"), FALSE);
v = al_get_config_value(cfg, sec, "image");
if (v) it->image = load_bitmap(v, NULL);
if (it->image) it->over = _a4_highlight(it->image);
v = al_get_config_value(cfg, sec, "sound");
if (v) it->sound = load_sample(v);
}
menu->nb_items = count;
al_destroy_config(cfg);
return menu;
}
BITMAP* lexMenuGetBitmap(LexMenu* menu, int item, int state) {
if (!menu || item < 0 || item >= menu->nb_items) return NULL;
return state == LEX_MENU_OVER ? menu->items[item].over : menu->items[item].image;
}
/* Rectangle of an item in buf coordinates. */
static void _a4_item_rect(const LexMenu* menu, const BITMAP* buf, int i, float* x, float* y, float* w, float* h) {
const LEX_ITEM* it = &menu->items[i];
float sx = menu->bg ? (float)buf->w / (float)menu->bg->w : 1.0f;
float sy = menu->bg ? (float)buf->h / (float)menu->bg->h : 1.0f;
float iw = it->image ? (float)it->image->w : 0.0f;
float ih = it->image ? (float)it->image->h : 0.0f;
float ix = (float)it->x;
float iy = (float)it->y;
/* h_align says which part of the image is at x. v_align says on which
* side of y the image hangs: "bottom" puts it below y, so y is its top
* edge. That is how the menu files of the games that used the old
* module place their buttons (Elementals' last button ends exactly on
* its panel's bottom edge, The Fallings' first one clears the byline). */
if (it->h_align == 1) ix -= iw / 2.0f;
if (it->h_align == 2) ix -= iw;
if (it->v_align == 1) iy -= ih / 2.0f;
if (it->v_align == 0) iy -= ih;
*x = ix * sx;
*y = iy * sy;
*w = iw * sx;
*h = ih * sy;
}
static int _a4_item_at(const LexMenu* menu, const BITMAP* buf, int mx, int my) {
for (int i = 0; i < menu->nb_items; i++) {
float x, y, w, h;
_a4_item_rect(menu, buf, i, &x, &y, &w, &h);
if ((float)mx >= x && (float)mx < x + w && (float)my >= y && (float)my < y + h) return i;
}
return -1;
}
int lexMenuRun(LexMenu* menu, BITMAP* buf) {
if (!menu || !buf || menu->nb_items <= 0) return -1;
int cursor = a4_cursor_shown;
show_mouse(buf);
/* do not take the click or key that opened the menu as a choice */
while ((mouse_b || key[KEY_ESC] || key[KEY_ENTER] || key[KEY_SPACE]) && !a4_close_requested) al_rest(0.01);
clear_keybuf();
int cx = 0, cy = 0;
_a4_take_click(&cx, &cy);
if (menu->selected < 0 || menu->selected >= menu->nb_items) menu->selected = 0;
int chosen = -2;
int last_mx = mouse_x, last_my = mouse_y;
while (chosen == -2) {
if (a4_close_requested) {
chosen = -1;
break;
}
int hover = _a4_item_at(menu, buf, mouse_x, mouse_y);
if (hover >= 0 && (mouse_x != last_mx || mouse_y != last_my)) menu->selected = hover;
last_mx = mouse_x;
last_my = mouse_y;
while (keypressed()) {
int k = readkey() >> 8;
if (k == KEY_UP) menu->selected = (menu->selected + menu->nb_items - 1) % menu->nb_items;
if (k == KEY_DOWN) menu->selected = (menu->selected + 1) % menu->nb_items;
if (k == KEY_ENTER || k == KEY_ENTER_PAD || k == KEY_SPACE) chosen = menu->selected;
if (k == KEY_ESC) chosen = -1;
}
if (_a4_take_click(&cx, &cy)) {
int clicked = _a4_item_at(menu, buf, cx, cy);
if (clicked >= 0) {
menu->selected = clicked;
chosen = clicked;
while ((mouse_b & 1) && !a4_close_requested) al_rest(0.01);
}
}
/* draw */
if (menu->bg)
stretch_blit(menu->bg, buf, 0, 0, menu->bg->w, menu->bg->h, 0, 0, buf->w, buf->h);
else
clear(buf);
for (int i = 0; i < menu->nb_items; i++) {
BITMAP* img = (i == menu->selected && menu->items[i].over) ? menu->items[i].over : menu->items[i].image;
if (!img) continue;
float x, y, w, h;
_a4_item_rect(menu, buf, i, &x, &y, &w, &h);
ALLEGRO_BITMAP* s = _a4_source(img, TRUE);
_a4_target(buf);
_a4_blend_alpha();
al_draw_scaled_bitmap(s, 0, 0, (float)img->w, (float)img->h, x, y, w, h, 0);
}
if (screen) {
int gl = a4_gl_mode;
a4_gl_mode = 1;
stretch_blit(buf, screen, 0, 0, buf->w, buf->h, 0, 0, screen->w, screen->h);
_a4_present();
a4_gl_mode = gl;
}
al_rest(0.01);
}
if (chosen >= 0 && menu->items[chosen].sound) play_sample(menu->items[chosen].sound, 255, 128, 1000, 0);
while (key[KEY_ESC] && !a4_close_requested) al_rest(0.01);
clear_keybuf();
if (!cursor) show_mouse(NULL);
return chosen;
}
LexMenu* lexMenuDestroy(LexMenu* menu) {
if (!menu) return NULL;
for (int i = 0; i < menu->nb_items; i++) {
destroy_bitmap(menu->items[i].image);
destroy_bitmap(menu->items[i].over);
destroy_sample(menu->items[i].sound);
}
Free(menu->items);
destroy_bitmap(menu->bg);
Free(menu);
return NULL;
}
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