~rycwo/forge

8c3b0a194d23b3618f3e8d14c50abab50940b391 — Ryan Chan 2 years ago d7ee94d
Refactor docs to use square brackets for API refs

fg_window_handle: Prefer strong typedefs of fg_handle to check
compatibilty of handles with their corresponding memory pool at compile
time.
M README.md => README.md +28 -37
@@ 1,20 1,22 @@
**Forge** is a suite of artist tools for creating 2d/3d content. The tools are
simple and [**really fast**](). They are developed with an emphasis on clarity
and simplicity. Forge software is, and always will be, committed to being
[free][].
developed with an emphasis on clarity and simplicity.

Popular content creation applications are monolithic, they all tend to do a
large number of things rather poorly.
large number of things rather poorly. This project aims to provide individual,
purpose-built tools that each solve a particular problem well.

Forge aims to provide individual, **purpose-built** tools that each solve a
particular problem well. The magic here is in the flexible data model that allow
multiple tools to read and write different attributes of the same data
**simultaneously**. This enables artist teams to collaborate in real-time and
creates the opportunity for seamless workflows between each stage of the content
creation pipeline. No more saving of your work to some intercharge format (OBJ,
Alembic, etc.) in order to transfer it between content creation packages.
Forge is the name of both the overarching project, and the foundation library
that Forge tools are built upon. The library provides a common framework for
programmers to develop digital content creation tools (sometimes abbreviated as
DCCs). It includes basic support for: linear algebra; geometry processing;
shading and rendering; and so on. It also provides a fundamental module for
building responsive graphical user interfaces.

Forge software is, and always will be, committed to being [free][]. Read the
[docs][] for more information.

[free]: https://www.fsf.org/about/what-is-free-software
[docs]: https://docs.polyforge.org/

## Overview



@@ 42,32 44,22 @@ Forge projects build using [Meson][meson].

[meson]: https://mesonbuild.com/

## Mailing list
### Preprocessor defines

Subscribe to release and security announcements at [forge-announce][].
`FG_REAL_64` — Set [`fg_real`][fg_real] to `double` (64-bit). This subsequently
switches the `fg_vecn`/`fg_matn` coefficient types and the types used by various
math operations.

[forge-announce]: https://lists.sr.ht/~rycwo/forge-announce/
[fg_real]: #

## Contributing

### Submitting changes

Patches should be submitted to [forge-devel][].
Patches should be submitted to [forge-devel][]. Please read through the [Style
Guide][style-guide] and [Contribution Guide][contrib-guide] prior to submission.

[contrib-guide]: https://docs.polyforge.org/contrib-guide
[forge-devel]: https://lists.sr.ht/~rycwo/forge-devel/

### Formatting your commits

Work as you like locally, then use [`git rebase`][git-rebase]
([man][git-rebase-man]) to tidy-up your changes before submitting patches.

Each commit should represent an atomic unit of work and leave the repo in a
working state. Commit messages should have sufficient detail to double as
details release notes (see [PostgreSQL][postgres]).

[git-rebase-man]: https://git-scm.com/book/en/v2/Git-Branching-Rebasing
[git-rebase]: https://git-rebase.io/
[postgres]: https://drewdevault.com/2021/08/05/In-praise-of-Postgres.html
[style-guide]: https://docs.polyforge.org/style-guide

## License



@@ 78,11 70,10 @@ only](LICENSE) [(more info)][gpl-v3], unless otherwise specified.

### Third-party licenses

[PCG](https://www.pcg-random.org/)
- [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html)

[FreeType 2](https://www.freetype.org/index.html)
- [Freetype Project License](https://www.freetype.org/license.html)
- [PCG](https://www.pcg-random.org/) ([Apache License 2.0][])
- [FreeType 2](https://www.freetype.org/index.html) ([Freetype Project License][])
- [Manrope](https://manropefont.com/), [Inter](https://rsms.me/inter/) ([SIL Open Font License 1.1][])

[Manrope](https://manropefont.com/), [Inter](https://rsms.me/inter/)
- [SIL Open Font License 1.1](https://scripts.sil.org/OFL)
[Apache License 2.0]: https://git.sr.ht/~rycwo/forge/tree/master/item/src
[Freetype Project License]: https://www.freetype.org/license.html
[SIL Open Font License 1.1]: https://scripts.sil.org/OFL

M include/forge/collection.h => include/forge/collection.h +3 -3
@@ 17,8 17,8 @@
/*
 * Handle to a memory pool block.
 *
 * Use `fg_access_memory_pool_block` to retrieve a pointer to the raw memory for
 * the block.
 * Use [[fg_access_memory_pool_block]] to retrieve a pointer to the raw memory
 * for the block.
 */
struct fg_handle {
	int32_t id;


@@ 38,7 38,7 @@ struct fg_handle {
 * A free list is used to keep track of holes in the buffer to enable constant
 * time "alloc" and "free" of individual blocks at runtime. No actual
 * allocations/frees occur besides the initial allocation made by
 * `fg_alloc_memory_pool`.
 * [[fg_alloc_memory_pool]].
 */
struct fg_memory_pool {
	struct fg_allocator alloc;

M include/forge/gui_basic.h => include/forge/gui_basic.h +23 -22
@@ 13,19 13,20 @@
#include <stdbool.h>
#include <stdint.h>

#include "forge/collection.h"
#include "forge/gui_type.h"
#include "forge/type.h"

/*
 * Set the active layer to draw proceeding GUI elements to.
 *
 * Only two layers are supported at the moment (see `fg_gui_layer`):
 * `FG_GUI_LAYER_BASE`, and `FG_GUI_LAYER_OVERLAY`.
 * Only two layers are supported at the moment (see [[fg_gui_layer]]):
 * [[FG_GUI_LAYER_BASE]], and [[FG_GUI_LAYER_OVERLAY]].
 *
 * Elements drawn in `FG_GUI_LAYER_OVERLAY` are always drawn on-top of elements
 * in `FG_GUI_LAYER_BASE`. The active layer must always be restored to
 * `FG_GUI_LAYER_BASE` by the end of the frame or a fatal error will be raised
 * on `commit_gui_frame`.
 * Elements drawn in [[FG_GUI_LAYER_OVERLAY]] are always drawn on-top of
 * elements in [[FG_GUI_LAYER_BASE]]. The active layer must always be restored
 * to [[FG_GUI_LAYER_BASE]] by the end of the frame or a fatal error will be
 * thrown on [[commit_gui_frame]].
 */
void
fg_set_gui_layer(struct fg_gui_context* context, int layer);


@@ 42,11 43,11 @@ fg_gui_rect(
/*
 * Push a container onto the layout stack.
 *
 * Unlike `fg_push_gui_layout_container`, this is intended to behave like other
 * GUI element functions. It respects `fg_set_next_gui_position` and it draws
 * the container according to the given style.
 * Unlike [[fg_push_gui_layout_container]], this is intended to behave like
 * other GUI element functions. It respects [[fg_set_next_gui_position]] and it
 * draws the container according to the given style.
 *
 * This should be bookended by a call to `fg_end_gui_container`.
 * This should be bookended by a call to [[fg_end_gui_container]].
 */
void
fg_begin_gui_container(


@@ 58,13 59,13 @@ fg_begin_gui_container(
 * Pop the top container off the layout stack.
 *
 * It is an error end a GUI container this without a matching previous call to
 * `fg_begin_gui_container`.
 * [[fg_begin_gui_container]].
 */
void
fg_end_gui_container(struct fg_gui_context* context);

/*
 * States for `fg_gui_button`.
 * States for [[fg_gui_button]].
 */
enum fg_gui_button_state {
	FG_GUI_BUTTON_STATE_NONE = 0,


@@ 76,8 77,8 @@ enum fg_gui_button_state {
};

/*
 * State bitflags for `fg_gui_button`.
 * See `struct fg_gui_button_style`.
 * State bitflags for [[fg_gui_button]].
 * See struct [[fg_gui_button_style]].
 */
enum fg_gui_button_style_state {
	FG_GUI_BUTTON_STYLE_NONE    = 0,


@@ 91,25 92,25 @@ enum fg_gui_button_style_state {
};

/* Style struct for each possible button state.
 * See `fg_gui_button`.
 * See [[fg_gui_button]].
 */
struct fg_gui_button_style {
	/*
	 * Array of styles for each button state.
	 * At the very least a style must be provided for the button's default
	 * state `FG_GUI_BUTTON_STATE_NONE`.
	 * state [[FG_GUI_BUTTON_STATE_NONE]].
	 */
	struct fg_gui_style style[FG_GUI_BUTTON_STATE_Size];
	/*
	 * Styles defined in the `style` array.
	 * This should be a bitwise OR of `enum fg_gui_button_style_state` variants
	 * for each style set in the `style` array.
	 * This should be a bitwise OR of enum [[fg_gui_button_style_state]]
	 * variants for each style set in the `style` array.
	 */
	int states;
};

/*
 * Flags for `fg_gui_button`.
 * Flags for [[fg_gui_button]].
 */
enum fg_gui_button_flag {
	FG_GUI_BUTTON_DEFAULT          = 0,


@@ 120,7 121,7 @@ enum fg_gui_button_flag {
 * Basic button element.
 *
 * Styles can be configured for each possible button state with
 * `struct fg_gui_button_style`.
 * struct [[fg_gui_button_style]].
 *
 * ```c
 *     struct fg_gui_button_style const button_style = {


@@ 133,14 134,14 @@ enum fg_gui_button_flag {
 * ```
 *
 * At the very least a style must be provided for the button's default state
 * `FG_GUI_BUTTON_STATE_NONE`.
 * [[FG_GUI_BUTTON_STATE_NONE]].
 *
 * Returns true if the button was triggered on the current frame.
 *
 * The default button trigger is determined by a basic sequence of events: left
 * mouse button is pressed while positioned over button → left mouse button is
 * **released** while positioned over button. If
 * `FG_GUI_BUTTON_TRIGGER_ON_PRESS` is specified, then the trigger happens
 * [[FG_GUI_BUTTON_TRIGGER_ON_PRESS]] is specified, then the trigger happens
 * immediately when the mouse button is **pressed** instead.
 */
bool

M include/forge/gui_context.h => include/forge/gui_context.h +3 -5
@@ 15,13 15,11 @@

/*
 * Buffer initialization configuration.
 * See `fg_init_gui`.
 * See [[fg_init_gui]].
 */
struct fg_gui_buffer_desc {
	// Capacity of basic primitive buffer.
	int rect_buffer_size;
	// Capacity of text primitive buffer.
	int text_buffer_size;
	// Capacity of line primitive buffer.
	int line_buffer_size;
	// Capacity of layout container stack.


@@ 70,11 68,11 @@ fg_commit_gui_frame(struct fg_gui_context* context);
 * Draw GUI elements.
 *
 * This should only be called once per-frame at any time after
 * `fg_commit_gui_frame`.
 * [[fg_commit_gui_frame]].
 *
 * Any draw state that is modified as a result of this call should be restored
 * before control is returned to the caller. The graphics backend is a separate
 * implementation that can be customized as needed, e.g. `gui_opengl_46.h`.
 * implementation that can be customized as needed, e.g. [[gui_opengl_46.h]].
 */
void
fg_draw_gui_frame(struct fg_gui_context* context);

M include/forge/gui_type.h => include/forge/gui_type.h +19 -17
@@ 22,7 22,7 @@
 * Input information for the current frame.
 *
 * Users are expected to update this struct by parsing inputs polled from the OS
 * and passing them to the corresponding functions from `forge/gui_input.h`.
 * and passing them to the corresponding functions from [[gui_input.h]].
 *
 * [GLFW](https://www.glfw.org/) example:
 *


@@ 71,7 71,7 @@ struct fg_gui_input {

/*
 * GUI element draw layer.
 * See `fg_set_gui_layer`.
 * See [[fg_set_gui_layer]].
 */
enum fg_gui_layer {
	FG_GUI_LAYER_BASE = 0,


@@ 80,7 80,7 @@ enum fg_gui_layer {
};

/*
 * Style feature bitflags for `struct fg_gui_style`.
 * Style feature bitflags for struct [[fg_gui_style]].
 */
enum fg_gui_style_flag {
	FG_GUI_STLYE_DEFAULT         = 0,


@@ 97,13 97,13 @@ struct fg_gui_style {
	float border_width;
	// Rounded corner radius in pixels.
	float corner_radius;
	// Flags to indicate style features. See `enum fg_gui_style_flag`.
	// Flags to indicate style features. See enum [[fg_gui_style_flag]].
	int flags;
};

/*
 * Packed rect representation.
 * Struct members are packed to reduce GPU throughput.
 * Struct members are packed to increase GPU throughput.
 */
struct fg_gui_rect {
	// Adhere to std430 shader storage buffer alignment rules


@@ 116,8 116,10 @@ struct fg_gui_rect {
	 *
	 * The rect was originally being compressed into four half (16-bit) floats,
	 * but the rounding becomes pretty harsh past 1024 so it was reverted back
	 * to single-precision floats. We could look at eventually implementing
	 * better quantization with more even distribution up to say 8K resolution.
	 * to single-precision floats.
	 *
	 * TODO: We could look at eventually implementing better quantization with
	 * more even distribution up to say 8K resolution.
	 */
	float rect[4];
	// unorm4x8: R, G, B, A.


@@ 144,11 146,11 @@ struct fg_gui_rect {
 * Buffer memory is allocated once at the start of the program lifetime and
 * re-used each frame, similar to a [memory pool][]. There is no risk of
 * fragmentation in its usage as the array is reset at the beginning of each
 * frame with `fg_begin_gui_frame`: rects are appended to the front of the
 * frame with [[fg_begin_gui_frame]]: rects are appended to the front of the
 * array, overwriting elements from the previous frame.
 *
 * We make full use of the buffer by writing `FG_GUI_LAYER_BASE` elements to the
 * front of the buffer and `FG_GUI_LAYER_OVERLAY` elements to the back.
 * We make full use of the buffer by writing [[FG_GUI_LAYER_BASE]] elements to
 * the front of the buffer and [[FG_GUI_LAYER_OVERLAY]] elements to the back.
 *
 * [memory pool]: https://en.wikipedia.org/wiki/Memory_pool
 */


@@ 163,9 165,9 @@ struct fg_gui_rect_buffer {
 * Monolithic struct storing GUI state.
 *
 * Most GUI functions receive the context as the first parameter. The context
 * stores global state for GUI modules. Use `fg_init_gui` to create a context
 * struct at the start of the program, eventually followed by `fg_terminate_gui`
 * to cleanup the context at the end of the program.
 * stores global state for GUI modules. Use [[fg_init_gui]] to create a context
 * struct at the start of the program, eventually followed by
 * [[fg_terminate_gui]] to cleanup the context at the end of the program.
 */
struct fg_gui_context {
	struct fg_allocator alloc;


@@ 192,19 194,19 @@ struct fg_gui_context {
	uint64_t hover;
	// Id of the actual item hovered on the current frame. We introduce a single
	// frame delay in order to correctly resolve interaction of overlapping
	// elements.  See `fg_gui_item_hovered` for implementation details.
	// elements.  See [[fg_gui_item_hovered]] for implementation details.
	uint64_t next_hover;
	// Layer of the `next_hover` item.
	int next_hover_layer;

	// Currently active layer. See `enum fg_gui_layer`.
	// Currently active layer. See enum [[fg_gui_layer]].
	int layer;
	// Index of the active clip buffer element.
	int clip;

	// Number of elements pushed to base layer, `FG_GUI_LAYER_BASE`.
	// Number of elements pushed to base layer, [[FG_GUI_LAYER_BASE]].
	int32_t base_count;
	// Number of elements pushed to overlay layer, `FG_GUI_LAYER_OVERLAY`.
	// Number of elements pushed to overlay layer, [[FG_GUI_LAYER_OVERLAY]].
	int32_t overlay_count;

	// Clip rects.

M include/forge/gui_window.h => include/forge/gui_window.h +36 -29
@@ 22,17 22,17 @@
 * module: forge/gui_window.h
 *
 * Windows are stored and managed by a persistent window manager (see
 * `fg_window_manager`) which organizes windows in a tree.
 * [[fg_window_manager]]) which organizes windows in a tree.
 *
 * ## Layout
 *
 * Window layout and drawing are handled separately. The layout is expected to
 * be modified through the functions:
 *
 * - `fg_insert_root_window` (this should only be called once)
 * - `fg_insert_window`
 * - `fg_delete_window`
 * - `fg_resize_window_viewport`
 * - [[fg_insert_root_window]] (this should only be called once)
 * - [[fg_insert_window]]
 * - [[fg_delete_window]]
 * - [[fg_resize_window_viewport]]
 *
 * ## Drawing
 *


@@ 89,18 89,18 @@
 *     float const width = ...;
 *     float const height = ...;
 *
 *     struct fg_handle const win_0 = fg_insert_root_window(
 *     struct fg_window_handle const win_0 = fg_insert_root_window(
 *             &wm,
 *             (struct fg_window){fg_hash_string("win_0", ...), "win_0"});
 *
 *     struct fg_handle const win_1 = fg_insert_window(
 *     struct fg_window_handle const win_1 = fg_insert_window(
 *             &wm,
 *             win_0,
 *             (struct fg_window){fg_hash_string("win_1", ...), "win_1"},
 *             (struct fg_window_tile_split){width * 0.55, FG_WINDOW_TILE_SPLIT_X},
 *             FG_WINDOW_TILE_LU);
 *
 *     struct fg_handle const win_2 = fg_insert_window(
 *     struct fg_window_handle const win_2 = fg_insert_window(
 *             &wm,
 *             win_1,
 *             (struct fg_window){fg_hash_string("win_2", ...), "win_2"},


@@ 167,6 167,13 @@ struct fg_window {
};

/*
 * Strong typedef for window.
 */
struct fg_window_handle {
	struct fg_handle handle;
};

/*
 * Struct with split properties.
 */
struct fg_window_tile_split {


@@ 175,7 182,7 @@ struct fg_window_tile_split {
	// changes in window/monitor size does not affect layout arrangement on
	// de-serialization.
	fg_real size;
	// Axis across which this split occurs. See `fg_window_tile_split_axis`.
	// Axis across which this split occurs. See [[fg_window_tile_split_axis]].
	int axis;
};



@@ 188,13 195,13 @@ struct fg_window_tile {
		struct {
			// Properties of this branch/split.
			struct fg_window_tile_split split;
			struct fg_handle lhs;
			struct fg_handle rhs;
			struct fg_window_handle lhs;
			struct fg_window_handle rhs;
		};
		// Window at the current node.
		struct fg_window window;
	};
	struct fg_handle parent;
	struct fg_window_handle parent;
	// Indicate whether this node is a split or window.
	bool leaf;
};


@@ 204,18 211,18 @@ struct fg_window_tile {
 *
 * The tree of windows can be traversed via the `root` node. Members of the
 * window manager should not be accessed directly. Instead use the API functions
 * provided for window management and drawing (see `forge/gui_window.h`).
 * provided for window management and drawing (see [[gui_window.h]]).
 */
struct fg_window_manager {
	// Pool of `fg_window_tile`.
	// Pool of [[fg_window_tile]].
	// Order of structs in this buffer is insignificant. The tree structure can
	// be traversed via the `root` node.
	struct fg_memory_pool nodes;
	// Root of the window tree.
	struct fg_handle root;
	struct fg_window_handle root;
	 // TODO. May be useful to highlight selected window, perform actions on a
	 // given window with a gamepad, etc.
	struct fg_handle active;
	struct fg_window_handle active;
};

/*


@@ 245,9 252,9 @@ struct fg_window_manager {
 * }
 * ```
 *
 * `user_data` for the callback can be provided when calling `fg_gui_windows`.
 * `user_data` for the callback can be provided when calling [[fg_gui_windows]].
 *
 * See `forge/gui_window.h`.
 * See [[gui_window.h]].
 */
struct fg_gui_window_draw_callback {
	void (*draw)(


@@ 277,22 284,22 @@ fg_terminate_window_manager(struct fg_window_manager* wm);
 * Insert root window.
 * It is an error to call this when there is already a root window.
 */
struct fg_handle
struct fg_window_handle
fg_insert_root_window(struct fg_window_manager* wm, struct fg_window window);

/*
 * Insert a window at the given tile.
 *
 * The `side` should be specified using `fg_window_tile_side`.
 * The `side` should be specified using [[fg_window_tile_side]].
 *
 * Window insertion splits an existing window into two. The newly created window
 * is returned and the original window (now occupying the other split) handle is
 * updated.
 */
struct fg_handle
struct fg_window_handle
fg_insert_window(
		struct fg_window_manager* wm,
		struct fg_handle node,
		struct fg_window_handle node,
		struct fg_window window,
		struct fg_window_tile_split split,
		int side);


@@ 304,7 311,7 @@ fg_insert_window(
 * for windows (leaf nodes).
 */
void
fg_delete_window(struct fg_window_manager* wm, struct fg_handle node);
fg_delete_window(struct fg_window_manager* wm, struct fg_window_handle node);

/*
 * Resize the window viewport.


@@ 315,7 322,7 @@ fg_resize_window_viewport(struct fg_window_manager* wm, fg_vec2 const scale);

/*
 * Draw windows from the given window manager `wm`.
 * See `forge/gui_window.h`.
 * See [[gui_window.h]].
 */
void
fg_gui_windows(


@@ 331,7 338,7 @@ fg_gui_windows(
 * Draw an interactable handle that can be used to adjust a window split.
 *
 * In the current state of the API this is used internally during window drawing
 * and should not be required outside of `fg_gui_windows`.
 * and should not be required outside of [[fg_gui_windows]].
 */
void
fg_gui_window_split(


@@ 344,11 351,11 @@ fg_gui_window_split(
/*
 * Begin window drawing.
 *
 * Required prior to invoking the associated `fg_gui_window_draw_callback` for
 * Required prior to invoking the associated [[fg_gui_window_draw_callback]] for
 * the given window.
 *
 * In the current state of the API this is used internally during window drawing
 * and should not be required outside of `fg_gui_windows`.
 * and should not be required outside of [[fg_gui_windows]].
 */
void
fg_begin_gui_window(


@@ 360,11 367,11 @@ fg_begin_gui_window(
/*
 * End window drawing.
 *
 * Required after invoking the associated `fg_gui_window_draw_callback` for
 * Required after invoking the associated [[fg_gui_window_draw_callback]] for
 * the given window.
 *
 * In the current state of the API this is used internally during window drawing
 * and should not be required outside of `fg_gui_windows`.
 * and should not be required outside of [[fg_gui_windows]].
 */
void
fg_end_gui_window(struct fg_gui_context* context);

M include/forge/hash_table.h => include/forge/hash_table.h +2 -2
@@ 37,7 37,7 @@
 * Allocate storage for the hash table.
 *
 * If unsure about the capacity, leave it at zero and the hash table will grow
 * automatically on insertion based on `FG_HASH_TABLE_INIT_CAPACITY`.
 * automatically on insertion based on [[FG_HASH_TABLE_INIT_CAPACITY]].
 */
void
fg_alloc_hash_table(


@@ 64,7 64,7 @@ fg_lookup_hash_table_entry(
 * Insert key/value pair into the hash table.
 *
 * The table will dynamically grow when the load factor passes
 * `FG_HASH_TABLE_MAX_LOAD_FACTOR` (default 0.6).
 * [[FG_HASH_TABLE_MAX_LOAD_FACTOR]] (default 0.6).
 */
void
fg_insert_hash_table_entry(

M include/forge/math.h.in => include/forge/math.h.in +1 -1
@@ 28,7 28,7 @@ fg_clamp{{$suffix}}({{$type}} value, {{$type}} lower, {{$type}} upper) {

{{end -}}

// Select math functions to match the precision of fg_real
// Select math functions to match the precision of [[fg_real]]
#ifndef FG_REAL_64
#define fg_clamp (fg_clampf)
#else

M include/forge/memory_pool.h => include/forge/memory_pool.h +4 -4
@@ 36,9 36,9 @@ fg_free_memory_pool(struct fg_memory_pool* pool);
 * Allocate a single block from the memory pool.
 *
 * This does not perform any further heap allocations than those already made in
 * an initial call to `fg_alloc_memory_pool`. Instead it finds the next free
 * an initial call to [[fg_alloc_memory_pool]]. Instead it finds the next free
 * block in constant O(1) time and returns a handle which can be used with
 * `fg_access_memory_pool_block` to retrieve a pointer to the raw memory.
 * [[fg_access_memory_pool_block]] to retrieve a pointer to the raw memory.
 */
struct fg_handle
fg_alloc_memory_pool_block(struct fg_memory_pool* pool);


@@ 55,8 55,8 @@ fg_free_memory_pool_block(
 * Get pointer to raw memory for the given block.
 *
 * Block memory will exactly match the block size initially provided to
 * `fg_alloc_memory_pool`. Users are expected to respect this when manipulating
 * the memory returned from this function.
 * [[fg_alloc_memory_pool]]. Users are expected to respect this when
 * manipulating the memory returned from this function.
 *
 * Block access is not reference counted in any way. It is up to the user to
 * ensure the block is not freed during use.

M include/forge/meson.build => include/forge/meson.build +1 -0
@@ 3,6 3,7 @@

tmplgen = find_program('tmplgen')

# Remember to update include/forge/install.sh!
tmpl_headers = [
    'math.h.in',
    'matrix.h.in',

M include/forge/type.h.in => include/forge/type.h.in +2 -2
@@ 29,7 29,7 @@
 * Base real type for math coefficients and operations.
 *
 * By default real values are single-precision (32-bit) floating-point values.
 * Define `FG_REAL_64` to use double-precision (64-bit).
 * Define [[FG_REAL_64]] to use double-precision (64-bit).
 */
#ifndef FG_REAL_64
typedef float fg_real;


@@ 74,7 74,7 @@ typedef fg_real fg_mat4[16];
{{- range $n := list 2 3 4 -}}

/*
 * Convenience struct for arrays of `fg_{{$type}}{{$n}}`.
 * Convenience struct for arrays of [[fg_{{$type}}{{$n}}]].
 */
struct fg_{{$type}}{{$n}}_buffer {
	fg_{{$type}}{{$n}}* buf;

R include/forge/stb_sprintf.h => include/stb_sprintf.h +0 -0
M meson.build => meson.build +27 -25
@@ 3,7 3,7 @@ project(
    'c',
    version : '0.1.0',
    license : 'GPL-3.0-only',
    meson_version: '>=0.54.0',
    meson_version: '>=0.56.0',
    default_options : [
        'warning_level=3',  # Enables -Wall, -Wextra, etc.
        'c_std=c11',


@@ 11,9 11,29 @@ project(

deps = [
    dependency('GL'),
    dependency('glew', version : '>=2.1.0'),
    dependency('glew', version : '>=2.2.0')
]

srcs = files([
    'src/compare_float.c',
    'src/gui_basic.c',
    'src/gui_context.c',
    'src/gui_draw.c',
    'src/gui_graph.c',
    'src/gui_input.c',
    'src/gui_layout.c',
    'src/gui_opengl_46.c',
    'src/gui_util.c',
    'src/gui_window.c',
    'src/hash_table.c',
    'src/memory_pool.c',
    'src/murmur_hash.c',
    'src/pack_float.c',
    'src/pcg_basic.c',
    'src/random_double.c',
    'src/stb_sprintf.c',
])

# https://mesonbuild.com/howtox.html#add-math-library-lm-portably
cc = meson.get_compiler('c')
deps += cc.find_library('m', required : false)


@@ 44,7 64,9 @@ spirv_gen = generator(
spirv_binary = spirv_gen.process(
    files([
        'shader/gui_rect.vert.glsl',
        'shader/gui_rect.frag.glsl'
        'shader/gui_rect.frag.glsl',
        'shader/gui_line.vert.glsl',
        'shader/gui_line.frag.glsl'
    ]))

# Headers must be generated from inside include/forge for two reasons relating


@@ 65,33 87,14 @@ spirv_binary = spirv_gen.process(
# Generated headers can thus be found in build/include/forge (assuming 'build'
# is your build directory).

# Declares:
# gen_headers:       list[custom_tgt]
# Declares: gen_headers list[custom_tgt]
subdir('include/forge')

includes = include_directories('include')

lib = library(
    meson.project_name(),
    files([
        'src/compare_float.c',
        'src/gui_basic.c',
        'src/gui_context.c',
        'src/gui_draw.c',
        'src/gui_graph.c',
        'src/gui_input.c',
        'src/gui_layout.c',
        'src/gui_opengl_46.c',
        'src/gui_util.c',
        'src/gui_window.c',
        'src/hash_table.c',
        'src/memory_pool.c',
        'src/murmur_hash.c',
        'src/pack_float.c',
        'src/pcg_basic.c',
        'src/random_double.c',
        'src/stb_sprintf.c',
    ]),
    srcs,
    gen_headers,
    spirv_binary,
    version : meson.project_version(),


@@ 132,7 135,6 @@ install_headers(
        'include/forge/pack_float.h',
        'include/forge/pcg_basic.h',
        'include/forge/random_double.h',
        'include/forge/stb_sprintf.h',
        'include/forge/stretchy_buffer.h',
    ]),
    subdir : meson.project_name())

M src/gui_context.c => src/gui_context.c +3 -3
@@ 39,7 39,7 @@ fg_init_gui(
	fg_mat4_identity(context->transform);
	context->transform_active = false;

	// FIXME(ryc): Receive clip buffer size as argument
	// FIXME: Receive clip buffer size as argument
	// Default clip is initialized in begin_gui_frame
	context->clips.buf = (float*)alloc->alloc(sizeof(float) * 4 * 128);
	context->clips.capacity = 128;


@@ 104,8 104,8 @@ fg_commit_gui_frame(struct fg_gui_context* context) {

	// Items in the overlay layer are written to the back of the primitive
	// buffers in reverse order. The order of the overlay items must be reversed
	// to respect the expected draw order. They also need to be contiguous with
	// the base layer items to simplify GPU operations.
	// to respect the expected draw order. They are also placed contiguously
	// with base layer items to simplify GPU operations.

	for (int i = 0; i < context->rects.size_back; ++i)
		context->rects.prims[context->rects.size_front + i]

M src/gui_window.c => src/gui_window.c +47 -37
@@ 12,6 12,8 @@
#include <assert.h>
#include <string.h>

#include "stb_sprintf.h"

#include "forge/gui_basic.h"
#include "forge/gui_draw.h"
#include "forge/gui_input.h"


@@ 19,18 21,19 @@
#include "forge/hash_table.h"
#include "forge/input.h"
#include "forge/memory_pool.h"
#include "forge/stb_sprintf.h"

static inline struct fg_window_tile*
get_window_tile(struct fg_window_manager const* wm, struct fg_handle handle) {
get_window_tile(
		struct fg_window_manager const* wm,
		struct fg_window_handle handle) {
	return (struct fg_window_tile*)(
			fg_access_memory_pool_block(&wm->nodes, handle));
			fg_access_memory_pool_block(&wm->nodes, handle.handle));
}

static void
resize_window(
		struct fg_window_manager* wm,
		struct fg_handle node,
		struct fg_window_handle node,
		fg_vec2 const scale) {
	struct fg_window_tile* tile = get_window_tile(wm, node);
	if (tile->leaf)


@@ 44,15 47,15 @@ static void
draw_gui_window(
		struct fg_gui_context* context,
		struct fg_window_manager* wm,
		struct fg_handle node,
		struct fg_window_handle node,
		struct fg_gui_style const* style,
		struct fg_gui_window_draw_callback const* cb,
		void* user_data,
		fg_real spacing,
		uint64_t seed) {
	// FIXME(rycwo): Although the recursion depth will be probably never be
	// enough to cause a stack overflow, an iterative version of this algorithm
	// would be preferred.
	// FIXME: Although the recursion depth will be probably never be enough to
	// cause a stack overflow, an iterative version of this algorithm would be
	// preferred.

	struct fg_window_tile* tile = get_window_tile(wm, node);



@@ 60,7 63,7 @@ draw_gui_window(
	fg_gui_top_layout_container(context, container, false);

	if (tile->leaf) {
		// FIXME(rycwo): Spacing to system window edges and in-between
		// FIXME: Spacing to system window edges and in-between
		// windows is consistent.
		fg_real const half_space = spacing * 0.5;
		fg_set_next_gui_position(


@@ 75,15 78,15 @@ draw_gui_window(
					container[2] - spacing,
					container[3] - spacing
				});
		// TODO(rycwo): Draw window decorations (title bar, close button, etc.)
		// TODO: Draw window decorations (title bar, close button, etc.)
		cb->draw(context, &tile->window, user_data);
		fg_end_gui_window(context);
		return;
	}

	// FIXME(rycwo)
	// FIXME
	static char name[32];
	stbsp_snprintf(&name[0], 32, u8"split_%d", node.id);
	stbsp_snprintf(&name[0], 32, "split_%d", node.handle.id);

	int const i = tile->split.axis;
	int const j = i ^ 1;


@@ 146,10 149,12 @@ fg_terminate_window_manager(struct fg_window_manager* wm) {
	fg_free_memory_pool(&wm->nodes);
}

struct fg_handle
struct fg_window_handle
fg_insert_root_window(struct fg_window_manager* wm, struct fg_window window) {
	assert(!fg_handle_valid(&wm->nodes, wm->root));
	struct fg_handle const root_hdl = fg_alloc_memory_pool_block(&wm->nodes);
	assert(!fg_handle_valid(&wm->nodes, wm->root.handle));
	struct fg_window_handle const root_hdl = {
		fg_alloc_memory_pool_block(&wm->nodes)
	};
	struct fg_window_tile* root = get_window_tile(wm, root_hdl);
	memset(root, 0, sizeof(struct fg_window_tile));
	root->window = window;


@@ 157,19 162,21 @@ fg_insert_root_window(struct fg_window_manager* wm, struct fg_window window) {
	return root_hdl;
}

struct fg_handle
struct fg_window_handle
fg_insert_window(
		struct fg_window_manager* wm,
		struct fg_handle node,
		struct fg_window_handle node,
		struct fg_window window,
		struct fg_window_tile_split split,
		int side) {
	assert(fg_handle_valid(&wm->nodes, node));
	assert(fg_handle_valid(&wm->nodes, node.handle));

	struct fg_window_tile* other = get_window_tile(wm, node);
	assert(other->leaf);

	struct fg_handle const branch_hdl = fg_alloc_memory_pool_block(&wm->nodes);
	struct fg_window_handle const branch_hdl = {
		fg_alloc_memory_pool_block(&wm->nodes)
	};
	struct fg_window_tile* branch = get_window_tile(wm, branch_hdl);

	branch->split = split;


@@ 177,10 184,10 @@ fg_insert_window(

	// Parent the branch under the parent. If there isn't a valid
	// parent then we assume we are splitting the root tile.
	struct fg_handle const parent_hdl = other->parent;
	if (fg_handle_valid(&wm->nodes, parent_hdl)) {
	struct fg_window_handle const parent_hdl = other->parent;
	if (fg_handle_valid(&wm->nodes, parent_hdl.handle)) {
		struct fg_window_tile* parent = get_window_tile(wm, parent_hdl);
		if (parent->lhs.id == node.id)
		if (parent->lhs.handle.id == node.handle.id)
			parent->lhs = branch_hdl;
		else
			parent->rhs = branch_hdl;


@@ 190,7 197,9 @@ fg_insert_window(
		wm->root = branch_hdl;

	// Parent the other and new windows under the branch
	struct fg_handle const new_hdl = fg_alloc_memory_pool_block(&wm->nodes);
	struct fg_window_handle const new_hdl = {
		fg_alloc_memory_pool_block(&wm->nodes)
	};
	struct fg_window_tile* new = get_window_tile(wm, new_hdl);

	new->window = window;


@@ 211,29 220,30 @@ fg_insert_window(
}

void
fg_delete_window(struct fg_window_manager* wm, struct fg_handle node) {
fg_delete_window(struct fg_window_manager* wm, struct fg_window_handle node) {
	struct fg_window_tile* window = get_window_tile(wm, node);
	assert(window->leaf);

	if (node.id == wm->root.id) {
		fg_free_memory_pool_block(&wm->nodes, node);
		memset(&wm->root, 0, sizeof(struct fg_handle));
	if (node.handle.id == wm->root.handle.id) {
		fg_free_memory_pool_block(&wm->nodes, node.handle);
		memset(&wm->root, 0, sizeof(struct fg_window_handle));
		return;
	}

	struct fg_handle const branch_hdl = window->parent;
	struct fg_window_handle const branch_hdl = window->parent;
	struct fg_window_tile* branch = get_window_tile(wm, branch_hdl);

	struct fg_handle const other_hdl = (branch->lhs.id == node.id)
	struct fg_window_handle const other_hdl
		= (branch->lhs.handle.id == node.handle.id)
		?  branch->rhs : branch->lhs;
	struct fg_window_tile* other = get_window_tile(wm, other_hdl);

	// Parent the remaining window under the parent. If there isn't
	// a valid parent then we assume the window is the new root.
	struct fg_handle const parent_hdl = branch->parent;
	if (fg_handle_valid(&wm->nodes, parent_hdl)) {
	struct fg_window_handle const parent_hdl = branch->parent;
	if (fg_handle_valid(&wm->nodes, parent_hdl.handle)) {
		struct fg_window_tile* parent = get_window_tile(wm, parent_hdl);
		if (parent->lhs.id == branch_hdl.id)
		if (parent->lhs.handle.id == branch_hdl.handle.id)
			parent->lhs = other_hdl;
		else
			parent->rhs = other_hdl;


@@ 242,13 252,13 @@ fg_delete_window(struct fg_window_manager* wm, struct fg_handle node) {
	else
		wm->root = other_hdl;

	fg_free_memory_pool_block(&wm->nodes, node);
	fg_free_memory_pool_block(&wm->nodes, branch_hdl);
	fg_free_memory_pool_block(&wm->nodes, node.handle);
	fg_free_memory_pool_block(&wm->nodes, branch_hdl.handle);
}

void
fg_resize_window_viewport(struct fg_window_manager* wm, fg_vec2 const scale) {
	if (!fg_handle_valid(&wm->nodes, wm->root))
	if (!fg_handle_valid(&wm->nodes, wm->root.handle))
		return;
	resize_window(wm, wm->root, scale);
}


@@ 262,7 272,7 @@ fg_gui_windows(
		void* user_data,
		fg_real spacing,
		uint64_t seed) {
	if (!fg_handle_valid(&wm->nodes, wm->root))
	if (!fg_handle_valid(&wm->nodes, wm->root.handle))
		return;

	fg_vec4 container;


@@ 335,7 345,7 @@ fg_begin_gui_window(
		struct fg_gui_style const* style,
		fg_vec2 const size) {
	assert(id != 0);
	// TODO(rycwo): Interaction
	// TODO: Interaction
	fg_begin_gui_container(context, style, size);
}


M src/stb_sprintf.c => src/stb_sprintf.c +1 -1
@@ 7,4 7,4 @@
 * about defining anything.
 */
#define STB_SPRINTF_IMPLEMENTATION
#include "forge/stb_sprintf.h"
#include "stb_sprintf.h"