~rycwo/forge

00595942a183156397216d1ca939faa91e4d15e5 — Ryan Chan 1 year, 7 months ago
Initial commit
65 files changed, 6149 insertions(+), 0 deletions(-)

A .editorconfig
A .gitignore
A LICENSE
A README.md
A TODO
A font/manrope/LICENSE
A font/manrope/Manrope-Bold.ttf
A font/manrope/Manrope-ExtraBold.ttf
A font/manrope/Manrope-ExtraLight.ttf
A font/manrope/Manrope-Light.ttf
A font/manrope/Manrope-Medium.ttf
A font/manrope/Manrope-Regular.ttf
A font/manrope/Manrope-SemiBold.ttf
A include/forge/collection.h
A include/forge/gui.h
A include/forge/gui_basic.h
A include/forge/gui_context.h
A include/forge/gui_draw.h
A include/forge/gui_graph.h
A include/forge/gui_input.h
A include/forge/gui_layout.h
A include/forge/gui_type.h
A include/forge/gui_window.h
A include/forge/hash_table.h
A include/forge/input.h
A include/forge/memory.h
A include/forge/memory_pool.h
A include/forge/murmur_hash.h
A include/forge/pcg_basic.h
A include/forge/seed.h
A include/forge/shader.h
A include/forge/stb_sprintf.h
A include/forge/stretchy_buffer.h
A include/forge/type.h
A include/forge/vector.h
A meson.build
A meson_options.txt
A shader/gui.frag.glsl
A shader/gui.geom.glsl
A shader/gui.vert.glsl
A src/gui_basic.c
A src/gui_context.c
A src/gui_draw.c
A src/gui_graph.c
A src/gui_input.c
A src/gui_layout.c
A src/gui_window.c
A src/hash_table.c
A src/memory_pool.c
A src/murmur_hash.c
A src/pcg_basic.c
A src/seed.c
A src/shader.c
A src/stb_sprintf.c
A test/hash_table_delete.c
A test/hash_table_grow_1.c
A test/hash_table_grow_2.c
A test/hash_table_insert_and_lookup.c
A test/hash_table_insert_same.c
A test/memory_pool_block_alloc_0.c
A test/memory_pool_block_alloc_1.c
A test/memory_pool_block_free.c
A test/meson.build
A test/murmur_hash.c
A test/unit_test.h
A  => .editorconfig +17 -0
@@ 1,17 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = tab
insert_final_newline = true
max_line_length = 80
trim_trailing_whitespace = true

[{*.build,meson_options.txt}]
indent_style = space

[*.md]
indent_size = 2
indent_style = space

A  => .gitignore +1 -0
@@ 1,1 @@
build/

A  => LICENSE +674 -0
@@ 1,674 @@
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

A  => README.md +30 -0
@@ 1,30 @@
Foundation library for [Tau][tau] tools.

## Objectives

- Lightweight
- GPU-accelerated
- Extensible via plug-ins
- Combines procedural and interactive (immediate) workflows
- Supported industry-standard file formats
  - [Alembic](http://www.alembic.io/)
  - [Universal Scene Description (USD)](http://www.openusd.org)
- Written in C11
- Portable ([^1],[^2])

## License

This project is licensed under [GNU General Public License v3.0 only](LICENSE)
[(more info)][gpl-v3].

[gpl-v3]: https://www.gnu.org/licenses/gpl-3.0-standalone.html

## Contributing

Patches welcome! Please refer to the [Tau project hub][tau] for contribution
guidelines.

[tau]: https://sr.ht/~rycwo/tau/

[^1]: Strives to be POSIX compliant (pending audit)
[^2]: No Microsoft Windows support

A  => TODO +45 -0
@@ 1,45 @@
Things to do (in rough order):

- Separate graphics API-specific impl. from core functionality
- Give overlay items interaction priority

- Window drag-drop
- Window tab groups

- Keyboard/gamepad navigation (focus)

- Ring buffer queue
- Half-edge data structure

- Common style/theme
- Basic style grammar

- Canvas view (2d transformation matrix)
- GPU font rasterization (high-quality, scalable)
- SDF font atlas

Third-party deps:

PCG by Melissa E. O'Neill - Apache License 2.0
https://www.pcg-random.org/
https://www.apache.org/licenses/LICENSE-2.0.html

stb_sprintf.h by Sean Barrett (originally Jeff Roberts) - public domain
https://github.com/nothings/stb/blob/master/stb_sprintf.h
https://unlicense.org/

stb_truetype.h by Sean Barrett - public domain
https://github.com/nothings/stb/blob/master/stb_truetype.h
https://unlicense.org/

stretchy_buffer.h by Sean Barrett - public domain
https://github.com/nothings/stb/blob/master/stretchy_buffer.h
https://unlicense.org/

FreeType 2 by multiple - Freetype Project License
https://www.freetype.org/index.html
https://www.freetype.org/license.html

Manrope by Mikhail Sharanda - SIL Open Font License 1.1
https://manropefont.com/
https://scripts.sil.org/OFL

A  => font/manrope/LICENSE +93 -0
@@ 1,93 @@
Copyright 2018 The Manrope Project Authors (https://github.com/sharanda/manrope)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded, 
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

A  => font/manrope/Manrope-Bold.ttf +0 -0
A  => font/manrope/Manrope-ExtraBold.ttf +0 -0
A  => font/manrope/Manrope-ExtraLight.ttf +0 -0
A  => font/manrope/Manrope-Light.ttf +0 -0
A  => font/manrope/Manrope-Medium.ttf +0 -0
A  => font/manrope/Manrope-Regular.ttf +0 -0
A  => font/manrope/Manrope-SemiBold.ttf +0 -0
A  => include/forge/collection.h +70 -0
@@ 1,70 @@
#ifndef COLLECTION_H
#define COLLECTION_H

#include <stdint.h>

#include "forge/memory.h"

/*
 * Handle to a memory pool block.
 * Use access_memory_pool_block() to retrieve a pointer to the raw memory for
 * the block.
 */
struct handle {
	int32_t id;
	int32_t ver;
};

/*
 * Basic memory pool allocator.
 *
 * Improves performance in situations that call for high frequency allocations
 * of memory blocks of the same size. A memory pool solves this problem by
 * enabling the user to pre-allocate a predictable "maximum" number of
 * fixed-size blocks of memory which are re-usable without any further
 * allocations.
 *
 * A free list is employed to keep track of holes in the buffer to enable
 * constant time "allocation" and "free" of individual blocks at runtime.
 * No actual allocations/frees occur besides the initial allocation made by
 * alloc_memory_pool().
 */
struct memory_pool {
	struct allocator alloc;
	unsigned char* mem;
	int32_t* ver;
	int32_t count;
	int32_t block_size;
};

/*
 * Hash table using open-addressing (linear probing) to resolve bucket
 * collisions.
 *
 * Expects keys to be come in hashed. Which means this does not support multiple
 * keys that hash to the same value. In practice, using a 64-bit hash should be
 * enough to avoid this issue:
 *
 * https://bitsquid.blogspot.com/2011/06/strings-redux.html
 *
 * Values will typically be indices into a separate array containing the actual
 * objects. Note that we reserve INT64_MIN to represent an empty table entry,
 * i.e. the range of supported values is (-2^63, 2^63).
 *
 * Operations should take constant time O(1) on average, but performance is
 * highly dependent on the quality of the hash (key), as is the case with any
 * hash table implementation.
 *
 * TODO(rycwo): Explicit trim operation
 */
struct hash_table {
	struct allocator alloc;
	uint64_t* keys;
	int64_t* values;
	int64_t capacity;
	int64_t size;
};

// TODO(rycwo): Hash set

#endif  // COLLECTION_H

A  => include/forge/gui.h +13 -0
@@ 1,13 @@
#ifndef GUI_H
#define GUI_H

#include "forge/gui_basic.h"
#include "forge/gui_context.h"
#include "forge/gui_draw.h"
#include "forge/gui_graph.h"
#include "forge/gui_input.h"
#include "forge/gui_layout.h"
#include "forge/gui_type.h"
#include "forge/gui_window.h"

#endif  // GUI_H

A  => include/forge/gui_basic.h +66 -0
@@ 1,66 @@
#ifndef GUI_BASIC_H
#define GUI_BASIC_H

#include <stdbool.h>
#include <stdint.h>

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

void
set_gui_layer(struct gui_context* context, int layer);

void
gui_rect(
		struct gui_context* context,
		struct gui_style const* style,
		vec2 const size);

void
begin_gui_container(
		struct gui_context* context,
		struct gui_style const* style,
		vec2 const size);

void
end_gui_container(struct gui_context* context);

enum gui_button_state {
	GUI_BUTTON_STATE_NONE = 0,
	GUI_BUTTON_STATE_HOVER,
	GUI_BUTTON_STATE_FOCUS,
	GUI_BUTTON_STATE_ACTIVE,
	GUI_BUTTON_STATE_TRIGGER,
	GUI_BUTTON_STATE_Size,
};

enum gui_button_style_state {
	GUI_BUTTON_STYLE_NONE    = 0,
	GUI_BUTTON_STYLE_HOVER   = 1,
	GUI_BUTTON_STYLE_FOCUS   = 1 << 1,
	GUI_BUTTON_STYLE_ACTIVE  = 1 << 2,
	GUI_BUTTON_STYLE_TRIGGER = 1 << 3,
	GUI_BUTTON_STYLE_ALL     = (
			GUI_BUTTON_STYLE_HOVER  | GUI_BUTTON_STYLE_FOCUS
			| GUI_BUTTON_STYLE_ACTIVE | GUI_BUTTON_STATE_TRIGGER),
};

struct gui_button_style {
	struct gui_style style[GUI_BUTTON_STATE_Size];
	int states;
};

enum gui_button_flag {
	GUI_BUTTON_DEFAULT          = 0,
	GUI_BUTTON_TRIGGER_ON_PRESS = 1,
};

bool
gui_button(
		struct gui_context* context,
		uint64_t id,
		struct gui_button_style const* button_style,
		vec2 const size,
		int flags);

#endif  // GUI_BASIC_H

A  => include/forge/gui_context.h +84 -0
@@ 1,84 @@
#ifndef GUI_CONTEXT_H
#define GUI_CONTEXT_H

#include "forge/gui_type.h"
#include "forge/memory.h"

/*
 * Maximum number of draw items.
 *
 * This is used to allocate buffer memory in init_gui_default().
 *
 * Under typical circumstances this should be enough. Re-define the constant
 * to match your memory constraints. See struct gui_item for size info.
 */
#define GUI_BASE_ITEM_LIMIT 100000

/*
 * Maximum number of overlay draw items.
 *
 * This is used to allocate buffer memory in init_gui_default(). The overlay
 * buffer adds an additional layer of drawing to the framework. It enables
 * items to be drawn on-top of the standard GUI layer. This is helpful for
 * context menus or pop-ups, etc.
 *
 * Under typical circumstances this should be enough. Re-define the constant
 * to match your memory constraints. See struct gui_item for size info.
 */
#define GUI_OVERLAY_ITEM_LIMIT 10000

/*
 * Initialize GUI resources with default buffer sizes.
 * This must be called prior to any GUI-related functions.
 */
void
init_gui_default(struct gui_context* context, struct allocator* alloc);

/*
 * Clean-up GUI resources.
 * This should be called before program exit.
 */
void
terminate_gui(struct gui_context* context);

/*
 * Reset GUI state in preparation for a new frame.
 * This should only be called once per-frame before any "GUI building"-related
 * function calls.
 */
void
begin_gui_frame(struct gui_context* context);

/*
 * Upload GPU resources for draw.
 * This should only be called once per-frame after all "GUI building"-related
 * function calls.
 */
void
commit_gui_frame(struct gui_context* context);

/*
 * Draw GUI elements.
 *
 * This should only be called once per-frame at any time after
 * commit_gui_frame().
 *
 * Any GL state that is modified as a result of this call is restored before
 * control is returned to the caller.
 */
void
draw_gui_frame(struct gui_context* context);

/*
 * Swap front/back GPU vertex buffers.
 *
 * This should only be called once per-frame just after draw_gui_frame().
 *
 * To avoid implicit synchronization when streaming vertex buffer data,
 * GUI elements are stored in alternating buffers. To learn more:
 * https://www.khronos.org/opengl/wiki/Buffer_Object_Streaming#Explicit_multiple_buffering
 */
void
swap_gui_frame(struct gui_context* context);

#endif  // GUI_CONTEXT_H

A  => include/forge/gui_draw.h +20 -0
@@ 1,20 @@
#ifndef GUI_DRAW_H
#define GUI_DRAW_H

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

void
draw_gui_rect(
		struct gui_context* context,
		struct gui_style const* style,
		vec4 const rect);

void
draw_gui_circle(
		struct gui_context* context,
		struct gui_style const* style,
		vec2 const center,
		float const radius);

#endif  // GUI_DRAW_H

A  => include/forge/gui_graph.h +21 -0
@@ 1,21 @@
#ifndef GUI_GRAPH_H
#define GUI_GRAPH_H

#include <stdbool.h>
#include <stdint.h>

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

bool
begin_gui_node(
		struct gui_context* context,
		uint64_t id,
		struct gui_style const* style,
		vec2 const size,
		vec2 pos);

void
end_gui_node(struct gui_context* context);

#endif  // GUI_GRAPH_H

A  => include/forge/gui_input.h +32 -0
@@ 1,32 @@
#ifndef GUI_INPUT_H
#define GUI_INPUT_H

#include <stdbool.h>
#include <stdint.h>

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

void
set_gui_cursor_position(struct gui_input* input, vec2 const pos);

void
set_gui_mouse_buttons(struct gui_input* input, int mouse_buttons);

void
gui_viewport_size(struct gui_context const* context, vec2 size);

void
set_gui_viewport_size(struct gui_context* context, vec2 const size);

bool
gui_item_hovered(struct gui_context* context, uint64_t id, vec4 const rect);

bool
gui_item_pressed(
		struct gui_context* context,
		uint64_t id,
		vec4 const rect,
		bool* hovered);

#endif  // GUI_INPUT_H

A  => include/forge/gui_layout.h +71 -0
@@ 1,71 @@
#ifndef GUI_LAYOUT_H
#define GUI_LAYOUT_H

#include <stdbool.h>

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

/*
 * Get the top container in the layout stack.
 */
void
gui_top_layout_container(
		struct gui_context const* context,
		vec4 rect,
		bool absolute);

/*
 * Push a container onto the layout stack.
 *
 * Proceeding GUI elements (including further containers) are expressed in the
 * space of the container at the top of the stack. Nested containers do not
 * necessarily have to be in the bounds of the containers below it.
 */
void
push_gui_layout_container(
		struct gui_context* context,
		vec4 const rect,
		bool absolute);

/*
 * Pop the top container off the layout stack.
 * It is an error to pop the first element which is reserved to track the
 * viewport size.
 */
void
pop_gui_layout_container(
		struct gui_context* context,
		vec4 rect,
		bool absolute);

void
set_next_gui_position(
		struct gui_context* context,
		vec2 const pos,
		bool absolute);

void
pop_next_gui_position(
		struct gui_context* context,
		vec2 pos,
		bool absolute);

enum gui_flex_layout_flag {
	GUI_FLEX_DEFAULT                  = 0,
	GUI_FLEX_DIRECTION_ROW            = 1,
	GUI_FLEX_DIRECTION_ROW_REVERSE    = 1 << 1,
	GUI_FLEX_DIRECTION_COLUMN         = 1 << 2,
	GUI_FLEX_DIRECTION_COLUMN_REVERSE = 1 << 3,
	GUI_FLEX_NO_WRAP                  = 1 << 4,
	GUI_FLEX_WRAP                     = 1 << 5,
	GUI_FLEX_WRAP_REVERSE             = 1 << 6,
};

void
begin_gui_flex_layout(struct gui_context* context, int count, int flags);

void
end_gui_flex_layout(struct gui_context* context);

#endif  // GUI_LAYOUT_H

A  => include/forge/gui_type.h +92 -0
@@ 1,92 @@
#ifndef GUI_TYPE_H
#define GUI_TYPE_H

#include <stdint.h>

#include <GL/glew.h>

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

/**
 * Convenience wrapper around vec4. Makes copying easier.
 */
struct gui_rect {
	vec4 rect;
};

struct gui_item {
	vec4 rect;
	vec4 color;
	vec4 border_color;
	float border_width;
};

struct gui_item_buffer {
	struct gui_item* mem;
	int capacity;
	int size;
};

enum gui_style_flag {
	GUI_STLYE_DEFAULT        = 0,
	GUI_STYLE_BORDER         = 1,
	GUI_STYLE_BORDER_DASHED  = 1 << 1,
	GUI_STYLE_CORNER_ROUNDED = 1 << 2,
};

// TODO(rycwo): Background image (texture) support
struct gui_style {
	vec4 color;
	vec4 border_color;
	float border_width;
	float corner_radius;
	int flags;
};

enum gui_layer {
	GUI_LAYER_BASE = 0,
	GUI_LAYER_OVERLAY,
	GUI_LAYER_Size
};

struct gui_input {
	vec2 cursor_pos;
	vec2 cursor_pos_delta;
	int mouse;
	int mouse_press;
	int mouse_release;
};

struct gui_context {
	struct allocator alloc;
	struct gui_input input;
	uint64_t active;
	uint64_t hover;
	/// Id of the actual item hovered on the current frame. We introduce a
	/// frame delay in order to correctly resolve interaction of overlapping
	/// elements.
	uint64_t next_hover;

	/// Layer of the item that most recently set next_hover.
	int layer;
	int next_hover_layer;

	struct gui_item_buffer buffer[GUI_LAYER_Size];

	/// Stack of container rects expressed in relative coordinates.
	struct gui_rect* layout_stack;  // stretchy
	/// Absolute pos of the top container of the layout stack.
	vec2 layout_top;
	/// Absolute pos of the next element.
	vec2 next_pos;

	// OpenGL resources
	GLuint program;
	GLuint uniform_buffer;
	GLuint vertex_array;
	GLuint vertex_buffer[2];  // 0: front, 1: back
};

#endif  // GUI_TYPE_H

A  => include/forge/gui_window.h +120 -0
@@ 1,120 @@
#ifndef GUI_WINDOW_H
#define GUI_WINDOW_H

#include <stdbool.h>
#include <stdint.h>

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

enum window_tile_split_axis {
	WINDOW_TILE_SPLIT_X = 0,
	WINDOW_TILE_SPLIT_Y = 1,
};

enum window_tile_side {
	WINDOW_TILE_LU = 0,  // Left/Up
	WINDOW_TILE_RD = 1,  // Right/Down
};

struct window {
	char const* name;
	void (*draw)(struct gui_context*);
};

struct window_tile_split {
	// Stored in-memory as absolute size as this makes a few operations
	// convenient. TODO(rycwo): Saved to disk as a ratio (closed range [0,1]) so
	// that changes in window/monitor size does not affect layout arrangement
	// on de-serialization.
	float size;
	int axis;
};

/*
 * Nodes are doubly linked.
 */
struct window_tile {
	union {
		struct {
			struct window_tile_split split;
			struct handle lhs;
			struct handle rhs;
		};
		struct window window;
	};
	struct handle parent;
	bool leaf;
};

struct window_manager {
	struct memory_pool nodes;
	struct handle root;
	struct handle active;  // TODO(rycwo)
};

void
init_window_manager(
		struct window_manager* wm,
		struct allocator* alloc,
		int max_window_tree_size);

void
terminate_window_manager(struct window_manager* wm);

/*
 * Insert root window.
 * It is an error to call this when there is already a root window.
 */
struct handle
insert_root_window(struct window_manager* wm, struct window window);

/*
 * Insert a window at the given tile.
 *
 * 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 handle
insert_window(
		struct window_manager* wm,
		struct handle node,
		struct window window,
		struct window_tile_split split,
		int side);

void
delete_window(struct window_manager* wm, struct handle node);

void
resize_window_viewport(struct window_manager* wm, vec2 const scale);

void
gui_windows(
		struct gui_context* context,
		struct window_manager* wm,
		struct gui_style const* style,
		float spacing);

void
gui_window_split(
		struct gui_context* context,
		uint64_t id,
		int axis,
		float length,
		float* pos);

void
begin_gui_window(
		struct gui_context* context,
		uint64_t id,
		struct gui_style const* style,
		vec2 const size);

void
end_gui_window(struct gui_context* context);

#endif  // GUI_WINDOW_H

A  => include/forge/hash_table.h +65 -0
@@ 1,65 @@
#ifndef HASH_TABLE_H
#define HASH_TABLE_H

#include <stdint.h>

#include "forge/collection.h"
#include "forge/memory.h"

#define HASH_TABLE_INIT_SIZE 32
#define HASH_TABLE_GROWTH_FACTOR 2
#define HASH_TABLE_MAX_LOAD_FACTOR 0.6f

/*
 * Allocate storage for the hash table.
 *
 * If unsure about the capacity, leave it at zero and the hash table will grow
 * automatically on insertion.
 */
void
alloc_hash_table(
		struct hash_table* table,
		struct allocator* alloc,
		int64_t initial_capacity);

/*
 * Deallocate storage for the hash table.
 */
void
free_hash_table(struct hash_table* table);

/*
 * Search for an entry in the hash table.
 */
int64_t
lookup_hash_table_entry(
		struct hash_table* table,
		uint64_t key,
		int64_t default_val);

/*
 * Insert key/value pair into the hash table.
 *
 * The table will dynamically grow when the load factor passes
 * HASH_TABLE_MAX_LOAD_FACTOR (default 0.6).
 *
 * Hash tables with linear probing will typically keep the threshold within the
 * range (0.5, 0.7). Lower values decrease cache locality, whilst higher values
 * may significantly decrease performance.
 */
void
insert_hash_table_entry(struct hash_table* table, uint64_t key, int64_t val);

/*
 * Remove a key/value pair from the hash table.
 */
void
delete_hash_table_entry(struct hash_table* table, uint64_t key);

/*
 * Hash string value to unsigned int64.
 */
uint64_t
hash_string(char const* val);

#endif  // HASH_TABLE_H

A  => include/forge/input.h +39 -0
@@ 1,39 @@
#ifndef INPUT_H
#define INPUT_H

enum mouse_button {
	MOUSE_BUTTON_NONE    = 0,
	MOUSE_BUTTON_LEFT    = 1,
	MOUSE_BUTTON_RIGHT   = 1 << 1,
	MOUSE_BUTTON_MIDDLE  = 1 << 2,
	MOUSE_BUTTON_EXTRA_0 = 1 << 3,
	MOUSE_BUTTON_EXTRA_1 = 1 << 4,
	MOUSE_BUTTON_EXTRA_2 = 1 << 5,
	MOUSE_BUTTON_EXTRA_3 = 1 << 6,
	MOUSE_BUTTON_EXTRA_4 = 1 << 7,
	MOUSE_BUTTON_EXTRA_5 = 1 << 8,
};

enum keyboard_key {
	KEY_NONE = 0,
	KEY_0,
	KEY_1,
	KEY_2,
	KEY_3,
	KEY_4,
	KEY_5,
	KEY_6,
	KEY_7,
	KEY_8,
	KEY_9,
	// TODO(rycwo)
};

enum keyboard_mod {
	MODIFIER_NONE  = 0,
	MODIFIER_CTRL  = 1,
	MODIFIER_ALT   = 1 << 1,
	MODIFIER_SHIFT = 1 << 2,
};

#endif  // INPUT_H

A  => include/forge/memory.h +11 -0
@@ 1,11 @@
#ifndef MEMORY_H
#define MEMORY_H

#include <stddef.h>

struct allocator {
	void* (*alloc)(size_t size);
	void (*free)(void* ptr);
};

#endif

A  => include/forge/memory_pool.h +76 -0
@@ 1,76 @@
#ifndef MEMORY_POOL_H
#define MEMORY_POOL_H

#include <stdbool.h>
#include <stdint.h>

#include "forge/collection.h"
#include "forge/memory.h"

/*
 * Pre-allocate memory pool buffers.
 */
void
alloc_memory_pool(
		struct memory_pool* pool,
		struct allocator* alloc,
		int32_t count,
		int32_t block_size);

/*
 * Free memory pool buffers.
 */
void
free_memory_pool(struct 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 alloc_memory_pool(). Instead it searches in constant
 * O(1) time for the next free block and returns a handle which can be used
 * with access_memory_pool_block() to retrieve a pointer to the raw memory.
 */
struct handle
alloc_memory_pool_block(struct memory_pool* pool);

/*
 * Return a single block to the memory pool.
 */
void
free_memory_pool_block(struct memory_pool* pool, struct handle handle);

/*
 * Return pointer to raw memory for the given block.
 *
 * Block memory will exactly match the block size initially provided to
 * 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.
 */
unsigned char*
access_memory_pool_block(struct memory_pool const* pool, struct handle handle);

/*
 * Check validity of pool handle.
 *
 * Handles are similar to weak pointers - they do not own the data and they may
 * become invalid over course of the program lifetime.
 */
bool
handle_valid(struct memory_pool const* pool, struct handle handle);

/*
 * Reduce fragmentation of both occupied and free items.
 *
 * This will often be called to improve cache efficiency when processing
 * memory pool items directly. Occupied blocks are guaranteed to sit in
 * contiguous memory when the process is complete. Takes O(n) time to
 * complete, where n is the size of the pool.
 */
void
compact_memory_pool(struct memory_pool* pool);

#endif  // MEMORY_POOL_H

A  => include/forge/murmur_hash.h +12 -0
@@ 1,12 @@
#ifndef MURMUR_HASH_H
#define MURMUR_HASH_H

#include <stdint.h>

/*
 * 64-bit general purpose non-cryptographic hash for 64-bit platforms.
 * https://github.com/aappleby/smhasher
 */
uint64_t murmur_hash_64(void const* key, int len, uint64_t seed);

#endif  // MURMUR_HASH_H

A  => include/forge/pcg_basic.h +78 -0
@@ 1,78 @@
/*
 * PCG Random Number Generation for C.
 *
 * Copyright 2014 Melissa O'Neill <oneill@pcg-random.org>
 *
 * 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.
 *
 * For additional information about the PCG random number generation scheme,
 * including its license and other licensing options, visit
 *
 *     http://www.pcg-random.org
 */

/*
 * This code is derived from the full C implementation, which is in turn
 * derived from the canonical C++ PCG implementation. The C++ version
 * has many additional features and is preferable if you can use C++ in
 * your project.
 */

#ifndef PCG_BASIC_H_INCLUDED
#define PCG_BASIC_H_INCLUDED 1

#include <inttypes.h>

#if __cplusplus
extern "C" {
#endif

struct pcg_state_setseq_64 {    // Internals are *Private*.
    uint64_t state;             // RNG state.  All values are possible.
    uint64_t inc;               // Controls which RNG sequence (stream) is
                                // selected. Must *always* be odd.
};
typedef struct pcg_state_setseq_64 pcg32_random_t;

// If you *must* statically initialize it, here's one.

#define PCG32_INITIALIZER   { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL }

// pcg32_srandom(initstate, initseq)
// pcg32_srandom_r(rng, initstate, initseq):
//     Seed the rng.  Specified in two parts, state initializer and a
//     sequence selection constant (a.k.a. stream id)

void pcg32_srandom(uint64_t initstate, uint64_t initseq);
void pcg32_srandom_r(pcg32_random_t* rng, uint64_t initstate,
                     uint64_t initseq);

// pcg32_random()
// pcg32_random_r(rng)
//     Generate a uniformly distributed 32-bit random number

uint32_t pcg32_random(void);
uint32_t pcg32_random_r(pcg32_random_t* rng);

// pcg32_boundedrand(bound):
// pcg32_boundedrand_r(rng, bound):
//     Generate a uniformly distributed number, r, where 0 <= r < bound

uint32_t pcg32_boundedrand(uint32_t bound);
uint32_t pcg32_boundedrand_r(pcg32_random_t* rng, uint32_t bound);

#if __cplusplus
}
#endif

#endif // PCG_BASIC_H_INCLUDED

A  => include/forge/seed.h +12 -0
@@ 1,12 @@
#ifndef SEED_H
#define SEED_H

#include <stdint.h>

/*
 * Compute a stable seed for the program runtime.
 * This takes time into account to generate some randomness.
 */
uint64_t seed(void);

#endif  // SEED_H

A  => include/forge/shader.h +30 -0
@@ 1,30 @@
#ifndef SHADER_H
#define SHADER_H

#include <stdbool.h>

#include <GL/glew.h>

#include "forge/memory.h"

struct shader_result {
	bool ok;
	char* log;
};

/*
 * Check shader compilation status.
 * The user is responsible for freeing log memory after use.
 */
struct shader_result
check_shader(GLuint shader, struct allocator* alloc);

/*
 * Check program link status.
 * The user is responsible for freeing log memory after use.
 */
struct shader_result
check_program(GLuint program, struct allocator* alloc);


#endif  // SHADER_H

A  => include/forge/stb_sprintf.h +1879 -0
@@ 1,1879 @@
// stb_sprintf - v1.08 - public domain snprintf() implementation
// originally by Jeff Roberts / RAD Game Tools, 2015/10/20
// http://github.com/nothings/stb
//
// allowed types:  sc uidBboXx p AaGgEef n
// lengths      :  hh h ll j z t I64 I32 I
//
// Contributors:
//    Fabian "ryg" Giesen (reformatting)
//
// Contributors (bugfixes):
//    github:d26435
//    github:trex78
//    github:account-login
//    Jari Komppa (SI suffixes)
//    Rohit Nirmal
//    Marcin Wojdyr
//    Leonard Ritter
//    Stefano Zanotti
//    Adam Allison
//    Arvid Gerstmann
//    Markus Kolb
//
// LICENSE:
//
//   See end of file for license information.

#ifndef STB_SPRINTF_H_INCLUDE
#define STB_SPRINTF_H_INCLUDE

/*
Single file sprintf replacement.

Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
Hereby placed in public domain.

This is a full sprintf replacement that supports everything that
the C runtime sprintfs support, including float/double, 64-bit integers,
hex floats, field parameters (%*.*d stuff), length reads backs, etc.

Why would you need this if sprintf already exists?  Well, first off,
it's *much* faster (see below). It's also much smaller than the CRT
versions code-space-wise. We've also added some simple improvements
that are super handy (commas in thousands, callbacks at buffer full,
for example). Finally, the format strings for MSVC and GCC differ
for 64-bit integers (among other small things), so this lets you use
the same format strings in cross platform code.

It uses the standard single file trick of being both the header file
and the source itself. If you just include it normally, you just get
the header file function definitions. To get the code, you include
it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.

It only uses va_args macros from the C runtime to do it's work. It
does cast doubles to S64s and shifts and divides U64s, which does
drag in CRT code on most platforms.

It compiles to roughly 8K with float support, and 4K without.
As a comparison, when using MSVC static libs, calling sprintf drags
in 16K.

API:
====
int stbsp_sprintf( char * buf, char const * fmt, ... )
int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
  Convert an arg list into a buffer.  stbsp_snprintf always returns
  a zero-terminated string (unlike regular snprintf).

int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
  Convert a va_list arg list into a buffer.  stbsp_vsnprintf always returns
  a zero-terminated string (unlike regular snprintf).

int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
    typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
  Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
  Your callback can then copy the chars out, print them or whatever.
  This function is actually the workhorse for everything else.
  The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
    // you return the next buffer to use or 0 to stop converting

void stbsp_set_separators( char comma, char period )
  Set the comma and period characters to use.

FLOATS/DOUBLES:
===============
This code uses a internal float->ascii conversion method that uses
doubles with error correction (double-doubles, for ~105 bits of
precision).  This conversion is round-trip perfect - that is, an atof
of the values output here will give you the bit-exact double back.

One difference is that our insignificant digits will be different than
with MSVC or GCC (but they don't match each other either).  We also
don't attempt to find the minimum length matching float (pre-MSVC15
doesn't either).

If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
and you'll save 4K of code space.

64-BIT INTS:
============
This library also supports 64-bit integers and you can use MSVC style or
GCC style indicators (%I64d or %lld).  It supports the C99 specifiers
for size_t and ptr_diff_t (%jd %zd) as well.

EXTRAS:
=======
Like some GCCs, for integers and floats, you can use a ' (single quote)
specifier and commas will be inserted on the thousands: "%'d" on 12345
would print 12,345.

For integers and floats, you can use a "$" specifier and the number
will be converted to float and then divided to get kilo, mega, giga or
tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is
"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn
2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three
$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the
suffix, add "_" specifier: "%_$d" -> "2.53M".

In addition to octal and hexadecimal conversions, you can print
integers in binary: "%b" for 256 would print 100.

PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
===================================================================
"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
"%24d" across all 32-bit ints (4.5x/4.2x faster)
"%x" across all 32-bit ints (4.5x/3.8x faster)
"%08x" across all 32-bit ints (4.3x/3.8x faster)
"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
"%f" for values near e-300 (7.9x/6.5x faster)
"%f" for values near e+300 (10.0x/9.1x faster)
"%e" for values near e-300 (10.1x/7.0x faster)
"%e" for values near e+300 (9.2x/6.0x faster)
"%.320f" for values near e-300 (12.6x/11.2x faster)
"%a" for random values (8.6x/4.3x faster)
"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
"%s%s%s" for 64 char strings (7.1x/7.3x faster)
"...512 char string..." ( 35.0x/32.5x faster!)
*/

#if defined(__clang__)
 #if defined(__has_feature) && defined(__has_attribute)
  #if __has_feature(address_sanitizer)
   #if __has_attribute(__no_sanitize__)
    #define STBSP__ASAN __attribute__((__no_sanitize__("address")))
   #elif __has_attribute(__no_sanitize_address__)
    #define STBSP__ASAN __attribute__((__no_sanitize_address__))
   #elif __has_attribute(__no_address_safety_analysis__)
    #define STBSP__ASAN __attribute__((__no_address_safety_analysis__))
   #endif
  #endif
 #endif
#elif __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)
 #if __SANITIZE_ADDRESS__
  #define STBSP__ASAN __attribute__((__no_sanitize_address__))
 #endif
#endif

#ifndef STBSP__ASAN
#define STBSP__ASAN
#endif

#ifdef STB_SPRINTF_STATIC
#define STBSP__PUBLICDEC static
#define STBSP__PUBLICDEF static STBSP__ASAN
#else
#ifdef __cplusplus
#define STBSP__PUBLICDEC extern "C"
#define STBSP__PUBLICDEF extern "C" STBSP__ASAN
#else
#define STBSP__PUBLICDEC extern
#define STBSP__PUBLICDEF STBSP__ASAN
#endif
#endif

#include <stdarg.h> // for va_list()
#include <stddef.h> // size_t, ptrdiff_t

#ifndef STB_SPRINTF_MIN
#define STB_SPRINTF_MIN 512 // how many characters per callback
#endif
typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);

#ifndef STB_SPRINTF_DECORATE
#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
#endif

STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...);
STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...);

STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);

#endif // STB_SPRINTF_H_INCLUDE

#ifdef STB_SPRINTF_IMPLEMENTATION

#include <stdlib.h> // for va_arg()

#define stbsp__uint32 unsigned int
#define stbsp__int32 signed int

#ifdef _MSC_VER
#define stbsp__uint64 unsigned __int64
#define stbsp__int64 signed __int64
#else
#define stbsp__uint64 unsigned long long
#define stbsp__int64 signed long long
#endif
#define stbsp__uint16 unsigned short

#ifndef stbsp__uintptr
#if defined(__ppc64__) || defined(__powerpc64__) || defined(__aarch64__) || defined(_M_X64) || defined(__x86_64__) || defined(__x86_64)
#define stbsp__uintptr stbsp__uint64
#else
#define stbsp__uintptr stbsp__uint32
#endif
#endif

#ifndef STB_SPRINTF_MSVC_MODE // used for MSVC2013 and earlier (MSVC2015 matches GCC)
#if defined(_MSC_VER) && (_MSC_VER < 1900)
#define STB_SPRINTF_MSVC_MODE
#endif
#endif

#ifdef STB_SPRINTF_NOUNALIGNED // define this before inclusion to force stbsp_sprintf to always use aligned accesses
#define STBSP__UNALIGNED(code)
#else
#define STBSP__UNALIGNED(code) code
#endif

#ifndef STB_SPRINTF_NOFLOAT
// internal float utility functions
static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits);
static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value);
#define STBSP__SPECIAL 0x7000
#endif

static char stbsp__period = '.';
static char stbsp__comma = ',';
static struct
{
   short temp; // force next field to be 2-byte aligned
   char pair[201];
} stbsp__digitpair =
{
  0,
   "00010203040506070809101112131415161718192021222324"
   "25262728293031323334353637383940414243444546474849"
   "50515253545556575859606162636465666768697071727374"
   "75767778798081828384858687888990919293949596979899"
};

STBSP__PUBLICDEF void STB_SPRINTF_DECORATE(set_separators)(char pcomma, char pperiod)
{
   stbsp__period = pperiod;
   stbsp__comma = pcomma;
}

#define STBSP__LEFTJUST 1
#define STBSP__LEADINGPLUS 2
#define STBSP__LEADINGSPACE 4
#define STBSP__LEADING_0X 8
#define STBSP__LEADINGZERO 16
#define STBSP__INTMAX 32
#define STBSP__TRIPLET_COMMA 64
#define STBSP__NEGATIVE 128
#define STBSP__METRIC_SUFFIX 256
#define STBSP__HALFWIDTH 512
#define STBSP__METRIC_NOSPACE 1024
#define STBSP__METRIC_1024 2048
#define STBSP__METRIC_JEDEC 4096

static void stbsp__lead_sign(stbsp__uint32 fl, char *sign)
{
   sign[0] = 0;
   if (fl & STBSP__NEGATIVE) {
      sign[0] = 1;
      sign[1] = '-';
   } else if (fl & STBSP__LEADINGSPACE) {
      sign[0] = 1;
      sign[1] = ' ';
   } else if (fl & STBSP__LEADINGPLUS) {
      sign[0] = 1;
      sign[1] = '+';
   }
}

STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va)
{
   static char hex[] = "0123456789abcdefxp";
   static char hexu[] = "0123456789ABCDEFXP";
   char *bf;
   char const *f;
   int tlen = 0;

   bf = buf;
   f = fmt;
   for (;;) {
      stbsp__int32 fw, pr, tz;
      stbsp__uint32 fl;

      // macros for the callback buffer stuff
      #define stbsp__chk_cb_bufL(bytes)                        \
         {                                                     \
            int len = (int)(bf - buf);                         \
            if ((len + (bytes)) >= STB_SPRINTF_MIN) {          \
               tlen += len;                                    \
               if (0 == (bf = buf = callback(buf, user, len))) \
                  goto done;                                   \
            }                                                  \
         }
      #define stbsp__chk_cb_buf(bytes)    \
         {                                \
            if (callback) {               \
               stbsp__chk_cb_bufL(bytes); \
            }                             \
         }
      #define stbsp__flush_cb()                      \
         {                                           \
            stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \
         } // flush if there is even one byte in the buffer
      #define stbsp__cb_buf_clamp(cl, v)                \
         cl = v;                                        \
         if (callback) {                                \
            int lg = STB_SPRINTF_MIN - (int)(bf - buf); \
            if (cl > lg)                                \
               cl = lg;                                 \
         }

      // fast copy everything up to the next % (or end of string)
      for (;;) {
         while (((stbsp__uintptr)f) & 3) {
         schk1:
            if (f[0] == '%')
               goto scandd;
         schk2:
            if (f[0] == 0)
               goto endfmt;
            stbsp__chk_cb_buf(1);
            *bf++ = f[0];
            ++f;
         }
         for (;;) {
            // Check if the next 4 bytes contain %(0x25) or end of string.
            // Using the 'hasless' trick:
            // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord
            stbsp__uint32 v, c;
            v = *(stbsp__uint32 *)f;
            c = (~v) & 0x80808080;
            if (((v ^ 0x25252525) - 0x01010101) & c)
               goto schk1;
            if ((v - 0x01010101) & c)
               goto schk2;
            if (callback)
               if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4)
                  goto schk1;
            #ifdef STB_SPRINTF_NOUNALIGNED
                if(((stbsp__uintptr)bf) & 3) {
                    bf[0] = f[0];
                    bf[1] = f[1];
                    bf[2] = f[2];
                    bf[3] = f[3];
                } else
            #endif
            {
                *(stbsp__uint32 *)bf = v;
            }
            bf += 4;
            f += 4;
         }
      }
   scandd:

      ++f;

      // ok, we have a percent, read the modifiers first
      fw = 0;
      pr = -1;
      fl = 0;
      tz = 0;

      // flags
      for (;;) {
         switch (f[0]) {
         // if we have left justify
         case '-':
            fl |= STBSP__LEFTJUST;
            ++f;
            continue;
         // if we have leading plus
         case '+':
            fl |= STBSP__LEADINGPLUS;
            ++f;
            continue;
         // if we have leading space
         case ' ':
            fl |= STBSP__LEADINGSPACE;
            ++f;
            continue;
         // if we have leading 0x
         case '#':
            fl |= STBSP__LEADING_0X;
            ++f;
            continue;
         // if we have thousand commas
         case '\'':
            fl |= STBSP__TRIPLET_COMMA;
            ++f;
            continue;
         // if we have kilo marker (none->kilo->kibi->jedec)
         case '$':
            if (fl & STBSP__METRIC_SUFFIX) {
               if (fl & STBSP__METRIC_1024) {
                  fl |= STBSP__METRIC_JEDEC;
               } else {
                  fl |= STBSP__METRIC_1024;
               }
            } else {
               fl |= STBSP__METRIC_SUFFIX;
            }
            ++f;
            continue;
         // if we don't want space between metric suffix and number
         case '_':
            fl |= STBSP__METRIC_NOSPACE;
            ++f;
            continue;
         // if we have leading zero
         case '0':
            fl |= STBSP__LEADINGZERO;
            ++f;
            goto flags_done;
         default: goto flags_done;
         }
      }
   flags_done:

      // get the field width
      if (f[0] == '*') {
         fw = va_arg(va, stbsp__uint32);
         ++f;
      } else {
         while ((f[0] >= '0') && (f[0] <= '9')) {
            fw = fw * 10 + f[0] - '0';
            f++;
         }
      }
      // get the precision
      if (f[0] == '.') {
         ++f;
         if (f[0] == '*') {
            pr = va_arg(va, stbsp__uint32);
            ++f;
         } else {
            pr = 0;
            while ((f[0] >= '0') && (f[0] <= '9')) {
               pr = pr * 10 + f[0] - '0';
               f++;
            }
         }
      }

      // handle integer size overrides
      switch (f[0]) {
      // are we halfwidth?
      case 'h':
         fl |= STBSP__HALFWIDTH;
         ++f;
         if (f[0] == 'h')
            ++f;  // QUARTERWIDTH
         break;
      // are we 64-bit (unix style)
      case 'l':
         fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0);
         ++f;
         if (f[0] == 'l') {
            fl |= STBSP__INTMAX;
            ++f;
         }
         break;
      // are we 64-bit on intmax? (c99)
      case 'j':
         fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0;
         ++f;
         break;
      // are we 64-bit on size_t or ptrdiff_t? (c99)
      case 'z':
         fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;
         ++f;
         break;
      case 't':
         fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0;
         ++f;
         break;
      // are we 64-bit (msft style)
      case 'I':
         if ((f[1] == '6') && (f[2] == '4')) {
            fl |= STBSP__INTMAX;
            f += 3;
         } else if ((f[1] == '3') && (f[2] == '2')) {
            f += 3;
         } else {
            fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0);
            ++f;
         }
         break;
      default: break;
      }

      // handle each replacement
      switch (f[0]) {
         #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307
         char num[STBSP__NUMSZ];
         char lead[8];
         char tail[8];
         char *s;
         char const *h;
         stbsp__uint32 l, n, cs;
         stbsp__uint64 n64;
#ifndef STB_SPRINTF_NOFLOAT
         double fv;
#endif
         stbsp__int32 dp;
         char const *sn;

      case 's':
         // get the string
         s = va_arg(va, char *);
         if (s == 0)
            s = (char *)"null";
         // get the length
         sn = s;
         for (;;) {
            if ((((stbsp__uintptr)sn) & 3) == 0)
               break;
         lchk:
            if (sn[0] == 0)
               goto ld;
            ++sn;
         }
         n = 0xffffffff;
         if (pr >= 0) {
            n = (stbsp__uint32)(sn - s);
            if (n >= (stbsp__uint32)pr)
               goto ld;
            n = ((stbsp__uint32)(pr - n)) >> 2;
         }
         while (n) {
            stbsp__uint32 v = *(stbsp__uint32 *)sn;
            if ((v - 0x01010101) & (~v) & 0x80808080UL)
               goto lchk;
            sn += 4;
            --n;
         }
         goto lchk;
      ld:

         l = (stbsp__uint32)(sn - s);
         // clamp to precision
         if (l > (stbsp__uint32)pr)
            l = pr;
         lead[0] = 0;
         tail[0] = 0;
         pr = 0;
         dp = 0;
         cs = 0;
         // copy the string in
         goto scopy;

      case 'c': // char
         // get the character
         s = num + STBSP__NUMSZ - 1;
         *s = (char)va_arg(va, int);
         l = 1;
         lead[0] = 0;
         tail[0] = 0;
         pr = 0;
         dp = 0;
         cs = 0;
         goto scopy;

      case 'n': // weird write-bytes specifier
      {
         int *d = va_arg(va, int *);
         *d = tlen + (int)(bf - buf);
      } break;

#ifdef STB_SPRINTF_NOFLOAT
      case 'A':              // float
      case 'a':              // hex float
      case 'G':              // float
      case 'g':              // float
      case 'E':              // float
      case 'e':              // float
      case 'f':              // float
         va_arg(va, double); // eat it
         s = (char *)"No float";
         l = 8;
         lead[0] = 0;
         tail[0] = 0;
         pr = 0;
         dp = 0;
         cs = 0;
         goto scopy;
#else
      case 'A': // hex float
      case 'a': // hex float
         h = (f[0] == 'A') ? hexu : hex;
         fv = va_arg(va, double);
         if (pr == -1)
            pr = 6; // default is 6
         // read the double into a string
         if (stbsp__real_to_parts((stbsp__int64 *)&n64, &dp, fv))
            fl |= STBSP__NEGATIVE;

         s = num + 64;

         stbsp__lead_sign(fl, lead);

         if (dp == -1023)
            dp = (n64) ? -1022 : 0;
         else
            n64 |= (((stbsp__uint64)1) << 52);
         n64 <<= (64 - 56);
         if (pr < 15)
            n64 += ((((stbsp__uint64)8) << 56) >> (pr * 4));
// add leading chars

#ifdef STB_SPRINTF_MSVC_MODE
         *s++ = '0';
         *s++ = 'x';
#else
         lead[1 + lead[0]] = '0';
         lead[2 + lead[0]] = 'x';
         lead[0] += 2;
#endif
         *s++ = h[(n64 >> 60) & 15];
         n64 <<= 4;
         if (pr)
            *s++ = stbsp__period;
         sn = s;

         // print the bits
         n = pr;
         if (n > 13)
            n = 13;
         if (pr > (stbsp__int32)n)
            tz = pr - n;
         pr = 0;
         while (n--) {
            *s++ = h[(n64 >> 60) & 15];
            n64 <<= 4;
         }

         // print the expo
         tail[1] = h[17];
         if (dp < 0) {
            tail[2] = '-';
            dp = -dp;
         } else
            tail[2] = '+';
         n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3));
         tail[0] = (char)n;
         for (;;) {
            tail[n] = '0' + dp % 10;
            if (n <= 3)
               break;
            --n;
            dp /= 10;
         }

         dp = (int)(s - sn);
         l = (int)(s - (num + 64));
         s = num + 64;
         cs = 1 + (3 << 24);
         goto scopy;

      case 'G': // float
      case 'g': // float
         h = (f[0] == 'G') ? hexu : hex;
         fv = va_arg(va, double);
         if (pr == -1)
            pr = 6;
         else if (pr == 0)
            pr = 1; // default is 6
         // read the double into a string
         if (stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000))
            fl |= STBSP__NEGATIVE;

         // clamp the precision and delete extra zeros after clamp
         n = pr;
         if (l > (stbsp__uint32)pr)
            l = pr;
         while ((l > 1) && (pr) && (sn[l - 1] == '0')) {
            --pr;
            --l;
         }

         // should we use %e
         if ((dp <= -4) || (dp > (stbsp__int32)n)) {
            if (pr > (stbsp__int32)l)
               pr = l - 1;
            else if (pr)
               --pr; // when using %e, there is one digit before the decimal
            goto doexpfromg;
         }
         // this is the insane action to get the pr to match %g semantics for %f
         if (dp > 0) {
            pr = (dp < (stbsp__int32)l) ? l - dp : 0;
         } else {
            pr = -dp + ((pr > (stbsp__int32)l) ? (stbsp__int32) l : pr);
         }
         goto dofloatfromg;

      case 'E': // float
      case 'e': // float
         h = (f[0] == 'E') ? hexu : hex;
         fv = va_arg(va, double);
         if (pr == -1)
            pr = 6; // default is 6
         // read the double into a string
         if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000))
            fl |= STBSP__NEGATIVE;
      doexpfromg:
         tail[0] = 0;
         stbsp__lead_sign(fl, lead);
         if (dp == STBSP__SPECIAL) {
            s = (char *)sn;
            cs = 0;
            pr = 0;
            goto scopy;
         }
         s = num + 64;
         // handle leading chars
         *s++ = sn[0];

         if (pr)
            *s++ = stbsp__period;

         // handle after decimal
         if ((l - 1) > (stbsp__uint32)pr)
            l = pr + 1;
         for (n = 1; n < l; n++)
            *s++ = sn[n];
         // trailing zeros
         tz = pr - (l - 1);
         pr = 0;
         // dump expo
         tail[1] = h[0xe];
         dp -= 1;
         if (dp < 0) {
            tail[2] = '-';
            dp = -dp;
         } else
            tail[2] = '+';
#ifdef STB_SPRINTF_MSVC_MODE
         n = 5;
#else
         n = (dp >= 100) ? 5 : 4;
#endif
         tail[0] = (char)n;
         for (;;) {
            tail[n] = '0' + dp % 10;
            if (n <= 3)
               break;
            --n;
            dp /= 10;
         }
         cs = 1 + (3 << 24); // how many tens
         goto flt_lead;

      case 'f': // float
         fv = va_arg(va, double);
      doafloat:
         // do kilos
         if (fl & STBSP__METRIC_SUFFIX) {
            double divisor;
            divisor = 1000.0f;
            if (fl & STBSP__METRIC_1024)
               divisor = 1024.0;
            while (fl < 0x4000000) {
               if ((fv < divisor) && (fv > -divisor))
                  break;
               fv /= divisor;
               fl += 0x1000000;
            }
         }
         if (pr == -1)
            pr = 6; // default is 6
         // read the double into a string
         if (stbsp__real_to_str(&sn, &l, num, &dp, fv, pr))
            fl |= STBSP__NEGATIVE;
      dofloatfromg:
         tail[0] = 0;
         stbsp__lead_sign(fl, lead);
         if (dp == STBSP__SPECIAL) {
            s = (char *)sn;
            cs = 0;
            pr = 0;
            goto scopy;
         }
         s = num + 64;

         // handle the three decimal varieties
         if (dp <= 0) {
            stbsp__int32 i;
            // handle 0.000*000xxxx
            *s++ = '0';
            if (pr)
               *s++ = stbsp__period;
            n = -dp;
            if ((stbsp__int32)n > pr)
               n = pr;
            i = n;
            while (i) {
               if ((((stbsp__uintptr)s) & 3) == 0)
                  break;
               *s++ = '0';
               --i;
            }
            while (i >= 4) {
               *(stbsp__uint32 *)s = 0x30303030;
               s += 4;
               i -= 4;
            }
            while (i) {
               *s++ = '0';
               --i;
            }
            if ((stbsp__int32)(l + n) > pr)
               l = pr - n;
            i = l;
            while (i) {
               *s++ = *sn++;
               --i;
            }
            tz = pr - (n + l);
            cs = 1 + (3 << 24); // how many tens did we write (for commas below)
         } else {
            cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (stbsp__uint32)dp) % 3) : 0;
            if ((stbsp__uint32)dp >= l) {
               // handle xxxx000*000.0
               n = 0;
               for (;;) {
                  if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
                     cs = 0;
                     *s++ = stbsp__comma;
                  } else {
                     *s++ = sn[n];
                     ++n;
                     if (n >= l)
                        break;
                  }
               }
               if (n < (stbsp__uint32)dp) {
                  n = dp - n;
                  if ((fl & STBSP__TRIPLET_COMMA) == 0) {
                     while (n) {
                        if ((((stbsp__uintptr)s) & 3) == 0)
                           break;
                        *s++ = '0';
                        --n;
                     }
                     while (n >= 4) {
                        *(stbsp__uint32 *)s = 0x30303030;
                        s += 4;
                        n -= 4;
                     }
                  }
                  while (n) {
                     if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
                        cs = 0;
                        *s++ = stbsp__comma;
                     } else {
                        *s++ = '0';
                        --n;
                     }
                  }
               }
               cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens
               if (pr) {
                  *s++ = stbsp__period;
                  tz = pr;
               }
            } else {
               // handle xxxxx.xxxx000*000
               n = 0;
               for (;;) {
                  if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) {
                     cs = 0;
                     *s++ = stbsp__comma;
                  } else {
                     *s++ = sn[n];
                     ++n;
                     if (n >= (stbsp__uint32)dp)
                        break;
                  }
               }
               cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens
               if (pr)
                  *s++ = stbsp__period;
               if ((l - dp) > (stbsp__uint32)pr)
                  l = pr + dp;
               while (n < l) {
                  *s++ = sn[n];
                  ++n;
               }
               tz = pr - (l - dp);
            }
         }
         pr = 0;

         // handle k,m,g,t
         if (fl & STBSP__METRIC_SUFFIX) {
            char idx;
            idx = 1;
            if (fl & STBSP__METRIC_NOSPACE)
               idx = 0;
            tail[0] = idx;
            tail[1] = ' ';
            {
               if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'.
                  if (fl & STBSP__METRIC_1024)
                     tail[idx + 1] = "_KMGT"[fl >> 24];
                  else
                     tail[idx + 1] = "_kMGT"[fl >> 24];
                  idx++;
                  // If printing kibits and not in jedec, add the 'i'.
                  if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) {
                     tail[idx + 1] = 'i';
                     idx++;
                  }
                  tail[0] = idx;
               }
            }
         };

      flt_lead:
         // get the length that we copied
         l = (stbsp__uint32)(s - (num + 64));
         s = num + 64;
         goto scopy;
#endif

      case 'B': // upper binary
      case 'b': // lower binary
         h = (f[0] == 'B') ? hexu : hex;
         lead[0] = 0;
         if (fl & STBSP__LEADING_0X) {
            lead[0] = 2;
            lead[1] = '0';
            lead[2] = h[0xb];
         }
         l = (8 << 4) | (1 << 8);
         goto radixnum;

      case 'o': // octal
         h = hexu;
         lead[0] = 0;
         if (fl & STBSP__LEADING_0X) {
            lead[0] = 1;
            lead[1] = '0';
         }
         l = (3 << 4) | (3 << 8);
         goto radixnum;

      case 'p': // pointer
         fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0;
         pr = sizeof(void *) * 2;
         fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros
                                    // fall through - to X

      case 'X': // upper hex
      case 'x': // lower hex
         h = (f[0] == 'X') ? hexu : hex;
         l = (4 << 4) | (4 << 8);
         lead[0] = 0;
         if (fl & STBSP__LEADING_0X) {
            lead[0] = 2;
            lead[1] = '0';
            lead[2] = h[16];
         }
      radixnum:
         // get the number
         if (fl & STBSP__INTMAX)
            n64 = va_arg(va, stbsp__uint64);
         else
            n64 = va_arg(va, stbsp__uint32);

         s = num + STBSP__NUMSZ;
         dp = 0;
         // clear tail, and clear leading if value is zero
         tail[0] = 0;
         if (n64 == 0) {
            lead[0] = 0;
            if (pr == 0) {
               l = 0;
               cs = (((l >> 4) & 15)) << 24;
               goto scopy;
            }
         }
         // convert to string
         for (;;) {
            *--s = h[n64 & ((1 << (l >> 8)) - 1)];
            n64 >>= (l >> 8);
            if (!((n64) || ((stbsp__int32)((num + STBSP__NUMSZ) - s) < pr)))
               break;
            if (fl & STBSP__TRIPLET_COMMA) {
               ++l;
               if ((l & 15) == ((l >> 4) & 15)) {
                  l &= ~15;
                  *--s = stbsp__comma;
               }
            }
         };
         // get the tens and the comma pos
         cs = (stbsp__uint32)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24);
         // get the length that we copied
         l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);
         // copy it
         goto scopy;

      case 'u': // unsigned
      case 'i':
      case 'd': // integer
         // get the integer and abs it
         if (fl & STBSP__INTMAX) {
            stbsp__int64 i64 = va_arg(va, stbsp__int64);
            n64 = (stbsp__uint64)i64;
            if ((f[0] != 'u') && (i64 < 0)) {
               n64 = (stbsp__uint64)-i64;
               fl |= STBSP__NEGATIVE;
            }
         } else {
            stbsp__int32 i = va_arg(va, stbsp__int32);
            n64 = (stbsp__uint32)i;
            if ((f[0] != 'u') && (i < 0)) {
               n64 = (stbsp__uint32)-i;
               fl |= STBSP__NEGATIVE;
            }
         }

#ifndef STB_SPRINTF_NOFLOAT
         if (fl & STBSP__METRIC_SUFFIX) {
            if (n64 < 1024)
               pr = 0;
            else if (pr == -1)
               pr = 1;
            fv = (double)(stbsp__int64)n64;
            goto doafloat;
         }
#endif

         // convert to string
         s = num + STBSP__NUMSZ;
         l = 0;

         for (;;) {
            // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators)
            char *o = s - 8;
            if (n64 >= 100000000) {
               n = (stbsp__uint32)(n64 % 100000000);
               n64 /= 100000000;
            } else {
               n = (stbsp__uint32)n64;
               n64 = 0;
            }
            if ((fl & STBSP__TRIPLET_COMMA) == 0) {
               do {
                  s -= 2;
                  *(stbsp__uint16 *)s = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];
                  n /= 100;
               } while (n);
            }
            while (n) {
               if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {
                  l = 0;
                  *--s = stbsp__comma;
                  --o;
               } else {
                  *--s = (char)(n % 10) + '0';
                  n /= 10;
               }
            }
            if (n64 == 0) {
               if ((s[0] == '0') && (s != (num + STBSP__NUMSZ)))
                  ++s;
               break;
            }
            while (s != o)
               if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) {
                  l = 0;
                  *--s = stbsp__comma;
                  --o;
               } else {
                  *--s = '0';
               }
         }

         tail[0] = 0;
         stbsp__lead_sign(fl, lead);

         // get the length that we copied
         l = (stbsp__uint32)((num + STBSP__NUMSZ) - s);
         if (l == 0) {
            *--s = '0';
            l = 1;
         }
         cs = l + (3 << 24);
         if (pr < 0)
            pr = 0;

      scopy:
         // get fw=leading/trailing space, pr=leading zeros
         if (pr < (stbsp__int32)l)
            pr = l;
         n = pr + lead[0] + tail[0] + tz;
         if (fw < (stbsp__int32)n)
            fw = n;
         fw -= n;
         pr -= l;

         // handle right justify and leading zeros
         if ((fl & STBSP__LEFTJUST) == 0) {
            if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr
            {
               pr = (fw > pr) ? fw : pr;
               fw = 0;
            } else {
               fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas
            }
         }

         // copy the spaces and/or zeros
         if (fw + pr) {
            stbsp__int32 i;
            stbsp__uint32 c;

            // copy leading spaces (or when doing %8.4d stuff)
            if ((fl & STBSP__LEFTJUST) == 0)
               while (fw > 0) {
                  stbsp__cb_buf_clamp(i, fw);
                  fw -= i;
                  while (i) {
                     if ((((stbsp__uintptr)bf) & 3) == 0)
                        break;
                     *bf++ = ' ';
                     --i;
                  }
                  while (i >= 4) {
                     *(stbsp__uint32 *)bf = 0x20202020;
                     bf += 4;
                     i -= 4;
                  }
                  while (i) {
                     *bf++ = ' ';
                     --i;
                  }
                  stbsp__chk_cb_buf(1);
               }

            // copy leader
            sn = lead + 1;
            while (lead[0]) {
               stbsp__cb_buf_clamp(i, lead[0]);
               lead[0] -= (char)i;
               while (i) {
                  *bf++ = *sn++;
                  --i;
               }
               stbsp__chk_cb_buf(1);
            }

            // copy leading zeros
            c = cs >> 24;
            cs &= 0xffffff;
            cs = (fl & STBSP__TRIPLET_COMMA) ? ((stbsp__uint32)(c - ((pr + cs) % (c + 1)))) : 0;
            while (pr > 0) {
               stbsp__cb_buf_clamp(i, pr);
               pr -= i;
               if ((fl & STBSP__TRIPLET_COMMA) == 0) {
                  while (i) {
                     if ((((stbsp__uintptr)bf) & 3) == 0)
                        break;
                     *bf++ = '0';
                     --i;
                  }
                  while (i >= 4) {
                     *(stbsp__uint32 *)bf = 0x30303030;
                     bf += 4;
                     i -= 4;
                  }
               }
               while (i) {
                  if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) {
                     cs = 0;
                     *bf++ = stbsp__comma;
                  } else
                     *bf++ = '0';
                  --i;
               }
               stbsp__chk_cb_buf(1);
            }
         }

         // copy leader if there is still one
         sn = lead + 1;
         while (lead[0]) {
            stbsp__int32 i;
            stbsp__cb_buf_clamp(i, lead[0]);
            lead[0] -= (char)i;
            while (i) {
               *bf++ = *sn++;
               --i;
            }
            stbsp__chk_cb_buf(1);
         }

         // copy the string
         n = l;
         while (n) {
            stbsp__int32 i;
            stbsp__cb_buf_clamp(i, n);
            n -= i;
            STBSP__UNALIGNED(while (i >= 4) {
               *(stbsp__uint32 *)bf = *(stbsp__uint32 *)s;
               bf += 4;
               s += 4;
               i -= 4;
            })
            while (i) {
               *bf++ = *s++;
               --i;
            }
            stbsp__chk_cb_buf(1);
         }

         // copy trailing zeros
         while (tz) {
            stbsp__int32 i;
            stbsp__cb_buf_clamp(i, tz);
            tz -= i;
            while (i) {
               if ((((stbsp__uintptr)bf) & 3) == 0)
                  break;
               *bf++ = '0';
               --i;
            }
            while (i >= 4) {
               *(stbsp__uint32 *)bf = 0x30303030;
               bf += 4;
               i -= 4;
            }
            while (i) {
               *bf++ = '0';
               --i;
            }
            stbsp__chk_cb_buf(1);
         }

         // copy tail if there is one
         sn = tail + 1;
         while (tail[0]) {
            stbsp__int32 i;
            stbsp__cb_buf_clamp(i, tail[0]);
            tail[0] -= (char)i;
            while (i) {
               *bf++ = *sn++;
               --i;
            }
            stbsp__chk_cb_buf(1);
         }

         // handle the left justify
         if (fl & STBSP__LEFTJUST)
            if (fw > 0) {
               while (fw) {
                  stbsp__int32 i;
                  stbsp__cb_buf_clamp(i, fw);
                  fw -= i;
                  while (i) {
                     if ((((stbsp__uintptr)bf) & 3) == 0)
                        break;
                     *bf++ = ' ';
                     --i;
                  }
                  while (i >= 4) {
                     *(stbsp__uint32 *)bf = 0x20202020;
                     bf += 4;
                     i -= 4;
                  }
                  while (i--)
                     *bf++ = ' ';
                  stbsp__chk_cb_buf(1);
               }
            }
         break;

      default: // unknown, just copy code
         s = num + STBSP__NUMSZ - 1;
         *s = f[0];
         l = 1;
         fw = fl = 0;
         lead[0] = 0;
         tail[0] = 0;
         pr = 0;
         dp = 0;
         cs = 0;
         goto scopy;
      }
      ++f;
   }
endfmt:

   if (!callback)
      *bf = 0;
   else
      stbsp__flush_cb();

done:
   return tlen + (int)(bf - buf);
}

// cleanup
#undef STBSP__LEFTJUST
#undef STBSP__LEADINGPLUS
#undef STBSP__LEADINGSPACE
#undef STBSP__LEADING_0X
#undef STBSP__LEADINGZERO
#undef STBSP__INTMAX
#undef STBSP__TRIPLET_COMMA
#undef STBSP__NEGATIVE
#undef STBSP__METRIC_SUFFIX
#undef STBSP__NUMSZ
#undef stbsp__chk_cb_bufL
#undef stbsp__chk_cb_buf
#undef stbsp__flush_cb
#undef stbsp__cb_buf_clamp

// ============================================================================
//   wrapper functions

STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...)
{
   int result;
   va_list va;
   va_start(va, fmt);
   result = STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
   va_end(va);
   return result;
}

typedef struct stbsp__context {
   char *buf;
   int count;
   int length;
   char tmp[STB_SPRINTF_MIN];
} stbsp__context;

static char *stbsp__clamp_callback(const char *buf, void *user, int len)
{
   stbsp__context *c = (stbsp__context *)user;
   c->length += len;

   if (len > c->count)
      len = c->count;

   if (len) {
      if (buf != c->buf) {
         const char *s, *se;
         char *d;
         d = c->buf;
         s = buf;
         se = buf + len;
         do {
            *d++ = *s++;
         } while (s < se);
      }
      c->buf += len;
      c->count -= len;
   }

   if (c->count <= 0)
      return c->tmp;
   return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can
}

static char * stbsp__count_clamp_callback( const char * buf, void * user, int len )
{
   stbsp__context * c = (stbsp__context*)user;
   (void) sizeof(buf);

   c->length += len;
   return c->tmp; // go direct into buffer if you can
}

STBSP__PUBLICDEF int STB_SPRINTF_DECORATE( vsnprintf )( char * buf, int count, char const * fmt, va_list va )
{
   stbsp__context c;

   if ( (count == 0) && !buf )
   {
      c.length = 0;

      STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__count_clamp_callback, &c, c.tmp, fmt, va );
   }
   else
   {
      int l;

      c.buf = buf;
      c.count = count;
      c.length = 0;

      STB_SPRINTF_DECORATE( vsprintfcb )( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va );

      // zero-terminate
      l = (int)( c.buf - buf );
      if ( l >= count ) // should never be greater, only equal (or less) than count
         l = count - 1;
      buf[l] = 0;
   }

   return c.length;
}

STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...)
{
   int result;
   va_list va;
   va_start(va, fmt);

   result = STB_SPRINTF_DECORATE(vsnprintf)(buf, count, fmt, va);
   va_end(va);

   return result;
}

STBSP__PUBLICDEF int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va)
{
   return STB_SPRINTF_DECORATE(vsprintfcb)(0, 0, buf, fmt, va);
}

// =======================================================================
//   low level float utility functions

#ifndef STB_SPRINTF_NOFLOAT

// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox)
#define STBSP__COPYFP(dest, src)                   \
   {                                               \
      int cn;                                      \
      for (cn = 0; cn < 8; cn++)                   \
         ((char *)&dest)[cn] = ((char *)&src)[cn]; \
   }

// get float info
static stbsp__int32 stbsp__real_to_parts(stbsp__int64 *bits, stbsp__int32 *expo, double value)
{
   double d;
   stbsp__int64 b = 0;

   // load value and round at the frac_digits
   d = value;

   STBSP__COPYFP(b, d);

   *bits = b & ((((stbsp__uint64)1) << 52) - 1);
   *expo = (stbsp__int32)(((b >> 52) & 2047) - 1023);

   return (stbsp__int32)((stbsp__uint64) b >> 63);
}

static double const stbsp__bot[23] = {
   1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011,
   1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022
};
static double const stbsp__negbot[22] = {
   1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011,
   1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022
};
static double const stbsp__negboterr[22] = {
   -5.551115123125783e-018,  -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023,
   4.5251888174113739e-024,  -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028,  2.0113352370744385e-029,
   -3.0373745563400371e-030, 1.1806906454401013e-032,  -7.7705399876661076e-032, 2.0902213275965398e-033,  -7.1542424054621921e-034, -7.1542424054621926e-035,
   2.4754073164739869e-036,  5.4846728545790429e-037,  9.2462547772103625e-038,  -4.8596774326570872e-039
};
static double const stbsp__top[13] = {
   1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299
};
static double const stbsp__negtop[13] = {
   1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299
};
static double const stbsp__toperr[13] = {
   8388608,
   6.8601809640529717e+028,
   -7.253143638152921e+052,
   -4.3377296974619174e+075,
   -1.5559416129466825e+098,
   -3.2841562489204913e+121,
   -3.7745893248228135e+144,
   -1.7356668416969134e+167,
   -3.8893577551088374e+190,
   -9.9566444326005119e+213,
   6.3641293062232429e+236,
   -5.2069140800249813e+259,
   -5.2504760255204387e+282
};
static double const stbsp__negtoperr[13] = {
   3.9565301985100693e-040,  -2.299904345391321e-063,  3.6506201437945798e-086,  1.1875228833981544e-109,
   -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178,  -5.7778912386589953e-201,
   7.4997100559334532e-224,  -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293,
   8.0970921678014997e-317
};

#if defined(_MSC_VER) && (_MSC_VER <= 1200)
static stbsp__uint64 const stbsp__powten[20] = {
   1,
   10,
   100,
   1000,
   10000,
   100000,
   1000000,
   10000000,
   100000000,
   1000000000,
   10000000000,
   100000000000,
   1000000000000,
   10000000000000,
   100000000000000,
   1000000000000000,
   10000000000000000,
   100000000000000000,
   1000000000000000000,
   10000000000000000000U
};
#define stbsp__tento19th ((stbsp__uint64)1000000000000000000)
#else
static stbsp__uint64 const stbsp__powten[20] = {
   1,
   10,
   100,
   1000,
   10000,
   100000,
   1000000,
   10000000,
   100000000,
   1000000000,
   10000000000ULL,
   100000000000ULL,
   1000000000000ULL,
   10000000000000ULL,
   100000000000000ULL,
   1000000000000000ULL,
   10000000000000000ULL,
   100000000000000000ULL,
   1000000000000000000ULL,
   10000000000000000000ULL
};
#define stbsp__tento19th (1000000000000000000ULL)
#endif

#define stbsp__ddmulthi(oh, ol, xh, yh)                            \
   {                                                               \
      double ahi = 0, alo, bhi = 0, blo;                           \
      stbsp__int64 bt;                                             \
      oh = xh * yh;                                                \
      STBSP__COPYFP(bt, xh);                                       \
      bt &= ((~(stbsp__uint64)0) << 27);                           \
      STBSP__COPYFP(ahi, bt);                                      \
      alo = xh - ahi;                                              \
      STBSP__COPYFP(bt, yh);                                       \
      bt &= ((~(stbsp__uint64)0) << 27);                           \
      STBSP__COPYFP(bhi, bt);                                      \
      blo = yh - bhi;                                              \
      ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \
   }

#define stbsp__ddtoS64(ob, xh, xl)          \
   {                                        \
      double ahi = 0, alo, vh, t;           \
      ob = (stbsp__int64)ph;                \
      vh = (double)ob;                      \
      ahi = (xh - vh);                      \
      t = (ahi - xh);                       \
      alo = (xh - (ahi - t)) - (vh + t);    \
      ob += (stbsp__int64)(ahi + alo + xl); \
   }

#define stbsp__ddrenorm(oh, ol) \
   {                            \
      double s;                 \
      s = oh + ol;              \
      ol = ol - (s - oh);       \
      oh = s;                   \
   }

#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh);

#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl);

static void stbsp__raise_to_power10(double *ohi, double *olo, double d, stbsp__int32 power) // power can be -323 to +350
{
   double ph, pl;
   if ((power >= 0) && (power <= 22)) {
      stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]);
   } else {
      stbsp__int32 e, et, eb;
      double p2h, p2l;

      e = power;
      if (power < 0)
         e = -e;
      et = (e * 0x2c9) >> 14; /* %23 */
      if (et > 13)
         et = 13;
      eb = e - (et * 23);

      ph = d;
      pl = 0.0;
      if (power < 0) {
         if (eb) {
            --eb;
            stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]);
            stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]);
         }
         if (et) {
            stbsp__ddrenorm(ph, pl);
            --et;
            stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]);
            stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]);
            ph = p2h;
            pl = p2l;
         }
      } else {
         if (eb) {
            e = eb;
            if (eb > 22)
               eb = 22;
            e -= eb;
            stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]);
            if (e) {
               stbsp__ddrenorm(ph, pl);
               stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]);
               stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl);
               ph = p2h;
               pl = p2l;
            }
         }
         if (et) {
            stbsp__ddrenorm(ph, pl);
            --et;
            stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]);
            stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]);
            ph = p2h;
            pl = p2l;
         }
      }
   }
   stbsp__ddrenorm(ph, pl);
   *ohi = ph;
   *olo = pl;
}

// given a float value, returns the significant bits in bits, and the position of the
//   decimal point in decimal_pos.  +/-INF and NAN are specified by special values
//   returned in the decimal_pos parameter.
// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000
static stbsp__int32 stbsp__real_to_str(char const **start, stbsp__uint32 *len, char *out, stbsp__int32 *decimal_pos, double value, stbsp__uint32 frac_digits)
{
   double d;
   stbsp__int64 bits = 0;
   stbsp__int32 expo, e, ng, tens;

   d = value;
   STBSP__COPYFP(bits, d);
   expo = (stbsp__int32)((bits >> 52) & 2047);
   ng = (stbsp__int32)((stbsp__uint64) bits >> 63);
   if (ng)
      d = -d;

   if (expo == 2047) // is nan or inf?
   {
      *start = (bits & ((((stbsp__uint64)1) << 52) - 1)) ? "NaN" : "Inf";
      *decimal_pos = STBSP__SPECIAL;
      *len = 3;
      return ng;
   }

   if (expo == 0) // is zero or denormal
   {
      if (((stbsp__uint64) bits << 1) == 0) // do zero
      {
         *decimal_pos = 1;
         *start = out;
         out[0] = '0';
         *len = 1;
         return ng;
      }
      // find the right expo for denormals
      {
         stbsp__int64 v = ((stbsp__uint64)1) << 51;
         while ((bits & v) == 0) {
            --expo;
            v >>= 1;
         }
      }
   }

   // find the decimal exponent as well as the decimal bits of the value
   {
      double ph, pl;

      // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046
      tens = expo - 1023;
      tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1);

      // move the significant bits into position and stick them into an int
      stbsp__raise_to_power10(&ph, &pl, d, 18 - tens);

      // get full as much precision from double-double as possible
      stbsp__ddtoS64(bits, ph, pl);

      // check if we undershot
      if (((stbsp__uint64)bits) >= stbsp__tento19th)
         ++tens;
   }

   // now do the rounding in integer land
   frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits);
   if ((frac_digits < 24)) {
      stbsp__uint32 dg = 1;
      if ((stbsp__uint64)bits >= stbsp__powten[9])
         dg = 10;
      while ((stbsp__uint64)bits >= stbsp__powten[dg]) {
         ++dg;
         if (dg == 20)
            goto noround;
      }
      if (frac_digits < dg) {
         stbsp__uint64 r;
         // add 0.5 at the right position and round
         e = dg - frac_digits;
         if ((stbsp__uint32)e >= 24)
            goto noround;
         r = stbsp__powten[e];
         bits = bits + (r / 2);
         if ((stbsp__uint64)bits >= stbsp__powten[dg])
            ++tens;
         bits /= r;
      }
   noround:;
   }

   // kill long trailing runs of zeros
   if (bits) {
      stbsp__uint32 n;
      for (;;) {
         if (bits <= 0xffffffff)
            break;
         if (bits % 1000)
            goto donez;
         bits /= 1000;
      }
      n = (stbsp__uint32)bits;
      while ((n % 1000) == 0)
         n /= 1000;
      bits = n;
   donez:;
   }

   // convert to string
   out += 64;
   e = 0;
   for (;;) {
      stbsp__uint32 n;
      char *o = out - 8;
      // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned)
      if (bits >= 100000000) {
         n = (stbsp__uint32)(bits % 100000000);
         bits /= 100000000;
      } else {
         n = (stbsp__uint32)bits;
         bits = 0;
      }
      while (n) {
         out -= 2;
         *(stbsp__uint16 *)out = *(stbsp__uint16 *)&stbsp__digitpair.pair[(n % 100) * 2];
         n /= 100;
         e += 2;
      }
      if (bits == 0) {
         if ((e) && (out[0] == '0')) {
            ++out;
            --e;
         }
         break;
      }
      while (out != o) {
         *--out = '0';
         ++e;
      }
   }

   *decimal_pos = tens;
   *start = out;
   *len = e;
   return ng;
}

#undef stbsp__ddmulthi
#undef stbsp__ddrenorm
#undef stbsp__ddmultlo
#undef stbsp__ddmultlos
#undef STBSP__SPECIAL
#undef STBSP__COPYFP

#endif // STB_SPRINTF_NOFLOAT

// clean up
#undef stbsp__uint16
#undef stbsp__uint32
#undef stbsp__int32
#undef stbsp__uint64
#undef stbsp__int64
#undef STBSP__UNALIGNED

#endif // STB_SPRINTF_IMPLEMENTATION

/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

A  => include/forge/stretchy_buffer.h +271 -0
@@ 1,271 @@
// stretchy_buffer.h - v1.04 - public domain - nothings.org/stb
// a vector<>-like dynamic array for C
//
// With modifications added to enable stack-like behavior. Namely in the
// form of an additional function:
//
//     sb_pop(TYPE *a)            pop lvalue of the last item from the array
//
// version history:
//      1.04 -  fix warning
//      1.03 -  compile as C++ maybe
//      1.02 -  tweaks to syntax for no good reason
//      1.01 -  added a "common uses" documentation section
//      1.0  -  fixed bug in the version I posted prematurely
//      0.9  -  rewrite to try to avoid strict-aliasing optimization
//              issues, but won't compile as C++
//
// Will probably not work correctly with strict-aliasing optimizations.
//
// The idea:
//
//    This implements an approximation to C++ vector<> for C, in that it
//    provides a generic definition for dynamic arrays which you can
//    still access in a typesafe way using arr[i] or *(arr+i). However,
//    it is simply a convenience wrapper around the common idiom of
//    of keeping a set of variables (in a struct or globals) which store
//        - pointer to array
//        - the length of the "in-use" part of the array
//        - the current size of the allocated array
//
//    I find it to be the single most useful non-built-in-structure when
//    programming in C (hash tables a close second), but to be clear
//    it lacks many of the capabilities of C++ vector<>: there is no
//    range checking, the object address isn't stable (see next section
//    for details), the set of methods available is small (although
//    the file stb.h has another implementation of stretchy buffers
//    called 'stb_arr' which provides more methods, e.g. for insertion
//    and deletion).
//
// How to use:
//
//    Unlike other stb header file libraries, there is no need to
//    define an _IMPLEMENTATION symbol. Every #include creates as
//    much implementation is needed.
//
//    stretchy_buffer.h does not define any types, so you do not
//    need to #include it to before defining data types that are
//    stretchy buffers, only in files that *manipulate* stretchy
//    buffers.
//
//    If you want a stretchy buffer aka dynamic array containing
//    objects of TYPE, declare such an array as:
//
//       TYPE *myarray = NULL;
//
//    (There is no typesafe way to distinguish between stretchy
//    buffers and regular arrays/pointers; this is necessary to
//    make ordinary array indexing work on these objects.)
//
//    Unlike C++ vector<>, the stretchy_buffer has the same
//    semantics as an object that you manually malloc and realloc.
//    The pointer may relocate every time you add a new object
//    to it, so you:
//
//         1. can't take long-term pointers to elements of the array
//         2. have to return the pointer from functions which might expand it
//            (either as a return value or by storing it to a ptr-to-ptr)
//
//    Now you can do the following things with this array:
//
//         sb_free(TYPE *a)           free the array
//         sb_count(TYPE *a)          the number of elements in the array
//         sb_push(TYPE *a, TYPE v)   adds v on the end of the array, a la push_back
//         sb_add(TYPE *a, int n)     adds n uninitialized elements at end of array & returns pointer to first added
//         sb_last(TYPE *a)           returns an lvalue of the last item in the array
//         a[n]                       access the nth (counting from 0) element of the array
//
//     #define STRETCHY_BUFFER_NO_SHORT_NAMES to only export
//     names of the form 'stb_sb_' if you have a name that would
//     otherwise collide.
//
//     Note that these are all macros and many of them evaluate
//     their arguments more than once, so the arguments should
//     be side-effect-free.
//
//     Note that 'TYPE *a' in sb_push and sb_add must be lvalues
//     so that the library can overwrite the existing pointer if
//     the object has to be reallocated.
//
//     In an out-of-memory condition, the code will try to
//     set up a null-pointer or otherwise-invalid-pointer
//     exception to happen later. It's possible optimizing
//     compilers could detect this write-to-null statically
//     and optimize away some of the code, but it should only
//     be along the failure path. Nevertheless, for more security
//     in the face of such compilers, #define STRETCHY_BUFFER_OUT_OF_MEMORY
//     to a statement such as assert(0) or exit(1) or something
//     to force a failure when out-of-memory occurs.
//
// Common use:
//
//    The main application for this is when building a list of
//    things with an unknown quantity, either due to loading from
//    a file or through a process which produces an unpredictable
//    number.
//
//    My most common idiom is something like:
//
//       SomeStruct *arr = NULL;
//       while (something)
//       {
//          SomeStruct new_one;
//          new_one.whatever = whatever;
//          new_one.whatup   = whatup;
//          new_one.foobar   = barfoo;
//          sb_push(arr, new_one);
//       }
//
//    and various closely-related factorings of that. For example,
//    you might have several functions to create/init new SomeStructs,
//    and if you use the above idiom, you might prefer to make them
//    return structs rather than take non-const-pointers-to-structs,
//    so you can do things like:
//
//       SomeStruct *arr = NULL;
//       while (something)
//       {
//          if (case_A) {
//             sb_push(arr, some_func1());
//          } else if (case_B) {
//             sb_push(arr, some_func2());
//          } else {
//             sb_push(arr, some_func3());
//          }
//       }
//
//    Note that the above relies on the fact that sb_push doesn't
//    evaluate its second argument more than once. The macros do
//    evaluate the *array* argument multiple times, and numeric
//    arguments may be evaluated multiple times, but you can rely
//    on the second argument of sb_push being evaluated only once.
//
//    Of course, you don't have to store bare objects in the array;
//    if you need the objects to have stable pointers, store an array
//    of pointers instead:
//
//       SomeStruct **arr = NULL;
//       while (something)
//       {
//          SomeStruct *new_one = malloc(sizeof(*new_one));
//          new_one->whatever = whatever;
//          new_one->whatup   = whatup;
//          new_one->foobar   = barfoo;
//          sb_push(arr, new_one);
//       }
//
// How it works:
//
//    A long-standing tradition in things like malloc implementations
//    is to store extra data before the beginning of the block returned
//    to the user. The stretchy buffer implementation here uses the
//    same trick; the current-count and current-allocation-size are
//    stored before the beginning of the array returned to the user.
//    (This means you can't directly free() the pointer, because the
//    allocated pointer is different from the type-safe pointer provided
//    to the user.)
//
//    The details are trivial and implementation is straightforward;
//    the main trick is in realizing in the first place that it's
//    possible to do this in a generic, type-safe way in C.
//
// Contributors:
//
// Timothy Wright (github:ZenToad)
//
// LICENSE
//
//   See end of file for license information.

#ifndef STB_STRETCHY_BUFFER_H_INCLUDED
#define STB_STRETCHY_BUFFER_H_INCLUDED

#ifndef NO_STRETCHY_BUFFER_SHORT_NAMES
#define sb_free   stb_sb_free
#define sb_push   stb_sb_push
#define sb_pop    stb_sb_pop
#define sb_count  stb_sb_count
#define sb_add    stb_sb_add
#define sb_last   stb_sb_last
#endif

#define stb_sb_free(a)         ((a) ? free(stb__sbraw(a)),0 : 0)
#define stb_sb_push(a,v)       (stb__sbmaybegrow(a,1), (a)[stb__sbn(a)++] = (v))
// Note that this does not do anything to shrink free space
#define stb_sb_pop(a)        ((a)[--stb__sbn(a)])
#define stb_sb_count(a)        ((a) ? stb__sbn(a) : 0)
#define stb_sb_add(a,n)        (stb__sbmaybegrow(a,n), stb__sbn(a)+=(n), &(a)[stb__sbn(a)-(n)])
#define stb_sb_last(a)         ((a)[stb__sbn(a)-1])

#define stb__sbraw(a) ((int *) (void *) (a) - 2)
#define stb__sbm(a)   stb__sbraw(a)[0]
#define stb__sbn(a)   stb__sbraw(a)[1]

#define stb__sbneedgrow(a,n)  ((a)==0 || stb__sbn(a)+(n) >= stb__sbm(a))
#define stb__sbmaybegrow(a,n) (stb__sbneedgrow(a,(n)) ? stb__sbgrow(a,n) : 0)
#define stb__sbgrow(a,n)      (*((void **)&(a)) = stb__sbgrowf((a), (n), sizeof(*(a))))

#include <stdlib.h>

static void * stb__sbgrowf(void *arr, int increment, int itemsize)
{
   int dbl_cur = arr ? 2*stb__sbm(arr) : 0;
   int min_needed = stb_sb_count(arr) + increment;
   int m = dbl_cur > min_needed ? dbl_cur : min_needed;
   int *p = (int *) realloc(arr ? stb__sbraw(arr) : 0, itemsize * m + sizeof(int)*2);
   if (p) {
      if (!arr)
         p[1] = 0;
      p[0] = m;
      return p+2;
   } else {
      #ifdef STRETCHY_BUFFER_OUT_OF_MEMORY
      STRETCHY_BUFFER_OUT_OF_MEMORY ;
      #endif
      return (void *) (2*sizeof(int)); // try to force a NULL pointer exception later
   }
}
#endif // STB_STRETCHY_BUFFER_H_INCLUDED


/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

A  => include/forge/type.h +14 -0
@@ 1,14 @@
#ifndef TYPE_H
#define TYPE_H

typedef float scalar;

typedef scalar vec2[2];
typedef scalar vec3[3];
typedef scalar vec4[4];

typedef scalar mat2[2][2];
typedef scalar mat3[3][3];
typedef scalar mat4[4][4];

#endif  // TYPE_H

A  => include/forge/vector.h +244 -0
@@ 1,244 @@
/*
 * Helper functions for fixed-size vectors.
 *
 * void vecn_set(vecn out, scalar x, ...)
 * void vecn_copy(vecn out, vecn const a)
 * void vecn_add(vecn out, vecn const a, vecn const b)
 * void vecn_sub(vecn out, vecn const a, vecn const b)
 * void vecn_mul(vecn out, vecn const a, vecn const b)
 * void vecn_div(vecn out, vecn const a, vecn const b)
 * void vecn_min(vecn out, vecn const a, vecn const b)
 * void vecn_max(vecn out, vecn const a, vecn const b)
 * void vecn_scale(vecn out, vecn const a, scalar alpha)
 * scalar vecn_dot(vecn const a, vecn const b)
 * scalar vecn_2norm(vecn const a)
 *
 * TODO(rycwo): GLSL parity
 * TODO(rycwo): Swizzling macros
 * TODO(rycwo): Intrinsics
 */
#ifndef VECTOR_H
#define VECTOR_H

#include "forge/type.h"

static inline void
vec2_set(vec2 out, scalar x, scalar y) {
	out[0] = x;
	out[1] = y;
}

static inline void
vec2_copy(vec2 out, vec2 const a) {
	out[0] = a[0];
	out[1] = a[1];
}

static inline void
vec2_add(vec2 out, vec2 const a, vec2 const b) {
	out[0] = a[0] + b[0];
	out[1] = a[1] + b[1];
}

static inline void
vec2_sub(vec2 out, vec2 const a, vec2 const b) {
	out[0] = a[0] - b[0];
	out[1] = a[1] - b[1];
}

static inline void
vec2_mul(vec2 out, vec2 const a, vec2 const b) {
	out[0] = a[0] * b[0];
	out[1] = a[1] * b[1];
}

static inline void
vec2_div(vec2 out, vec2 const a, vec2 const b) {
	out[0] = a[0] / b[0];
	out[1] = a[1] / b[1];
}

static inline void
vec2_min(vec2 out, vec2 const a, vec2 const b) {
	out[0] = (a[0] < b[0]) ? a[0] : b[0];
	out[1] = (a[1] < b[1]) ? a[1] : b[1];
}

static inline void
vec2_max(vec2 out, vec2 const a, vec2 const b) {
	out[0] = (a[0] > b[0]) ? a[0] : b[0];
	out[1] = (a[1] > b[1]) ? a[1] : b[1];
}

static inline void
vec2_scale(vec2 out, vec2 const a, scalar alpha) {
	out[0] = alpha * a[0];
	out[1] = alpha * a[1];
}

static inline scalar
vec2_dot(vec2 const a, vec2 const b) {
	return a[0] * b[0] + a[1] * b[1];
}

static inline scalar
vec2_2norm(vec2 const a) {
	return a[0] * a[0] + a[1] * a[1];
}

static inline void
vec3_set(vec3 out, scalar x, scalar y, scalar z) {
	out[0] = x;
	out[1] = y;
	out[2] = z;
}

static inline void
vec3_copy(vec3 out, vec3 const a) {
	out[0] = a[0];
	out[1] = a[1];
	out[2] = a[2];
}

static inline void
vec3_add(vec3 out, vec3 const a, vec3 const b) {
	out[0] = a[0] + b[0];
	out[1] = a[1] + b[1];
	out[2] = a[2] + b[2];
}

static inline void
vec3_sub(vec3 out, vec3 const a, vec3 const b) {
	out[0] = a[0] - b[0];
	out[1] = a[1] - b[1];
	out[2] = a[2] - b[2];
}

static inline void
vec3_mul(vec3 out, vec3 const a, vec3 const b) {
	out[0] = a[0] * b[0];
	out[1] = a[1] * b[1];
	out[2] = a[2] * b[2];
}

static inline void
vec3_div(vec3 out, vec3 const a, vec3 const b) {
	out[0] = a[0] / b[0];
	out[1] = a[1] / b[1];
	out[2] = a[2] / b[2];
}

static inline void
vec3_min(vec3 out, vec3 const a, vec3 const b) {
	out[0] = (a[0] < b[0]) ? a[0] : b[0];
	out[1] = (a[1] < b[1]) ? a[1] : b[1];
	out[2] = (a[2] < b[2]) ? a[2] : b[2];
}

static inline void
vec3_max(vec3 out, vec3 const a, vec3 const b) {
	out[0] = (a[0] > b[0]) ? a[0] : b[0];
	out[1] = (a[1] > b[1]) ? a[1] : b[1];
	out[2] = (a[2] > b[2]) ? a[2] : b[2];
}

static inline void
vec3_scale(vec3 out, vec3 const a, scalar alpha) {
	out[0] = alpha * a[0];
	out[1] = alpha * a[1];
	out[2] = alpha * a[2];
}

static inline scalar
vec3_dot(vec3 const a, vec3 const b) {
	return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}

static inline scalar
vec3_2norm(vec3 const a) {
	return a[0] * a[0] + a[1] * a[1] + a[2] * a[2];
}

static inline void
vec4_set(vec4 out, scalar x, scalar y, scalar z, scalar w) {
	out[0] = x;
	out[1] = y;
	out[2] = z;
	out[3] = w;
}

static inline void
vec4_copy(vec4 out, vec4 const a) {
	out[0] = a[0];
	out[1] = a[1];
	out[2] = a[2];
	out[3] = a[3];
}

static inline void
vec4_add(vec4 out, vec4 const a, vec4 const b) {
	out[0] = a[0] + b[0];
	out[1] = a[1] + b[1];
	out[2] = a[2] + b[2];
	out[3] = a[3] + b[3];
}

static inline void
vec4_sub(vec4 out, vec4 const a, vec4 const b) {
	out[0] = a[0] - b[0];
	out[1] = a[1] - b[1];
	out[2] = a[2] - b[2];
	out[3] = a[3] - b[3];
}

static inline void
vec4_mul(vec4 out, vec4 const a, vec4 const b) {
	out[0] = a[0] * b[0];
	out[1] = a[1] * b[1];
	out[2] = a[2] * b[2];
	out[3] = a[3] * b[3];
}

static inline void
vec4_div(vec4 out, vec4 const a, vec4 const b) {
	out[0] = a[0] / b[0];
	out[1] = a[1] / b[1];
	out[2] = a[2] / b[2];
	out[3] = a[3] / b[3];
}

static inline void
vec4_min(vec4 out, vec4 const a, vec4 const b) {
	out[0] = (a[0] < b[0]) ? a[0] : b[0];
	out[1] = (a[1] < b[1]) ? a[1] : b[1];
	out[2] = (a[2] < b[2]) ? a[2] : b[2];
	out[3] = (a[3] < b[3]) ? a[3] : b[3];
}

static inline void
vec4_max(vec4 out, vec4 const a, vec4 const b) {
	out[0] = (a[0] > b[0]) ? a[0] : b[0];
	out[1] = (a[1] > b[1]) ? a[1] : b[1];
	out[2] = (a[2] > b[2]) ? a[2] : b[2];
	out[3] = (a[3] > b[3]) ? a[3] : b[3];
}

static inline void
vec4_scale(vec4 out, vec4 const a, scalar alpha) {
	out[0] = alpha * a[0];
	out[1] = alpha * a[1];
	out[2] = alpha * a[2];
	out[3] = alpha * a[3];
}

static inline scalar
vec4_dot(vec4 const a, vec4 const b) {
	return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
}

static inline scalar
vec4_2norm(vec4 const a) {
	return a[0] * a[0] + a[1] * a[1] + a[2] * a[2] + a[3] * a[3];
}

#endif  // VECTOR_H

A  => meson.build +133 -0
@@ 1,133 @@
project(
    'forge',
    'c',
    version : '0.1.0',
    license : 'GPL-3.0-only',
    meson_version: '>=0.54.0',
    default_options : [
        'warning_level=3',  # Enables -Wall, -Wextra, etc.
        'c_std=c11',
    ])

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

# TODO(rycwo)
jemalloc_dep = dependency(
    'jemalloc',
    version : '>=5.2.0',
    required : get_option('use_jemalloc'))

freetype2_dep = dependency(
    'freetype2',
    version : '>=2.10.0',
    required : get_option('use_freetype2'))

if get_option('use_freetype2').enabled()
    deps += freetype2_dep
else
    # TODO(rycwo): Include stb_truetype.h
endif

includes = include_directories('include')

# Pre-compile shaders into SPIR-V IR to be included directly in the binary.
# TODO(rycwo): Add support for machines using OpenGL 3.3 and above.
glsl_validator = find_program('glslangValidator')

glsl_validator_args = ['-G450', '-x']

optimization = get_option('optimization')

if get_option('debug') or optimization == 'g'
    glsl_validator_args += ['-g']
endif

if optimization != 'g'
    if optimization == 's'
        glsl_validator_args += ['-Os']
    elif optimization.to_int() == 0
        glsl_validator_args += ['-Od']
    endif
endif

spirv_gen = generator(
    glsl_validator,
    output : '@PLAINNAME@.spv',
    arguments : glsl_validator_args + ['-o', '@OUTPUT@', '@INPUT@'])

spirv_binary = spirv_gen.process(
    files([
        'shader/gui.vert.glsl',
        'shader/gui.geom.glsl',
        'shader/gui.frag.glsl'
    ]))

lib = library(
    meson.project_name(),
    files([
        '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_window.c',
        'src/hash_table.c',
        'src/memory_pool.c',
        'src/murmur_hash.c',
        'src/pcg_basic.c',
        'src/seed.c',
        'src/shader.c',
        'src/stb_sprintf.c',
    ]),
    spirv_binary,
    version : meson.project_version(),
    dependencies : deps,
    include_directories : includes,
    install : true)

forge_dep = declare_dependency(
    link_with : lib,
    version : meson.project_version(),
    dependencies : deps,
    include_directories : includes)

install_headers(
    files([
        'include/forge/collection.h',
        'include/forge/gui.h',
        'include/forge/gui_basic.h',
        'include/forge/gui_context.h',
        'include/forge/gui_draw.h',
        'include/forge/gui_graph.h',
        'include/forge/gui_input.h',
        'include/forge/gui_layout.h',
        'include/forge/gui_type.h',
        'include/forge/gui_window.h',
        'include/forge/hash_table.h',
        'include/forge/input.h',
        'include/forge/memory.h',
        'include/forge/memory_pool.h',
        'include/forge/murmur_hash.h',
        'include/forge/pcg_basic.h',
        'include/forge/seed.h',
        'include/forge/shader.h',
        'include/forge/stb_sprintf.h',
        'include/forge/stretchy_buffer.h',
        'include/forge/type.h',
        'include/forge/vector.h',
    ]),
    subdir : meson.project_name())

pkgconfig = import('pkgconfig')
pkgconfig.generate(
    libraries : lib,
    version : meson.project_version(),
    name : lib.name(),
    filebase : meson.project_name(),
    description : 'A Library to barnicate your foos.')

subdir('test')

A  => meson_options.txt +2 -0
@@ 1,2 @@
option('use_jemalloc', type : 'feature', value : 'auto')
option('use_freetype2', type : 'feature', value : 'auto')

A  => shader/gui.frag.glsl +29 -0
@@ 1,29 @@
#version 460 core

layout(location = 0) in geometry_attr {
	vec2  tex;
	vec2  size;
	vec4  color;
	vec4  border_color;
	float border_width;
} frag_in;

layout(location = 0) out vec4 color;

// https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
float
box(in vec2 pos, in vec2 size) {
	vec2 d = abs(pos) - size;
	return length(max(d, 0.0)) + min(max(d.x, d.y), 0.0);
}

void
main(void) {
	// Express box distance function in terms of pixel size
	float d = box((frag_in.tex - 0.5) * frag_in.size, frag_in.size / 2.0);

	// Border function
	float p = abs(d) - frag_in.border_width;

	color = mix(frag_in.color, frag_in.border_color, float(p < 0.0));
}

A  => shader/gui.geom.glsl +66 -0
@@ 1,66 @@
#version 460 core

layout(points) in;
layout(triangle_strip, max_vertices = 4) out;

layout(location = 0) in vertex_attr {
	vec4  color;
	vec4  border_color;
	float border_width;
} geom_in[];

layout(location = 0) out geometry_attr {
	vec2  tex;
	vec2  size;
	vec4  color;
	vec4  border_color;
	float border_width;
} geom_out;

layout(std140, binding = 0) uniform viewport_block {
	vec2 size;
} viewport;

const vec2 tex[4] = vec2[](
		vec2(0.0, 0.0),
		vec2(1.0, 0.0),
		vec2(0.0, 1.0),
		vec2(1.0, 1.0));

vec2
ndc(in vec2 pos) {
	return (2.0 * pos / viewport.size) - 1.0;
}

void
main(void) {
	// Treating the input position as the top-left vert, a quad is expanded
	// outwards using a width and height. Vertices of the triangle strip are
	// defined in CCW winding-order. This means they are processed in the order
	// 0, 1, 2, then 2, 1, 3, starting from the bottom-left vertex.

	vec2 pos = vec2(
		gl_in[0].gl_Position.x - geom_in[0].border_width,
		gl_in[0].gl_Position.y + geom_in[0].border_width
	);
	vec2 size = gl_in[0].gl_Position.zw + 2.0 * geom_in[0].border_width;

	vec2 verts[4] = vec2[](
		vec2(pos - vec2(0.0, size.y)),      // Bottom-left
		vec2(pos + vec2(size.x, -size.y)),  // Bottom-right
		vec2(pos),                          // Top-left
		vec2(pos + vec2(size.x, 0.0)));     // Top-right

	for (int i = 0; i < 4; ++i) {
		gl_Position = vec4(ndc(verts[i]), 0.0, 1.0);
		gl_PrimitiveID = gl_PrimitiveIDIn;
		geom_out.tex = tex[i];
		geom_out.size = size;
		geom_out.color = geom_in[0].color;
		geom_out.border_color = geom_in[0].border_color;
		geom_out.border_width = geom_in[0].border_width;
		EmitVertex();
	}

	EndPrimitive();
}

A  => shader/gui.vert.glsl +24 -0
@@ 1,24 @@
#version 460 core

layout(location = 0) in vec4  rect;
layout(location = 1) in vec4  color;
layout(location = 2) in vec4  border_color;
layout(location = 3) in float border_width;

layout(location = 0) out vertex_attr {
	vec4  color;
	vec4  border_color;
	float border_width;
} vert_out;

layout(std140, binding = 0) uniform viewport_block {
	vec2 size;
} viewport;

void
main(void) {
	gl_Position = vec4(rect.x, viewport.size.y - rect.y, rect.zw);
	vert_out.color = color;
	vert_out.border_color = border_color;
	vert_out.border_width = border_width;
}

A  => src/gui_basic.c +97 -0
@@ 1,97 @@
#include "forge/gui_basic.h"

#include <assert.h>

#include "forge/gui_draw.h"
#include "forge/gui_input.h"
#include "forge/gui_layout.h"
#include "forge/input.h"
#include "forge/stretchy_buffer.h"
#include "forge/vector.h"

void
set_gui_layer(struct gui_context* context, int layer) {
	context->layer = layer;
}

void
gui_rect(
		struct gui_context* context,
		struct gui_style const* style,
		vec2 const size) {
	vec2 pos;
	pop_next_gui_position(context, pos, true);
	draw_gui_rect(context, style, (vec4){pos[0], pos[1], size[0], size[1]});
}

void
begin_gui_container(
		struct gui_context* context,
		struct gui_style const* style,
		vec2 const size) {
	vec2 pos;
	pop_next_gui_position(context, pos, false);

	push_gui_layout_container(
			context,
			(vec4){pos[0], pos[1], size[0], size[1]},
			false);

	vec4 rect;
	gui_top_layout_container(context, rect, true);
	draw_gui_rect(context, style, rect);
}

void
end_gui_container(struct gui_context* context) {
	pop_gui_layout_container(context, (vec4){}, false);
}

bool
gui_button(
		struct gui_context* context,
		uint64_t id,
		struct gui_button_style const* button_style,
		vec2 const size,
		int flags) {
	assert(id != 0);
	vec2 pos;
	pop_next_gui_position(context, pos, true);
	vec4 const rect = {pos[0], pos[1], size[0], size[1]};

	// TODO(rycwo): Take button flags into account
	bool const hovered = gui_item_hovered(context, id, rect);
	int state = GUI_BUTTON_STATE_NONE;
	if (hovered && !context->active) {
		// The button stays active as long as the mouse button is held
		if (context->input.mouse_press & MOUSE_BUTTON_LEFT)
			context->active = id;
		// Only enter hover state if the mouse wasn't already pressed
		if (!(context->input.mouse & MOUSE_BUTTON_LEFT))
			state = GUI_BUTTON_STATE_HOVER;
	}
	if (context->active == id) {
		if (context->input.mouse_release & MOUSE_BUTTON_LEFT) {
			// Only trigger the button if the mouse was released over it
			if (hovered)
				state = GUI_BUTTON_STATE_TRIGGER;
			context->active = 0;
		}
		else if (hovered)
			state = GUI_BUTTON_STATE_ACTIVE;
	}

	static int const states[GUI_BUTTON_STATE_Size] = {
		GUI_BUTTON_STATE_NONE,
		GUI_BUTTON_STATE_HOVER,
		GUI_BUTTON_STATE_FOCUS,
		GUI_BUTTON_STYLE_ACTIVE,
		GUI_BUTTON_STYLE_TRIGGER
	};

	struct gui_style const* style = &button_style->style[GUI_BUTTON_STATE_NONE];
	if (button_style->states & states[state])
		style = &button_style->style[state];
	draw_gui_rect(context, style, rect);
	return state == GUI_BUTTON_STATE_TRIGGER;
}

A  => src/gui_context.c +272 -0
@@ 1,272 @@
#include "forge/gui_context.h"

#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>

#include <GL/glew.h>

#include "forge/gui_input.h"
#include "forge/shader.h"
#include "forge/stretchy_buffer.h"
#include "forge/vector.h"

// Pre-compiled SPIR-V IR. Output from glslangValidator as part
// of the build process.
static uint32_t const vert_spirv[] = {
	#include "gui.vert.glsl.spv"
};
static uint32_t const geom_spirv[] = {
	#include "gui.geom.glsl.spv"
};
static uint32_t const frag_spirv[] = {
	#include "gui.frag.glsl.spv"
};

static GLuint
load_shader(
		uint32_t const* source,
		size_t size,
		GLenum type,
		struct allocator* alloc) {
	GLuint shader = glCreateShader(type);
	glShaderBinary(
			1, &shader,
			GL_SHADER_BINARY_FORMAT_SPIR_V,
			(void const*)source,
			size);
	glSpecializeShader(shader, "main", 0, NULL, NULL);

	struct shader_result result = check_shader(shader, alloc);
	if (!result.ok) {
		fprintf(stderr, "Error compiling shader:\n%s", result.log);
		alloc->free(result.log);
	}

	return shader;
}

static GLuint
load_gui_program(struct allocator* alloc) {
	GLuint vert_shader = load_shader(
			vert_spirv, sizeof(vert_spirv),
			GL_VERTEX_SHADER,
			alloc);
	GLuint geom_shader = load_shader(
			geom_spirv, sizeof(geom_spirv),
			GL_GEOMETRY_SHADER,
			alloc);
	GLuint frag_shader = load_shader(
			frag_spirv, sizeof(frag_spirv),
			GL_FRAGMENT_SHADER,
			alloc);

	GLuint program = glCreateProgram();

	glAttachShader(program, vert_shader);
	glAttachShader(program, geom_shader);
	glAttachShader(program, frag_shader);

	glLinkProgram(program);

	struct shader_result result = check_program(program, alloc);
	if (!result.ok) {
		fprintf(stderr, "Error linking program:\n%s", result.log);
		alloc->free(result.log);
	}

	return program;
}

static inline void
get_viewport_size(struct gui_context const* context, vec2 size) {
	assert(sb_count(context->layout_stack) > 0);
	size[0] = context->layout_stack[0].rect[2];
	size[1] = context->layout_stack[0].rect[3];
}

void
init_gui_default(struct gui_context* context, struct allocator* alloc) {
	memset(context, 0, sizeof(struct gui_context));
	context->alloc = *alloc;

	size_t const buffer_size = sizeof(struct gui_item)
		* GUI_BASE_ITEM_LIMIT + GUI_OVERLAY_ITEM_LIMIT;

	// We initialize one contiguous chunk for both the base items and
	// the overlay items. Buffer memory are slices of this combined buffer.
	struct gui_item* buffer = (struct gui_item*)alloc->alloc(buffer_size);

	context->buffer[GUI_LAYER_BASE].mem = &buffer[0];
	context->buffer[GUI_LAYER_BASE].capacity = GUI_BASE_ITEM_LIMIT;

	context->buffer[GUI_LAYER_OVERLAY].mem = &buffer[GUI_BASE_ITEM_LIMIT];
	context->buffer[GUI_LAYER_OVERLAY].capacity = GUI_OVERLAY_ITEM_LIMIT;

	// FIXME(ryc): Unfortunately the current stretchy buffer impl.
	// does not make use of the caller-provided allocator.
	struct gui_rect r = {{0.0, 0.0, 0.0, 0.0}};
	sb_push(context->layout_stack, r);

	context->program = load_gui_program(alloc);
	if (context->program == 0)
		return;

	glGenBuffers(1, &context->uniform_buffer);
	glBindBuffer(GL_UNIFORM_BUFFER, context->uniform_buffer);
	glBufferData(GL_UNIFORM_BUFFER, sizeof(vec2), NULL, GL_STREAM_DRAW);
	// layout(std140, binding = 0) uniform viewport_block
	glBindBufferBase(GL_UNIFORM_BUFFER, 0, context->uniform_buffer);
	glBindBuffer(GL_UNIFORM_BUFFER, 0);

	glGenBuffers(2, &context->vertex_buffer[0]);
	glBindBuffer(GL_ARRAY_BUFFER, context->vertex_buffer[0]);
	glBufferData(GL_ARRAY_BUFFER, buffer_size, NULL, GL_STREAM_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, context->vertex_buffer[1]);
	glBufferData(GL_ARRAY_BUFFER, buffer_size, NULL, GL_STREAM_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	glGenVertexArrays(1, &context->vertex_array);
	glBindVertexArray(context->vertex_array);

	// layout(location = 0) in vec4 rect
	glEnableVertexAttribArray(0);
	glVertexAttribFormat(
			0,
			4, GL_FLOAT, GL_FALSE,
			offsetof(struct gui_item, rect));
	glVertexAttribBinding(0, 0);

	// layout(location = 1) in vec4 color
	glEnableVertexAttribArray(1);
	glVertexAttribFormat(
			1,
			4, GL_FLOAT, GL_FALSE,
			offsetof(struct gui_item, color));
	glVertexAttribBinding(1, 0);

	// layout(location = 2) in vec4 border_color
	glEnableVertexAttribArray(2);
	glVertexAttribFormat(
			2,
			4, GL_FLOAT, GL_FALSE,
			offsetof(struct gui_item, border_color));
	glVertexAttribBinding(2, 0);

	// layout(location = 3) in float border_width
	glEnableVertexAttribArray(3);
	glVertexAttribFormat(
			3,
			1, GL_FLOAT, GL_FALSE,
			offsetof(struct gui_item, border_width));
	glVertexAttribBinding(3, 0);

	glBindVertexArray(0);
}

void
terminate_gui(struct gui_context* context) {
	glDeleteVertexArrays(1, &context->vertex_array);
	glDeleteBuffers(2, &context->vertex_buffer[0]);
	glDeleteBuffers(1, &context->uniform_buffer);
	glDeleteProgram(context->program);
	// Base and overlay buffers share memory
	context->alloc.free(context->buffer[GUI_LAYER_BASE].mem);
	memset(context, 0, sizeof(struct gui_context));
}

void
begin_gui_frame(struct gui_context* context) {
	context->buffer[GUI_LAYER_BASE].size = 0;
	context->buffer[GUI_LAYER_OVERLAY].size = 0;

	// Reset event state
	context->hover = context->next_hover;
	context->next_hover = 0;
	context->next_hover_layer = 0;
}

void
commit_gui_frame(struct gui_context* context) {
	// Missing call to set_gui_layer(GUI_LAYER_BASE) before commit!
	assert(context->layer == 0);
	// Missing call(s) to pop_gui_layout_container() before commit!
	assert(sb_count(context->layout_stack) == 1);

	// Merge overlay buffer with base buffer. The two buffers share the same
	// contiguous length of memory so we can simply move it across the gap.
	int const base_size = context->buffer[GUI_LAYER_BASE].size;
	int const overlay_size = context->buffer[GUI_LAYER_OVERLAY].size;
	int const merge_size = base_size + overlay_size;
	memmove(
			&context->buffer[GUI_LAYER_BASE].mem[base_size],
			&context->buffer[GUI_LAYER_OVERLAY].mem[0],
			overlay_size * sizeof(struct gui_item));
	context->buffer[GUI_LAYER_BASE].size = merge_size;

	glBindBuffer(GL_ARRAY_BUFFER, context->vertex_buffer[0]);
	glBufferSubData(
			GL_ARRAY_BUFFER,
			0, merge_size * sizeof(struct gui_item),
			(GLvoid const*)(context->buffer[GUI_LAYER_BASE].mem));
	glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void
draw_gui_frame(struct gui_context* context) {
	// Save blend state
	GLboolean blend;
	glGetBooleanv(GL_BLEND, &blend);
	GLint blend_src_alpha;
	glGetIntegerv(GL_BLEND_SRC_ALPHA, &blend_src_alpha);
	GLint blend_eq_rgb;
	glGetIntegerv(GL_BLEND_EQUATION_RGB, &blend_eq_rgb);
	GLint blend_eq_alpha;
	glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blend_eq_alpha);

	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glBlendEquation(GL_FUNC_ADD);

	vec2 viewport_size;
	gui_viewport_size(context, viewport_size);

	glViewport(0, 0, (GLsizei)viewport_size[0], (GLsizei)viewport_size[1]);

	glBindBuffer(GL_UNIFORM_BUFFER, context->uniform_buffer);
	glBufferSubData(
			GL_UNIFORM_BUFFER,
			0, sizeof(vec2),
			(GLvoid const*)&viewport_size[0]);
	glBindBuffer(GL_UNIFORM_BUFFER, 0);

	glUseProgram(context->program);

	glBindVertexArray(context->vertex_array);
	glDrawArrays(GL_POINTS, 0, context->buffer[GUI_LAYER_BASE].size);
	glBindVertexArray(0);
	glUseProgram(0);

	// Restore blend state
	if (blend == GL_TRUE) {
		glBlendFunc(GL_SRC_ALPHA, (GLenum)blend_src_alpha);
		glBlendEquationSeparate((GLenum)blend_eq_rgb, (GLenum)blend_eq_alpha);
	}
	else
		glDisable(GL_BLEND);
}

void
swap_gui_frame(struct gui_context* context) {
	GLuint const tmp = context->vertex_buffer[0];
	context->vertex_buffer[0] = context->vertex_buffer[1];
	context->vertex_buffer[1] = tmp;

	glVertexArrayVertexBuffer(
			context->vertex_array,
			0,
			context->vertex_buffer[0],
			0, sizeof(struct gui_item));
}

A  => src/gui_draw.c +39 -0
@@ 1,39 @@
#include "forge/gui_draw.h"

#include <assert.h>
#include <string.h>

#include "forge/vector.h"

static inline void
push_item(struct gui_item_buffer* buffer, struct gui_item item) {
	assert(buffer->size < buffer->capacity);
	buffer->mem[(buffer->size)++] = item;
}

void
draw_gui_rect(
		struct gui_context* context,
		struct gui_style const* style,
		vec4 const rect) {
	struct gui_item box;
	memset(&box, 0, sizeof(struct gui_item));
	vec4_copy(box.rect, rect);
	vec4_copy(box.color, style->color);
	if (style->flags & GUI_STYLE_BORDER) {
		vec4_copy(box.border_color, style->border_color);
		box.border_width = style->border_width;
	}
	// We currently only support two layers. Algorithm is described here:
	// https://ourmachinery.com/post/one-draw-call-ui/
	push_item(&context->buffer[context->layer], box);
}

void
draw_gui_circle(
		struct gui_context* context,
		struct gui_style const* style,
		vec2 const center,
		float const radius) {
	// TODO(rycwo)
}

A  => src/gui_graph.c +49 -0
@@ 1,49 @@
#include "forge/gui_graph.h"

#include <assert.h>

#include "forge/gui_basic.h"
#include "forge/gui_input.h"
#include "forge/gui_layout.h"
#include "forge/input.h"
#include "forge/stretchy_buffer.h"
#include "forge/vector.h"

bool
begin_gui_node(
		struct gui_context* context,
		uint64_t id,
		struct gui_style const* style,
		vec2 const size,
		vec2 pos) {
	assert(id != 0);

	// Important. Process events and apply input changes since last frame (if
	// necessary) before drawing. Otherwise a frame delay is introduced and may
	// cause inconsistencies.

	vec4 container;
	gui_top_layout_container(context, container, true);

	vec4 rect;
	vec4_set(
			rect,
			container[0] + pos[0],
			container[1] + pos[1],
			size[0],
			size[1]);

	bool hovered;
	bool const pressed = gui_item_pressed(context, id, rect, &hovered);
	if (pressed)
		vec2_add(pos, pos, context->input.cursor_pos_delta);

	set_next_gui_position(context, pos, false);
	begin_gui_container(context, style, size);
	return context->active == id;
}

void
end_gui_node(struct gui_context* context) {
	end_gui_container(context);
}

A  => src/gui_input.c +67 -0
@@ 1,67 @@
#include "forge/gui_input.h"

#include <assert.h>

#include "forge/input.h"
#include "forge/stretchy_buffer.h"
#include "forge/vector.h"

static inline bool
check_bounds(vec2 const pos, vec4 const rect) {
	return pos[0] > rect[0] && pos[0] < (rect[0] + rect[2])
		&& pos[1] > rect[1] && pos[1] < (rect[1] + rect[3]);
}

void
set_gui_cursor_position(struct gui_input* input, vec2 const pos) {
	vec2_sub(input->cursor_pos_delta, pos, input->cursor_pos);
	vec2_copy(input->cursor_pos, pos);
}

void
set_gui_mouse_buttons(struct gui_input* input, int mouse_buttons) {
	input->mouse_press = ~(input->mouse) & mouse_buttons;
	input->mouse_release = input->mouse & ~mouse_buttons;
	input->mouse = mouse_buttons;
}

void
gui_viewport_size(struct gui_context const* context, vec2 size) {
	assert(sb_count(context->layout_stack) > 0);
	size[0] = context->layout_stack[0].rect[2];
	size[1] = context->layout_stack[0].rect[3];
}

void
set_gui_viewport_size(struct gui_context* context, vec2 const size) {
	assert(sb_count(context->layout_stack) > 0);
	context->layout_stack[0].rect[2] = size[0];
	context->layout_stack[0].rect[3] = size[1];
}

bool
gui_item_hovered(struct gui_context* context, uint64_t id, vec4 const rect) {
	if (context->layer >= context->next_hover_layer
			&& check_bounds(context->input.cursor_pos, rect)) {
		context->next_hover = id;
		context->next_hover_layer = context->layer;
	}
	return context->hover == id;
}

bool
gui_item_pressed(
		struct gui_context* context,
		uint64_t id,
		vec4 const rect,
		bool* hovered) {
	*hovered = gui_item_hovered(context, id, rect);
	if (*hovered
			&& !context->active
			&& context->input.mouse_press & MOUSE_BUTTON_LEFT)
		context->active = id;
	else if (context->active == id
			&& !(context->input.mouse & MOUSE_BUTTON_LEFT))
		context->active = 0;
	return context->active == id;
}

A  => src/gui_layout.c +105 -0
@@ 1,105 @@
#include "forge/gui_layout.h"

#include <assert.h>

#include "forge/stretchy_buffer.h"
#include "forge/vector.h"

void
gui_top_layout_container(
		struct gui_context const* context,
		vec4 rect,
		bool absolute) {
	struct gui_rect const r = sb_last(context->layout_stack);
	if (absolute)
		vec4_set(
				rect,
				context->layout_top[0],
				context->layout_top[1],
				r.rect[2],
				r.rect[3]);
	else
		vec4_copy(rect, r.rect);
}

void
push_gui_layout_container(
		struct gui_context* context,
		vec4 const rect,
		bool absolute) {
	struct gui_rect r;
	if (absolute) {
		vec4_set(
				r.rect,
				rect[0] - context->layout_top[0],
				rect[1] - context->layout_top[1],
				rect[2],
				rect[3]);
		vec2_set(context->layout_top, rect[0], rect[1]);
	}
	else {
		vec4_copy(r.rect, rect);
		vec2_set(
				context->layout_top,
				context->layout_top[0] + rect[0],
				context->layout_top[1] + rect[1]);
	}
	sb_push(context->layout_stack, r);
}

void
pop_gui_layout_container(
		struct gui_context* context,
		vec4 rect,
		bool absolute) {
	assert(sb_count(context->layout_stack) > 1);
	gui_top_layout_container(context, rect, absolute);
	struct gui_rect const r = sb_pop(context->layout_stack);
	vec2_set(
			context->layout_top,
			context->layout_top[0] - r.rect[0],
			context->layout_top[1] - r.rect[1]);
}

void
set_next_gui_position(
		struct gui_context* context,
		vec2 const pos,
		bool absolute) {
	if (absolute)
		vec2_copy(context->next_pos, pos);
	else
		vec2_set(
				context->next_pos,
				context->layout_top[0] + pos[0],
				context->layout_top[1] + pos[1]);
}

void
pop_next_gui_position(
		struct gui_context* context,
		vec2 pos,
		bool absolute) {
	if (absolute)
		vec2_copy(pos, context->next_pos);
	else
		vec2_set(
				pos,
				context->next_pos[0] - context->layout_top[0],
				context->next_pos[1] - context->layout_top[1]);
	// Reset position to zero (relative to top container) in absolute coords
	vec2_set(
			context->next_pos,