Final Platform Layer 0.9.8-beta
Loading...
Searching...
No Matches
final_platform_layer.h
Go to the documentation of this file.
1/*
2final_platform_layer.h
3
4-------------------------------------------------------------------------------
5 About
6-------------------------------------------------------------------------------
7
8Final Platform Layer is a Single-Header-File cross-platform C development library designed to abstract the underlying platform to a simple and easy-to-use API - providing low-level access to (Window, Video, Audio, Input, File/Path IO, Threads, Memory, Hardware, etc.).
9
10The main focus is game/media/simulation development, so the default settings will create a window, set up an OpenGL rendering context, and initialize audio playback on any platform.
11
12It is written in C99 for simplicity and best portability but is C++ compatible as well.
13
14FPL supports the platforms Windows/Linux/Unix for the architectures x86/x64/arm.
15
16The only dependencies are built-in operating system libraries and a C99 compliant compiler.
17
18It is licensed under the MIT-License. This license allows you to use FPL freely in any software.
19
20-------------------------------------------------------------------------------
21 Getting started
22-------------------------------------------------------------------------------
23
24- Drop this file into any C/C++ projects you want and include it in any place you want
25- In your main translation unit provide the typical main() entry point
26- Define FPL_IMPLEMENTATION in at least one translation unit before including this header file
27- Init the platform using fplPlatformInit()
28- Use the features you want
29- Release the platform when you are done using fplPlatformRelease()
30
31-------------------------------------------------------------------------------
32 Usage: Hello world console application
33-------------------------------------------------------------------------------
34
35#define FPL_IMPLEMENTATION
36#include <final_platform_layer.h>
37
38int main(int argc, char **args){
39 if (fplPlatformInit(fplInitFlags_None, fpl_null)) {
40 fplConsoleOut("Hello World!");
41 fplPlatformRelease();
42 return 0;
43 } else {
44 return -1;
45 }
46}
47
48-------------------------------------------------------------------------------
49 Usage: OpenGL legacy or modern application
50-------------------------------------------------------------------------------
51
52#define FPL_IMPLEMENTATION
53#include <final_platform_layer.h>
54
55int main(int argc, char **args){
56 // Create default settings
57 fplSettings settings = fplMakeDefaultSettings();
58
59 // Overwrite the video backend
60 settings.video.backend = fplVideoBackendType_OpenGL;
61
62 // Legacy OpenGL
63 settings.video.graphics.opengl.compabilityFlags = fplOpenGLCompabilityFlags_Legacy;
64
65 // or
66
67 // Modern OpenGL
68 settings.video.graphics.opengl.compabilityFlags = fplOpenGLCompabilityFlags_Core;
69 settings.video.graphics.opengl.majorVersion = 3;
70 settings.video.graphics.opengl.minorVersion = 3;
71
72 if (fplPlatformInit(fplInitFlags_Video, &settings)) {
73 // Event/Main loop
74 while (fplWindowUpdate()) {
75 // Poll events
76 fplEvent ev;
77 while (fplPollEvent(&ev)) {
79 }
80
81 // your code goes here
82
83 fplVideoFlip();
84 }
85 fplPlatformRelease();
86 return 0;
87 } else {
88 return -1;
89 }
90}
91
92-------------------------------------------------------------------------------
93 License
94-------------------------------------------------------------------------------
95
96Final Platform Layer is released under the following license:
97
98MIT License
99
100Copyright (c) 2017-2023 Torsten Spaete
101
102Permission is hereby granted, free of charge, to any person obtaining a copy
103of this software and associated documentation files (the "Software"), to deal
104in the Software without restriction, including without limitation the rights
105to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
106copies of the Software, and to permit persons to whom the Software is
107furnished to do so, subject to the following conditions:
108
109The above copyright notice and this permission notice shall be included in all
110copies or substantial portions of the Software.
111
112THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
113IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
114FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
115AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
116LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
117OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
118SOFTWARE.
119*/
120
128// ----------------------------------------------------------------------------
129// > CHANGELOG
130// ----------------------------------------------------------------------------
1525// ****************************************************************************
1526//
1527// > HEADER
1528//
1529// ****************************************************************************
1530#ifndef FPL_HEADER_H
1531#define FPL_HEADER_H
1532
1533//
1534// C99 detection
1535//
1536// https://en.wikipedia.org/wiki/C99#Version_detection
1537// C99 is partially supported since MSVC 2015
1538#if !defined(__cplusplus) && ((defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(_MSC_VER) && (_MSC_VER >= 1900)))
1540# define FPL_IS_C99
1541#elif defined(__cplusplus)
1543# define FPL_IS_CPP
1544# if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || (__cplusplus >= 201103L) || (_MSC_VER >= 1900)
1546# define FPL_IS_CPP11
1547# endif
1548#else
1549# error "This C/C++ compiler is not supported!"
1550#endif
1551
1552//
1553// Architecture detection (x86, x64)
1554// https://sourceforge.net/p/predef/wiki/Architectures/
1555//
1556#if defined(__x86_64__) || defined(_M_X64) || defined(__amd64__)
1557# define FPL_ARCH_X64
1558#elif defined(__i386__) || defined(_M_IX86) || defined(__X86__) || defined(_X86_)
1559# define FPL_ARCH_X86
1560#elif defined(__aarch64__) || defined(_M_ARM64)
1561# define FPL_ARCH_ARM64
1562#elif defined(__arm__) || defined(_M_ARM)
1563# define FPL_ARCH_ARM32
1564#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) || defined(_ARCH_PPC64)
1565# define FPL_ARCH_POWERPC64
1566#elif defined(__powerpc__) || defined(__POWERPC__) || defined(__ppc__) || defined(__PPC__) || defined(_ARCH_PPC)
1567# define FPL_ARCH_POWERPC32
1568#else
1569# error "This architecture is not supported!"
1570#endif // FPL_ARCH
1571
1572//
1573// 32-bit or 64-bit
1574//
1575#if defined(_WIN32)
1576# if defined(_WIN64)
1577# define FPL__M_CPU_64BIT
1578# else
1579# define FPL__M_CPU_32BIT
1580# endif
1581#elif defined(__GNUC__)
1582# if defined(__LP64__)
1583# define FPL__M_CPU_64BIT
1584# else
1585# define FPL__M_CPU_32BIT
1586# endif
1587#else
1588# if (defined(__SIZEOF_POINTER__) && __SIZEOF_POINTER__ == 8) || (sizeof(void *) == 8)
1589# define FPL__M_CPU_64BIT
1590# else
1591# define FPL__M_CPU_32BIT
1592# endif
1593#endif
1594
1595#if defined(FPL__M_CPU_64BIT)
1597# define FPL_CPU_64BIT
1598#elif defined(FPL__M_CPU_32BIT)
1600# define FPL_CPU_32BIT
1601#endif
1602
1603//
1604// Compiler detection
1605// http://beefchunk.com/documentation/lang/c/pre-defined-c/precomp.html
1606// http://nadeausoftware.com/articles/2012/10/c_c_tip_how_detect_compiler_name_and_version_using_compiler_predefined_macros
1607//
1608#if defined(__clang__)
1610# define FPL_COMPILER_CLANG
1611#elif defined(__INTEL_COMPILER)
1613# define FPL_COMPILER_INTEL
1614#elif defined(__MINGW32__)
1616# define FPL_COMPILER_MINGW
1617#elif defined(__CC_ARM)
1619# define FPL_COMPILER_ARM
1620#elif defined(__GNUC__)
1622# define FPL_COMPILER_GCC
1623#elif defined(_MSC_VER)
1625# define FPL_COMPILER_MSVC
1626#else
1628#error "This compiler is not supported!"
1629#endif // FPL_COMPILER
1630
1631//
1632// Platform detection
1633// https://sourceforge.net/p/predef/wiki/OperatingSystems/
1634//
1635#if defined(_WIN32) || defined(_WIN64)
1636# define FPL_PLATFORM_WINDOWS
1637# define FPL_PLATFORM_NAME "Windows"
1638#elif defined(__ANDROID__)
1639# define FPL_PLATFORM_ANDROID
1640# define FPL_PLATFORM_NAME "Android"
1641# define FPL_SUBPLATFORM_POSIX
1642# define FPL_SUBPLATFORM_STD_STRINGS
1643# define FPL_SUBPLATFORM_STD_CONSOLE
1644#elif defined(__linux__) || defined(__gnu_linux__)
1645# define FPL_PLATFORM_LINUX
1646# define FPL_PLATFORM_NAME "Linux"
1647# define FPL_SUBPLATFORM_POSIX
1648# define FPL_SUBPLATFORM_X11
1649# define FPL_SUBPLATFORM_STD_STRINGS
1650# define FPL_SUBPLATFORM_STD_CONSOLE
1651#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__bsdi__)
1652 // @NOTE(final): BSD is treated as a subplatform for now
1653# define FPL_PLATFORM_UNIX
1654# define FPL_PLATFORM_NAME "BSD"
1655# define FPL_SUBPLATFORM_BSD
1656# define FPL_SUBPLATFORM_POSIX
1657# define FPL_SUBPLATFORM_X11
1658# define FPL_SUBPLATFORM_STD_STRINGS
1659# define FPL_SUBPLATFORM_STD_CONSOLE
1660#elif defined(unix) || defined(__unix) || defined(__unix__)
1661# define FPL_PLATFORM_UNIX
1662# define FPL_PLATFORM_NAME "Unix"
1663# define FPL_SUBPLATFORM_POSIX
1664# define FPL_SUBPLATFORM_X11
1665# define FPL_SUBPLATFORM_STD_STRINGS
1666# define FPL_SUBPLATFORM_STD_CONSOLE
1667#else
1668# error "This platform is not supported!"
1669#endif // FPL_PLATFORM
1670
1671// Assembler keyword is compiler specific
1672#if defined(FPL_COMPILER_CLANG) || defined(FPL_COMPILER_GCC)
1673#define fpl__m_Asm __asm__
1674#elif defined(FPL_COMPILER_MSVC)
1675#define fpl__m_Asm __asm
1676#else
1677#define fpl__m_Asm asm
1678#endif
1679
1681#define fplAsm fpl__m_Asm
1682
1683// Minimum alignment
1684#if defined(FPL_COMPILER_MSVC)
1685# define fpl__MinAlignment 8
1686#elif defined(FPL_COMPILER_GCC) || defined(FPL_COMPILER_CLANG)
1687# if defined(FPL_CPU_64BIT)
1688# define fpl__MinAlignment 8
1689# else
1690# define fpl__MinAlignment 4
1691# endif
1692#else
1693# define fpl__MinAlignment 8
1694#endif
1696#define fplMinAlignment fpl__MinAlignment
1697
1698// Alignment keyword
1699#if defined(FPL_IS_CPP11)
1700#define fpl__m_AlignAs(N) alignas(N)
1701#elif defined(FPL_COMPILER_MSVC)
1702#define fpl__m_AlignAs(N) __declspec(align(N))
1703#elif defined(FPL_COMPILER_GCC) || defined(FPL_COMPILER_CLANG)
1704#define fpl__m_AlignAs(N) __attribute__((aligned(N)))
1705#else
1706#define fpl__m_AlignAs(N)
1707#endif
1708#define fpl__m_AlignAsAuto(N) fpl__m_AlignAs(((N) < fplMinAlignment ? fplMinAlignment : (N)))
1709
1711#define fplAlignAs(N) fpl__m_AlignAsAuto(N)
1712
1713//
1714// Defines required for POSIX (mmap, 64-bit file io, etc.)
1715//
1716#if defined(FPL_SUBPLATFORM_POSIX)
1717# if !defined(_XOPEN_SOURCE)
1718# define _XOPEN_SOURCE 600
1719# endif
1720# if !defined(_DEFAULT_SOURCE)
1721# define _DEFAULT_SOURCE 1
1722# endif
1723# if !defined(__STDC_FORMAT_MACROS)
1724# define __STDC_FORMAT_MACROS
1725# endif
1726# if !defined(__STDC_LIMIT_MACROS)
1727# define __STDC_LIMIT_MACROS
1728# endif
1729# if !defined(_LARGEFILE_SOURCE)
1730# define _LARGEFILE_SOURCE
1731# endif
1732# if !defined(_LARGEFILE64_SOURCE)
1733# define _LARGEFILE64_SOURCE
1734# endif
1735# if !defined(_FILE_OFFSET_BITS)
1736# define _FILE_OFFSET_BITS 64
1737# endif
1738#endif
1739
1740#if defined(FPL_PLATFORM_LINUX)
1741# define FPL__INCLUDE_ALLOCA
1742#else
1743# define FPL__INCLUDE_MALLOC
1744#endif
1745
1746// MingW compiler hack
1747#if defined(FPL_PLATFORM_WINDOWS) && defined(FPL_COMPILER_MINGW)
1748# if !defined(_WIN32_WINNT)
1749# define _WIN32_WINNT 0x0600
1750# endif
1751#endif // FPL_COMPILER_MINGW
1752
1753//
1754// Storage class identifiers
1755//
1756
1764#define fpl_globalvar static
1766#define fpl_localvar static
1768#define fpl_internal static
1770#define fpl_inline inline
1772#define fpl_internal_inline inline
1774#if defined(FPL_IS_CPP)
1775# define fpl_extern
1776#else
1777# define fpl_extern extern
1778#endif
1779
1780//
1781// DLL Export/Import definition
1782//
1783#if defined(_WIN32) || defined(__CYGWIN__)
1784# ifdef __GNUC__
1785# define fpl__m_dllexport __attribute__ ((dllexport))
1786# define fpl__m_dllimport __attribute__ ((dllimport))
1787# else
1788# define fpl__m_dllexport __declspec(dllexport)
1789# define fpl__m_dllimport __declspec(dllimport)
1790# endif
1791# define fpl__m_dlllocal
1792#else
1793# if __GNUC__ >= 4
1794# define fpl__m_dllimport __attribute__((visibility("default")))
1795# define fpl__m_dllexport __attribute__((visibility("default")))
1796# define fpl__m_dlllocal __attribute__((visibility("hidden")))
1797# else
1798# define fpl__m_dllimport
1799# define fpl__m_dllexport
1800# define fpl__m_dlllocal
1801# endif
1802#endif
1803
1805#define fpl_dllimport fpl__m_dllimport
1807#define fpl_dllexport fpl__m_dllexport
1809#define fpl_dlllocal fpl__m_dlllocal
1810
1811//
1812// API Call
1813//
1814#if defined(FPL_API_AS_PRIVATE)
1815# define fpl__m_api static
1816#elif defined(FPL_DLLEXPORT)
1817# define fpl__m_api fpl_dllexport
1818#elif defined(FPL_DLLIMPORT)
1819# define fpl__m_api fpl_dllimport
1820#else
1821# define fpl__m_api fpl_extern
1822#endif // FPL_API_AS_PRIVATE
1823
1825#define fpl_api fpl__m_api
1826
1828#define fpl_main
1829
1830#if defined(FPL_IS_CPP)
1831# define fpl__m_platform_api extern "C" fpl_api
1832# define fpl__m_common_api extern "C" fpl_api
1833#else
1834# define fpl__m_platform_api fpl_api
1835# define fpl__m_common_api fpl_api
1836#endif
1837
1839#define fpl_platform_api fpl__m_platform_api
1841#define fpl_common_api fpl__m_common_api
1842
1843//
1844// Inlining
1845//
1846#if defined(FPL_COMPILER_MSVC)
1847# define fpl__m_force_inline __forceinline
1848# define fpl__m_no_inline __declspec(noinline)
1849#elif defined(FPL_COMPILER_GCC) || defined(FPL_COMPILER_CLANG)
1850# define fpl__m_force_inline __attribute__((__always_inline__)) inline
1851# define fpl__m_no_inline __attribute__((noinline))
1852#else
1853# define fpl__m_force_inline inline
1854# define fpl__m_no_inline
1855#endif
1856
1858#define fpl_force_inline fpl__m_force_inline
1860#define fpl_no_inline fpl__m_no_inline
1861
1864//
1865// When C-Runtime is disabled we cannot use any function from the C-Standard Library <stdio.h> or <stdlib.h>
1866//
1867#if defined(FPL_NO_CRT)
1868# if defined(FPL_SUBPLATFORM_STD_CONSOLE)
1869# undef FPL_SUBPLATFORM_STD_CONSOLE
1870# endif
1871# if defined(FPL_SUBPLATFORM_STD_STRINGS)
1872# undef FPL_SUBPLATFORM_STD_STRINGS
1873# endif
1874# if !defined(FPL_USERFUNC_vsnprintf)
1875# error "You need to provide a replacement for vsnprintf() by defining FPL_USERFUNC_vsnprintf!"
1876# endif
1877#endif
1878
1879//
1880// Application type detection
1881// - Can be disabled by FPL_NO_APPTYPE
1882// - Must be explicitly set for No-CRT on Win32
1883//
1884#if defined(FPL_APPTYPE_CONSOLE) && defined(FPL_APPTYPE_WINDOW)
1885# error "Its now allowed to define both FPL_APPTYPE_CONSOLE and FPL_APPTYPE_WINDOW!"
1886#endif
1887#if defined(FPL_NO_CRT)
1888# if !defined(FPL_APPTYPE_CONSOLE) && !defined(FPL_APPTYPE_WINDOW)
1889# error "In 'No-CRT' mode you need to define either FPL_APPTYPE_CONSOLE or FPL_APPTYPE_WINDOW manually!"
1890# endif
1891#elif !defined(FPL_NO_APPTYPE) && !(defined(FPL_APPTYPE_CONSOLE) || defined(FPL_APPTYPE_WINDOW))
1892# if !defined(FPL_NO_WINDOW)
1894# define FPL_APPTYPE_WINDOW
1895# else
1897# define FPL_APPTYPE_CONSOLE
1898# endif
1899#endif
1900
1901//
1902// Include entry points always when its not disabled and implementation block is compiled in
1903//
1904#if defined(FPL_IMPLEMENTATION) && !defined(FPL_NO_ENTRYPOINT)
1905# define FPL_ENTRYPOINT
1906#endif
1907
1908//
1909// Debug/Release detection
1910//
1911#if defined(FPL_DEBUG)
1912# define FPL__ENABLE_DEBUG
1913#elif defined(FPL_RELEASE)
1914# define FPL__ENABLE_RELEASE
1915#endif
1916
1917//
1918// Compiler settings
1919//
1920#if defined(FPL_COMPILER_MSVC)
1921 // Debug/Release detection
1922# if !defined(FPL__ENABLE_DEBUG) && !defined(FPL__ENABLE_RELEASE)
1923# if defined(_DEBUG) || (!defined(NDEBUG))
1924# define FPL__ENABLE_DEBUG
1925# else
1926# define FPL__ENABLE_RELEASE
1927# endif
1928# endif
1929
1930 // Function name macro (Win32)
1931# define FPL__M_FUNCTION_NAME __FUNCTION__
1932
1933 // Setup MSVC subsystem hints
1934# if defined(FPL_APPTYPE_WINDOW)
1935# pragma comment(linker, "/SUBSYSTEM:WINDOWS")
1936# elif defined(FPL_APPTYPE_CONSOLE)
1937# pragma comment(linker, "/SUBSYSTEM:CONSOLE")
1938# endif
1939
1940 // Setup MSVC linker hints
1941# pragma comment(lib, "kernel32.lib")
1942#else
1943 // Function name macro (Other compilers)
1944# define FPL__M_FUNCTION_NAME __FUNCTION__
1945#endif // FPL_COMPILER
1946
1947// Debug Release fallback
1948#if !defined(FPL__ENABLE_DEBUG) && !defined(FPL__ENABLE_RELEASE)
1949# define FPL__ENABLE_DEBUG
1950#endif
1951
1953#if defined(__INTELLISENSE__) || defined(__JETBRAINS_IDE__)
1954# define FPL_IS_IDE 1
1955#else
1956# define FPL_IS_IDE 0
1957#endif
1958
1960#define FPL_FUNCTION_NAME FPL__M_FUNCTION_NAME
1961
1962//
1963// Options & Feature detection
1964//
1965
1966//
1967// CPU Instruction Set Detection based on compiler settings
1968//
1969#if defined(__AVX512F__)
1970# define FPL__COMPILED_X86_CPU_INSTR_SET 9
1971#elif defined(__AVX2__)
1972# define FPL__COMPILED_X86_CPU_INSTR_SET 8
1973#elif defined(__AVX__)
1974# define FPL__COMPILED_X86_CPU_INSTR_SET 7
1975#elif defined(__SSE4_2__)
1976# define FPL__COMPILED_X86_CPU_INSTR_SET 6
1977#elif defined(__SSE4_1__)
1978# define FPL__COMPILED_X86_CPU_INSTR_SET 5
1979#elif defined(__SSSE3__)
1980# define FPL__COMPILED_X86_CPU_INSTR_SET 4
1981#elif defined(__SSE3__)
1982# define FPL__COMPILED_X86_CPU_INSTR_SET 3
1983#elif defined(__SSE2__) || (_M_IX86_FP >= 2)
1984# define FPL__COMPILED_X86_CPU_INSTR_SET 2
1985#elif defined(__SSE__) || (_M_IX86_FP >= 1)
1986# define FPL__COMPILED_X86_CPU_INSTR_SET 1
1987#elif defined(_M_IX86_FP)
1988# define FPL__COMPILED_X86_CPU_INSTR_SET _M_IX86_FP
1989#else
1990# define FPL__COMPILED_X86_CPU_INSTR_SET 0
1991#endif
1992
1993//
1994// Assertions
1995//
1996#if !defined(FPL_NO_ASSERTIONS)
1997# if !defined(FPL_FORCE_ASSERTIONS)
1998# if defined(FPL__ENABLE_DEBUG)
1999# define FPL__ENABLE_ASSERTIONS
2000# endif
2001# else
2002# define FPL__ENABLE_ASSERTIONS
2003# endif
2004#endif // !FPL_NO_ASSERTIONS
2005#if defined(FPL__ENABLE_ASSERTIONS)
2006# if !defined(FPL_NO_C_ASSERT) && !defined(FPL_NO_CRT)
2007# define FPL__ENABLE_C_ASSERT
2008# endif
2009#endif // FPL__ENABLE_ASSERTIONS
2010
2011//
2012// HasInclude
2013//
2014#if defined(__has_include)
2015# define fpl__m_HasInclude(inc) __has_include(inc)
2016#else
2017# define fpl__m_HasInclude(inc) (1)
2018#endif
2020#define fplHasInclude(inc) fpl__m_HasInclude(inc)
2021
2022//
2023// Window
2024//
2025#if !defined(FPL_NO_WINDOW) && !defined(FPL_APPTYPE_CONSOLE)
2026# define FPL__SUPPORT_WINDOW
2027#endif
2028
2029//
2030// Video
2031//
2032#if !defined(FPL_NO_VIDEO)
2033# define FPL__SUPPORT_VIDEO
2034#endif
2035#if defined(FPL__SUPPORT_VIDEO)
2036# if !defined(FPL_NO_VIDEO_OPENGL)
2037# define FPL__SUPPORT_VIDEO_OPENGL
2038# endif
2039# if !defined(FPL_NO_VIDEO_VULKAN)
2040# define FPL__SUPPORT_VIDEO_VULKAN
2041# endif
2042# if !defined(FPL_NO_VIDEO_SOFTWARE)
2043# define FPL__SUPPORT_VIDEO_SOFTWARE
2044# endif
2045#endif // FPL__SUPPORT_VIDEO
2046
2047//
2048// Audio
2049//
2050#if !defined(FPL_NO_AUDIO)
2051 // Audio support
2052# define FPL__SUPPORT_AUDIO
2053#endif
2054#if defined(FPL__SUPPORT_AUDIO)
2055# if !defined(FPL_NO_AUDIO_DIRECTSOUND) && defined(FPL_PLATFORM_WINDOWS)
2056# define FPL__SUPPORT_AUDIO_DIRECTSOUND // <dsound.h> is always present on windows
2057# endif
2058# if !defined(FPL_NO_AUDIO_ALSA) && defined(FPL_PLATFORM_LINUX)
2059# if fplHasInclude(<alsa/asoundlib.h>)
2060# define FPL__SUPPORT_AUDIO_ALSA
2061# else
2062# warning "FPL-Warning: ALSA audio development library is missing. Please install 'libasound2-dev' and try again!"
2063# endif
2064# endif
2065#endif // FPL__SUPPORT_AUDIO
2066
2067//
2068// Remove video support when the window is disabled
2069//
2070#if !defined(FPL__SUPPORT_WINDOW)
2071# if defined(FPL_SUBPLATFORM_X11)
2072# undef FPL_SUBPLATFORM_X11
2073# endif
2074
2075# if defined(FPL__SUPPORT_VIDEO)
2076# undef FPL__SUPPORT_VIDEO
2077# endif
2078# if defined(FPL__SUPPORT_VIDEO_OPENGL)
2079# undef FPL__SUPPORT_VIDEO_OPENGL
2080# endif
2081# if defined(FPL__SUPPORT_VIDEO_VULKAN)
2082# undef FPL__SUPPORT_VIDEO_VULKAN
2083# endif
2084# if defined(FPL__SUPPORT_VIDEO_SOFTWARE)
2085# undef FPL__SUPPORT_VIDEO_SOFTWARE
2086# endif
2087#endif // !FPL__SUPPORT_WINDOW
2088
2089//
2090// Enable supports (FPL uses _ENABLE_ internally only)
2091//
2092#if defined(FPL__SUPPORT_WINDOW)
2093# define FPL__ENABLE_WINDOW
2094#endif
2095
2096#if defined(FPL__SUPPORT_VIDEO)
2097# define FPL__ENABLE_VIDEO
2098# if defined(FPL__SUPPORT_VIDEO_OPENGL)
2099# define FPL__ENABLE_VIDEO_OPENGL
2100# endif
2101# if defined(FPL__SUPPORT_VIDEO_VULKAN)
2102# define FPL__ENABLE_VIDEO_VULKAN
2103# endif
2104# if defined(FPL__SUPPORT_VIDEO_SOFTWARE)
2105# define FPL__ENABLE_VIDEO_SOFTWARE
2106# endif
2107#endif // FPL__SUPPORT_VIDEO
2108
2109#if defined(FPL__SUPPORT_AUDIO)
2110# define FPL__ENABLE_AUDIO
2111# if defined(FPL__SUPPORT_AUDIO_DIRECTSOUND)
2112# define FPL__ENABLE_AUDIO_DIRECTSOUND
2113# endif
2114# if defined(FPL__SUPPORT_AUDIO_ALSA)
2115# define FPL__ENABLE_AUDIO_ALSA
2116# endif
2117#endif // FPL__SUPPORT_AUDIO
2118
2119#if defined(FPL_LOGGING)
2120# define FPL__ENABLE_LOGGING
2121# if defined(FPL_LOG_MULTIPLE_WRITERS)
2122# define FPL__ENABLE_LOG_MULTIPLE_WRITERS
2123# endif
2124#endif
2125
2126//
2127// Assertions & Debug
2128//
2129
2136#if defined(FPL__ENABLE_ASSERTIONS)
2137# if defined(FPL__ENABLE_C_ASSERT) && !defined(FPL_FORCE_ASSERTIONS)
2138# define FPL__INCLUDE_ASSERT
2139# define fpl__m_Assert(exp) assert(exp)
2140# if defined(__cplusplus)
2141# define fpl__m_StaticAssert(exp) static_assert(exp, "fpl_static_assert")
2142# endif
2143# else
2144# define fpl__m_Assert(exp) if(!(exp)) {*(int *)0 = 0;}
2145# endif // FPL__ENABLE_C_ASSERT
2146# if !defined(fpl__m_StaticAssert)
2147# define FPL__M_STATICASSERT_0(exp, line, counter) \
2148 int fpl__ct_assert_##line_##counter(int ct_assert_failed[(exp)?1:-1])
2149# define fpl__m_StaticAssert(exp) \
2150 FPL__M_STATICASSERT_0(exp, __LINE__, __COUNTER__)
2151# endif
2152#else
2153# define fpl__m_Assert(exp)
2154# define fpl__m_StaticAssert(exp)
2155#endif // FPL__ENABLE_ASSERTIONS
2156
2158#define fplAssert(exp) fpl__m_Assert(exp)
2160#define fplStaticAssert(exp) fpl__m_StaticAssert(exp)
2162#define fplAlwaysAssert(exp) if(!(exp)) {*(int *)0 = 0;}
2164#define fplAssertPtr(ptr) fpl__m_Assert((ptr) != fpl_null)
2165
2166//
2167// Debug-Break
2168// Based on: https://stackoverflow.com/questions/173618/is-there-a-portable-equivalent-to-debugbreak-debugbreak
2169//
2170#if defined(__has_builtin)
2171# if __has_builtin(__builtin_debugtrap)
2172# define fpl__m_DebugBreak() __builtin_debugtrap()
2173# elif __has_builtin(__debugbreak)
2174# define fpl__m_DebugBreak() __debugbreak()
2175# endif
2176#endif
2177#if !defined(fpl__m_DebugBreak)
2178# if defined(FPL_COMPILER_MSVC) || defined(FPL_COMPILER_INTEL)
2179# define fpl__m_DebugBreak() __debugbreak()
2180# elif defined(FPL_COMPILER_ARM)
2181# define fpl__m_DebugBreak() __breakpoint(42)
2182# elif defined(FPL_ARCH_X86) || defined(FPL_ARCH_X64)
2183fpl_internal fpl_force_inline void fpl__m_DebugBreak() { __asm__ __volatile__("int $03"); }
2184# elif defined(__thumb__)
2185fpl_internal fpl_force_inline void fpl__m_DebugBreak() { __asm__ __volatile__(".inst 0xde01"); }
2186# elif defined(FPL_ARCH_ARM64)
2187fpl_internal fpl_force_inline void fpl__m_DebugBreak() { __asm__ __volatile__(".inst 0xd4200000"); }
2188# elif defined(FPL_ARCH_ARM32)
2189fpl_internal fpl_force_inline void fpl__m_DebugBreak() { __asm__ __volatile__(".inst 0xe7f001f0"); }
2190# elif defined(FPL_COMPILER_GCC)
2191# define fpl__m_DebugBreak() __builtin_trap()
2192# else
2193# define FPL__INCLUDE_SIGNAL
2194# if defined(SIGTRAP)
2195# define fpl__m_DebugBreak() raise(SIGTRAP)
2196# else
2197# define fpl__m_DebugBreak() raise(SIGABRT)
2198# endif
2199# endif
2200#endif
2201
2203#define fplDebugBreak() fpl__m_DebugBreak()
2204
2207//
2208// Memory macros
2209//
2210
2217#if !defined(FPL_NO_MEMORY_MACROS) || defined(FPL_FORCE_MEMORY_MACROS)
2218# define FPL__ENABLE_MEMORY_MACROS
2219#endif
2220
2223//
2224// Types & Limits
2225//
2226#include <stdint.h> // uint32_t, ...
2227#include <stddef.h> // size_t
2228#include <stdbool.h> // bool
2229#include <stdarg.h> // va_start, va_end, va_list, va_arg
2230#include <limits.h> // UINT32_MAX, ...
2231#if defined(FPL__INCLUDE_ASSERT)
2232# include <assert.h>
2233#endif
2234#if defined(FPL__INCLUDE_SIGNAL)
2235# include <signal.h>
2236#endif
2237#if defined(FPL__INCLUDE_MALLOC)
2238# include <malloc.h>
2239#endif
2240#if defined(FPL__INCLUDE_ALLOCA)
2241# include <alloca.h>
2242#endif
2243
2245#if !defined(UINT32_MAX)
2246 // On android or older posix versions there is no UINT32_MAX
2247# define UINT32_MAX ((uint32_t)-1)
2248#endif
2250
2251#if defined(FPL_IS_CPP11)
2252# define fpl__m_null nullptr
2253#elif defined(NULL)
2254# define fpl__m_null NULL
2255#else
2256# define fpl__m_null 0
2257#endif
2259#define fpl_null fpl__m_null
2260
2262typedef int32_t fpl_b32;
2263
2264//
2265// Test sizes
2266//
2268#if defined(FPL_CPU_64BIT)
2269fplStaticAssert(sizeof(uintptr_t) >= sizeof(uint64_t));
2270fplStaticAssert(sizeof(size_t) >= sizeof(uint64_t));
2271#elif defined(FPL_CPU_32BIT)
2272fplStaticAssert(sizeof(uintptr_t) >= sizeof(uint32_t));
2273fplStaticAssert(sizeof(size_t) >= sizeof(uint32_t));
2274#endif
2276
2277//
2278// Macro functions
2279//
2280
2288#define FPL_NOT_IMPLEMENTED {*(int *)0 = 0xBAD;}
2289
2290#if defined(FPL_IS_C99)
2291# define fpl__m_ZeroInit {0}
2292# define fpl__m_StructSet(ptr, type, value) *(ptr) = (type)value
2293# define fpl__m_StructInit(type, ...) (type){__VA_ARGS__}
2294#else
2295# define fpl__m_ZeroInit {}
2296# define fpl__m_StructSet(ptr, type, value) *(ptr) = value
2297# define fpl__m_StructInit(type, ...) {__VA_ARGS__}
2298#endif
2299
2301#define fplZeroInit fpl__m_ZeroInit
2303#define fplStructSet fpl__m_StructSet
2305#define fplStructInit fpl__m_StructInit
2306
2308#define fplGetAlignmentOffset(value, alignment) ( (((alignment) > 1) && (((value) & ((alignment) - 1)) != 0)) ? ((alignment) - ((value) & (alignment - 1))) : 0)
2310#define fplGetAlignedSize(size, alignment) (((size) > 0 && (alignment) > 0) ? ((size) + fplGetAlignmentOffset(size, alignment)) : (size))
2312#define fplIsAligned(ptr, alignment) (((uintptr_t)(const void *)(ptr)) % (alignment) == 0)
2314#define fplIsPowerOfTwo(value) (((value) != 0) && (((value) & (~(value) + 1)) == (value)))
2316#define fplIsBigEndian() (*(uint16_t *)"\0\xff" < 0x100)
2318#define fplIsLittleEndian() (!fplIsBigEndian())
2320#define fplIsBitSet(value, bit) (((value) >> (bit)) & 0x1)
2321
2323#define fplKiloBytes(value) (((value) * 1024ull))
2325#define fplMegaBytes(value) ((fplKiloBytes(value) * 1024ull))
2327#define fplGigaBytes(value) ((fplMegaBytes(value) * 1024ull))
2329#define fplTeraBytes(value) ((fplGigaBytes(value) * 1024ull))
2330
2332#define fplClearStruct(ptr) fplMemoryClear((void *)(ptr), sizeof(*(ptr)))
2334#define fplCopyStruct(src, dst) fplMemoryCopy(src, sizeof(*(src)), dst);
2335
2336// Array count
2337#if defined(FPL_COMPILER_MSVC) && !defined(FPL_NO_CRT) // @TODO(final): Find a better way to detect no-crt inclusion!
2338# define fpl__m_ArrayCount(arr) _countof(arr)
2339#elif defined(ARRAY_SIZE)
2340# define fpl__m_ArrayCount(arr) ARRAY_SIZE(arr)
2341#else
2343# define FPL__NO_ARRAYCOUNT_VALIDATION
2344# define fpl__m_ArrayCount(arr) (sizeof(arr) / sizeof((arr)[0]))
2345#endif
2347#define fplArrayCount(arr) fpl__m_ArrayCount(arr)
2348
2350#define fplOffsetOf(type, field) ((size_t)(&(((type*)(0))->field)))
2351
2353#define fplMin(a, b) ((a) < (b) ? (a) : (b))
2354
2356#define fplMax(a, b) ((a) > (b) ? (a) : (b))
2357
2358#if defined(FPL_PLATFORM_WINDOWS)
2359# define fpl__m_StackAllocate(size) _alloca(size)
2360#else
2361# define fpl__m_StackAllocate(size) alloca(size)
2362#endif
2363
2365#define fplStackAllocate(size) fpl__m_StackAllocate(size)
2366
2369#if defined(FPL_IS_CPP)
2370# define FPL__M_ENUM_AS_FLAGS_OPERATORS(etype) \
2371 inline etype operator | (etype a, etype b) { \
2372 return static_cast<etype>(static_cast<int>(a) | static_cast<int>(b)); \
2373 } \
2374 inline etype& operator |= (etype &a, etype b) { \
2375 return a = a | b; \
2376 } \
2377 inline etype operator & (etype a, etype b) { \
2378 return static_cast<etype>(static_cast<int>(a) & static_cast<int>(b)); \
2379 } \
2380 inline etype& operator &= (etype &a, etype b) { \
2381 return a = a & b; \
2382 } \
2383 inline etype operator ~ (etype a) { \
2384 return static_cast<etype>(~static_cast<int>(a)); \
2385 } \
2386 inline etype operator ^ (etype a, etype b) { \
2387 return static_cast<etype>(static_cast<int>(a) ^ static_cast<int>(b)); \
2388 } \
2389 inline etype& operator ^= (etype &a, etype b) { \
2390 return a = a ^ b; \
2391 }
2392#else
2393# define FPL__M_ENUM_AS_FLAGS_OPERATORS(etype)
2394#endif
2395
2397#define FPL_ENUM_AS_FLAGS_OPERATORS(type) FPL__M_ENUM_AS_FLAGS_OPERATORS(type)
2398
2399// ****************************************************************************
2400//
2401// Platform Includes
2402//
2403// ****************************************************************************
2404#if !defined(FPL_NO_PLATFORM_INCLUDES) && !defined(FPL__HAS_PLATFORM_INCLUDES)
2405# define FPL__HAS_PLATFORM_INCLUDES
2406
2407# if defined(FPL_PLATFORM_WINDOWS)
2408 // @NOTE(final): windef.h defines min/max macros in lowerspace, this will break for example std::min/max, so we have to tell the header we dont want this!
2409# if !defined(NOMINMAX)
2410# define NOMINMAX
2411# endif
2412 // @NOTE(final): For now we dont want any network, com or gdi stuff at all, maybe later who knows.
2413# if !defined(WIN32_LEAN_AND_MEAN)
2414# define WIN32_LEAN_AND_MEAN 1
2415# endif
2416 // @STUPID(final): Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here"
2417struct IUnknown;
2418# include <windows.h> // Win32 api
2419# if _WIN32_WINNT < 0x0600
2420# error "Windows Vista or higher required!"
2421# endif
2422# endif // FPL_PLATFORM_WINDOWS
2423
2424# if defined(FPL_SUBPLATFORM_POSIX)
2425# include <pthread.h> // pthread_t, pthread_mutex_, pthread_cond_, pthread_barrier_
2426# include <sched.h> // sched_param, sched_get_priority_max, SCHED_FIFO
2427# include <semaphore.h> // sem_t
2428# include <dirent.h> // DIR, dirent
2429# endif // FPL_SUBPLATFORM_POSIX
2430
2431# if defined(FPL_SUBPLATFORM_X11)
2432# include <X11/X.h> // Window
2433# include <X11/Xlib.h> // Display
2434# include <X11/Xutil.h> // XVisualInfo
2435# include <X11/Xatom.h> // XA_CARDINAL
2436# endif // FPL_SUBPLATFORM_X11
2437
2438#endif // !FPL_NO_PLATFORM_INCLUDES
2439
2440//
2441// Platform handles
2442//
2443#if !defined(FPL__HAS_PLATFORM_INCLUDES) || defined(FPL_OPAQUE_HANDLES)
2444
2445# if defined(FPL_PLATFORM_WINDOWS)
2446
2448typedef uint64_t fpl__Win32Guid[4];
2450typedef void *fpl__Win32Handle;
2452typedef fpl__Win32Handle fpl__Win32InstanceHandle;
2454typedef fpl__Win32Handle fpl__Win32LibraryHandle;
2456typedef fpl__Win32Handle fpl__Win32FileHandle;
2458typedef fpl__Win32Handle fpl__Win32ThreadHandle;
2460typedef uint64_t fpl__Win32MutexHandle[16];
2462typedef fpl__Win32Handle fpl__Win32SignalHandle;
2464typedef void *fpl__Win32ConditionVariable;
2466typedef fpl__Win32Handle fpl__Win32SemaphoreHandle;
2468typedef fpl__Win32Handle fpl__Win32WindowHandle;
2470typedef fpl__Win32Handle fpl__Win32DeviceContext;
2472typedef fpl__Win32Handle fpl__Win32RenderingContext;
2474typedef union fpl__Win32LargeInteger {
2476 int64_t QuadPart;
2477 struct {
2479 int32_t LowPart;
2481 int32_t HighPart;
2482 };
2483} fpl__Win32LargeInteger;
2484
2485# endif // FPL_PLATFORM_WINDOWS
2486
2487# if defined(FPL_SUBPLATFORM_POSIX)
2488
2490typedef void *fpl__POSIXLibraryHandle;
2492typedef int fpl__POSIXFileHandle;
2494typedef void *fpl__POSIXDirHandle;
2496typedef uint64_t fpl__POSIXThreadHandle;
2498typedef uint64_t fpl__POSIXMutexHandle[16];
2500typedef uint64_t fpl__POSIXSemaphoreHandle[8];
2502typedef uint64_t fpl__POSIXConditionVariable[16];
2503
2504# endif // FPL_SUBPLATFORM_POSIX
2505
2506# if defined(FPL_SUBPLATFORM_X11)
2507
2509typedef void *fpl__X11Display;
2511typedef int fpl__X11Window;
2513typedef void *fpl__X11Visual;
2515typedef void *fpl__X11GC;
2517typedef void *fpl__X11Image;
2519typedef void *fpl__GLXContext;
2520
2521# endif // FPL_SUBPLATFORM_X11
2522
2523# if defined(FPL_PLATFORM_LINUX)
2524
2526typedef int fpl__LinuxSignalHandle;
2527
2528# endif // FPL_PLATFORM_LINUX
2529
2530#else
2531
2532# if defined(FPL_PLATFORM_WINDOWS)
2533
2535typedef GUID fpl__Win32Guid;
2537typedef HANDLE fpl__Win32Handle;
2539typedef HINSTANCE fpl__Win32InstanceHandle;
2541typedef HMODULE fpl__Win32LibraryHandle;
2543typedef HANDLE fpl__Win32ThreadHandle;
2545typedef HANDLE fpl__Win32FileHandle;
2547typedef CRITICAL_SECTION fpl__Win32MutexHandle;
2549typedef HANDLE fpl__Win32SignalHandle;
2551typedef CONDITION_VARIABLE fpl__Win32ConditionVariable;
2553typedef HANDLE fpl__Win32SemaphoreHandle;
2555typedef HWND fpl__Win32WindowHandle;
2557typedef HDC fpl__Win32DeviceContext;
2559typedef HGLRC fpl__Win32RenderingContext;
2561typedef LARGE_INTEGER fpl__Win32LargeInteger;
2562
2563# endif // FPL_PLATFORM_WINDOWS
2564
2565# if defined(FPL_SUBPLATFORM_POSIX)
2566
2568typedef void *fpl__POSIXLibraryHandle;
2570typedef int fpl__POSIXFileHandle;
2572typedef DIR *fpl__POSIXDirHandle;
2574typedef pthread_t fpl__POSIXThreadHandle;
2576typedef pthread_mutex_t fpl__POSIXMutexHandle;
2578typedef sem_t fpl__POSIXSemaphoreHandle;
2580typedef pthread_cond_t fpl__POSIXConditionVariable;
2581
2582# endif // FPL_SUBPLATFORM_POSIX
2583
2584# if defined(FPL_SUBPLATFORM_X11)
2585
2587typedef Display *fpl__X11Display;
2589typedef Window fpl__X11Window;
2591typedef Visual *fpl__X11Visual;
2593typedef GC fpl__X11GC;
2595typedef XImage *fpl__X11Image;
2597typedef void *fpl__GLXContext;
2598
2599# endif // FPL_SUBPLATFORM_X11
2600
2601
2602# if defined(FPL_PLATFORM_LINUX)
2603
2605typedef int fpl__LinuxSignalHandle;
2606
2607# endif // FPL_PLATFORM_LINUX
2608
2609
2610#endif
2611
2612//
2613// Constants
2614//
2615
2621#if defined(FPL_PLATFORM_WINDOWS)
2622# if defined(MAX_PATH)
2623# define FPL__M_MAX_FILENAME_LENGTH (MAX_PATH)
2624# define FPL__M_MAX_PATH_LENGTH (MAX_PATH * 2)
2625# else
2626# define FPL__M_MAX_FILENAME_LENGTH (260)
2627# define FPL__M_MAX_PATH_LENGTH (260 * 2)
2628# endif
2629# define FPL__M_PATH_SEPARATOR '\\'
2630# define FPL__M_FILE_EXT_SEPARATOR '.'
2631#else
2632# define FPL__M_MAX_FILENAME_LENGTH (512)
2633# define FPL__M_MAX_PATH_LENGTH (2048)
2634# define FPL__M_PATH_SEPARATOR '/'
2635# define FPL__M_FILE_EXT_SEPARATOR '.'
2636#endif
2637
2639#define FPL_MAX_FILENAME_LENGTH FPL__M_MAX_FILENAME_LENGTH
2641#define FPL_MAX_PATH_LENGTH FPL__M_MAX_PATH_LENGTH
2643#define FPL_PATH_SEPARATOR FPL__M_PATH_SEPARATOR
2645#define FPL_FILE_EXT_SEPARATOR FPL__M_FILE_EXT_SEPARATOR
2647#define FPL_MAX_NAME_LENGTH (256)
2649#define FPL_MAX_BUFFER_LENGTH (2048)
2650
2653// ****************************************************************************
2654//
2655// > API
2656//
2657// ****************************************************************************
2658
2659// ----------------------------------------------------------------------------
2666// ----------------------------------------------------------------------------
2667
2668//
2669// Barrier/Fence
2670//
2671
2690
2691//
2692// Exchange
2693//
2694
2703fpl_platform_api uint32_t fplAtomicExchangeU32(volatile uint32_t *target, const uint32_t value);
2712fpl_platform_api uint64_t fplAtomicExchangeU64(volatile uint64_t *target, const uint64_t value);
2721fpl_platform_api int32_t fplAtomicExchangeS32(volatile int32_t *target, const int32_t value);
2730fpl_platform_api int64_t fplAtomicExchangeS64(volatile int64_t *target, const int64_t value);
2739fpl_common_api void *fplAtomicExchangePtr(volatile void **target, const void *value);
2748fpl_common_api size_t fplAtomicExchangeSize(volatile size_t *target, const size_t value);
2749
2750//
2751// Fetch and Add
2752//
2753
2762fpl_platform_api uint32_t fplAtomicFetchAndAddU32(volatile uint32_t *value, const uint32_t addend);
2771fpl_platform_api uint64_t fplAtomicFetchAndAddU64(volatile uint64_t *value, const uint64_t addend);
2780fpl_platform_api int32_t fplAtomicFetchAndAddS32(volatile int32_t *value, const int32_t addend);
2789fpl_platform_api int64_t fplAtomicFetchAndAddS64(volatile int64_t *value, const int64_t addend);
2798fpl_common_api size_t fplAtomicFetchAndAddSize(volatile size_t *dest, const size_t addend);
2807fpl_common_api void *fplAtomicFetchAndAddPtr(volatile void **dest, const intptr_t addend);
2808
2809//
2810// Add and Fetch
2811//
2812
2821fpl_platform_api uint32_t fplAtomicAddAndFetchU32(volatile uint32_t *dest, const uint32_t addend);
2830fpl_platform_api uint64_t fplAtomicAddAndFetchU64(volatile uint64_t *dest, const uint64_t addend);
2839fpl_platform_api int32_t fplAtomicAddAndFetchS32(volatile int32_t *dest, const int32_t addend);
2848fpl_platform_api int64_t fplAtomicAddAndFetchS64(volatile int64_t *dest, const int64_t addend);
2857fpl_common_api size_t fplAtomicAddAndFetchSize(volatile size_t *dest, const size_t addend);
2866fpl_common_api void *fplAtomicAddAndFetchPtr(volatile void **dest, const intptr_t addend);
2867
2868//
2869// Increment
2870//
2871
2879fpl_platform_api uint32_t fplAtomicIncrementU32(volatile uint32_t *dest);
2887fpl_platform_api uint64_t fplAtomicIncrementU64(volatile uint64_t *dest);
2895fpl_platform_api int32_t fplAtomicIncrementS32(volatile int32_t *dest);
2903fpl_platform_api int64_t fplAtomicIncrementS64(volatile int64_t *dest);
2911fpl_common_api size_t fplAtomicIncrementSize(volatile size_t *dest);
2919fpl_common_api void *fplAtomicIncrementPtr(volatile void **dest);
2920
2921//
2922// CAS
2923//
2924
2935fpl_platform_api uint32_t fplAtomicCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange);
2946fpl_platform_api uint64_t fplAtomicCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange);
2957fpl_platform_api int32_t fplAtomicCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange);
2968fpl_platform_api int64_t fplAtomicCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange);
2979fpl_common_api size_t fplAtomicCompareAndSwapSize(volatile size_t *dest, const size_t comparand, const size_t exchange);
2990fpl_common_api void *fplAtomicCompareAndSwapPtr(volatile void **dest, const void *comparand, const void *exchange);
2991
3001fpl_platform_api bool fplAtomicIsCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange);
3011fpl_platform_api bool fplAtomicIsCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange);
3021fpl_platform_api bool fplAtomicIsCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange);
3031fpl_platform_api bool fplAtomicIsCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange);
3041fpl_common_api bool fplAtomicIsCompareAndSwapSize(volatile size_t *dest, const size_t comparand, const size_t exchange);
3051fpl_common_api bool fplAtomicIsCompareAndSwapPtr(volatile void **dest, const void *comparand, const void *exchange);
3052
3053//
3054// Load
3055//
3056
3065fpl_platform_api uint32_t fplAtomicLoadU32(volatile uint32_t *source);
3074fpl_platform_api uint64_t fplAtomicLoadU64(volatile uint64_t *source);
3083fpl_platform_api int32_t fplAtomicLoadS32(volatile int32_t *source);
3092fpl_platform_api int64_t fplAtomicLoadS64(volatile int64_t *source);
3101fpl_common_api size_t fplAtomicLoadSize(volatile size_t *source);
3110fpl_common_api void *fplAtomicLoadPtr(volatile void **source);
3111
3112//
3113// Store
3114//
3115
3123fpl_platform_api void fplAtomicStoreU32(volatile uint32_t *dest, const uint32_t value);
3131fpl_platform_api void fplAtomicStoreU64(volatile uint64_t *dest, const uint64_t value);
3139fpl_platform_api void fplAtomicStoreS32(volatile int32_t *dest, const int32_t value);
3147fpl_platform_api void fplAtomicStoreS64(volatile int64_t *dest, const int64_t value);
3155fpl_common_api void fplAtomicStoreSize(volatile size_t *dest, const size_t value);
3163fpl_common_api void fplAtomicStorePtr(volatile void **dest, const void *value);
3164
3167// ----------------------------------------------------------------------------
3173// ----------------------------------------------------------------------------
3174
3176typedef struct fplMemoryBlock {
3178 void *base;
3180 size_t size;
3182
3202
3209fpl_common_api void fplMemoryClear(void *mem, const size_t size);
3217fpl_common_api void fplMemorySet(void *mem, const uint8_t value, const size_t size);
3225fpl_common_api void fplMemoryCopy(const void *sourceMem, const size_t sourceSize, void *targetMem);
3235fpl_platform_api void *fplMemoryAllocate(const size_t size);
3253fpl_common_api void *fplMemoryAlignedAllocate(const size_t size, const size_t alignment);
3269
3272// ----------------------------------------------------------------------------
3278// ----------------------------------------------------------------------------
3279
3281typedef char fplVersionNumberPart[4 + 1];
3282
3302
3314
3323
3326// ----------------------------------------------------------------------------
3332// ----------------------------------------------------------------------------
3333
3341fpl_platform_api size_t fplSessionGetUsername(char *nameBuffer, const size_t maxNameBufferLen);
3342
3345// ----------------------------------------------------------------------------
3352// ----------------------------------------------------------------------------
3353
3374
3400
3402typedef union fplCPUIDLeaf {
3403 struct {
3405 uint32_t eax;
3407 uint32_t ebx;
3409 uint32_t ecx;
3411 uint32_t edx;
3412 };
3414 uint32_t raw[4];
3416
3423fpl_common_api void fplCPUID(fplCPUIDLeaf *outLeaf, const uint32_t functionId);
3456fpl_common_api size_t fplCPUGetName(char *destBuffer, const size_t maxDestBufferLen);
3470
3473// ----------------------------------------------------------------------------
3479// ----------------------------------------------------------------------------
3480
3500
3517
3542
3550
3567
3568#if defined(FPL__ENABLE_VIDEO_OPENGL)
3580
3594#endif // FPL__ENABLE_VIDEO_OPENGL
3595
3596#if defined(FPL__ENABLE_VIDEO_VULKAN)
3597
3599typedef void (fplVulkanValidationLayerCallback)(void *userData, const char *message, const uint32_t messageSeverity, const uint32_t messageType, const void *debugUtilsMessengerCallbackData);
3600
3610
3626
3654#endif // FPL__ENABLE_VIDEO_VULKAN
3655
3658#if defined(FPL__ENABLE_VIDEO_OPENGL)
3661#endif
3662#if defined(FPL__ENABLE_VIDEO_VULKAN)
3665#endif
3669
3681
3689
3706
3731
3749
3757
3777
3797
3799typedef union fplAudioDeviceID {
3800#if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
3802 fpl__Win32Guid dshow;
3803#endif
3804#if defined(FPL__ENABLE_AUDIO_ALSA)
3806 char alsa[256];
3807#endif
3811
3819
3820#if defined(FPL__ENABLE_AUDIO_ALSA)
3822typedef struct fplAlsaAudioSettings {
3824 fpl_b32 noMMap;
3825} fplAlsaAudioSettings;
3826#endif
3827
3830#if defined(FPL__ENABLE_AUDIO_ALSA)
3832 fplAlsaAudioSettings alsa;
3833#endif
3837
3847typedef uint32_t(fpl_audio_client_read_callback)(const fplAudioDeviceFormat *deviceFormat, const uint32_t frameCount, void *outputSamples, void *userData);
3848
3868
3876
3884
3886typedef struct fplImageSource {
3888 const uint8_t *data;
3890 uint32_t width;
3892 uint32_t height;
3896
3905typedef bool (fpl_window_event_callback)(const fplPlatformType platformType, void *windowState, void *rawEventData, void *userData);
3906
3916
3928
3930typedef struct fplWindowSize {
3932 uint32_t width;
3934 uint32_t height;
3936
3938typedef struct fplWindowPosition {
3940 int32_t left;
3942 int32_t top;
3944
3946typedef union fplColor32 {
3947 struct {
3949 uint8_t b;
3951 uint8_t g;
3953 uint8_t r;
3955 uint8_t a;
3956 };
3958 uint32_t value;
3960 uint8_t m[4];
3962
3971fpl_inline fplColor32 fplCreateColorRGBA(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a) {
3972 fplColor32 result = fplStructInit(fplColor32, b, g, r, a);
3973 return(result);
3974}
3975
4005
4013
4019
4026
4034
4042
4044typedef void *(fpl_memory_allocate_callback)(void *userData, const size_t size, const size_t alignment);
4046typedef void (fpl_memory_release_callback)(void *userData, void *ptr);
4047
4055
4067
4075
4091
4111
4114// ----------------------------------------------------------------------------
4120// ----------------------------------------------------------------------------
4121
4143fpl_common_api bool fplPlatformInit(const fplInitFlags initFlags, const fplSettings *initSettings);
4160
4163// ----------------------------------------------------------------------------
4169// ----------------------------------------------------------------------------
4170
4195
4196#if defined(FPL__ENABLE_LOGGING)
4205typedef void (fpl_log_func_callback)(const char *funcName, const int lineNumber, const fplLogLevel level, const char *message);
4206
4222
4228
4234
4244
4246typedef struct fplLogSettings {
4247#if defined(FPL__ENABLE_LOG_MULTIPLE_WRITERS)
4248 union {
4251 struct {
4253 fplLogWriter criticalWriter;
4255 fplLogWriter errorWriter;
4257 fplLogWriter warningWriter;
4259 fplLogWriter infoWriter;
4261 fplLogWriter verboseWriter;
4263 fplLogWriter debugWriter;
4264 };
4265 };
4266#else
4269#endif // FPL_USE_LOG_SIMPLE
4271 fplLogLevel maxLevel;
4275
4304#endif // FPL__ENABLE_LOGGING
4305
4308// ----------------------------------------------------------------------------
4314// ----------------------------------------------------------------------------
4315
4330fpl_common_api const char *fplGetErrorByIndex(const size_t index);
4344
4347// ----------------------------------------------------------------------------
4353// ----------------------------------------------------------------------------
4354
4357#if defined(FPL_PLATFORM_WINDOWS)
4359 fpl__Win32LibraryHandle win32LibraryHandle;
4360#elif defined(FPL_SUBPLATFORM_POSIX)
4362 fpl__POSIXLibraryHandle posixLibraryHandle;
4363#endif
4365
4373
4381fpl_platform_api bool fplDynamicLibraryLoad(const char *libraryFilePath, fplDynamicLibraryHandle *outHandle);
4396
4399// ----------------------------------------------------------------------------
4404// ----------------------------------------------------------------------------
4405
4412fpl_platform_api void fplDebugOut(const char *text);
4420fpl_common_api void fplDebugFormatOut(const char *format, ...);
4421
4424// ----------------------------------------------------------------------------
4430// ----------------------------------------------------------------------------
4431
4437fpl_platform_api void fplConsoleOut(const char *text);
4443fpl_platform_api void fplConsoleError(const char *text);
4450
4457fpl_common_api void fplConsoleFormatOut(const char *format, ...);
4464fpl_common_api void fplConsoleFormatError(const char *format, ...);
4465
4468// ----------------------------------------------------------------------------
4474// ----------------------------------------------------------------------------
4475
4477typedef union fplTimestamp {
4478#if defined(FPL_PLATFORM_WINDOWS)
4480 struct {
4482 fpl__Win32LargeInteger qpc;
4484 uint64_t ticks;
4485 } win32;
4486#elif defined(FPL_SUBPLATFORM_POSIX)
4488 struct {
4490 uint64_t seconds;
4492 int64_t nanoSeconds;
4493 } posix;
4494#endif
4496 uint64_t unused;
4498
4500typedef uint32_t fplTimeoutValue;
4502#define FPL_TIMEOUT_INFINITE UINT32_MAX
4503
4505typedef double fplSeconds;
4506
4508typedef uint64_t fplMilliseconds;
4509
4526
4529// ----------------------------------------------------------------------------
4535// ----------------------------------------------------------------------------
4536
4548
4550typedef uint32_t fplThreadState;
4551
4578
4581
4587typedef void (fpl_run_thread_callback)(const fplThreadHandle *thread, void *data);
4588
4591#if defined(FPL_PLATFORM_WINDOWS)
4593 fpl__Win32ThreadHandle win32ThreadHandle;
4594#elif defined(FPL_SUBPLATFORM_POSIX)
4596 fpl__POSIXThreadHandle posixThread;
4597#endif
4599
4611
4627
4628#if defined(FPL_PLATFORM_WINDOWS)
4629typedef struct fpl__Win32InternalSemaphore {
4631 fpl__Win32SemaphoreHandle handle;
4633 volatile int32_t value;
4634} fpl__Win32InternalSemaphore;
4635#endif
4636
4639#if defined(FPL_PLATFORM_WINDOWS)
4641 fpl__Win32InternalSemaphore win32;
4642#elif defined(FPL_SUBPLATFORM_POSIX)
4644 fpl__POSIXSemaphoreHandle posixHandle;
4645#endif
4647
4655
4658#if defined(FPL_PLATFORM_WINDOWS)
4660 fpl__Win32MutexHandle win32CriticalSection;
4661#elif defined(FPL_SUBPLATFORM_POSIX)
4663 fpl__POSIXMutexHandle posixMutex;
4664#endif
4666
4674
4677#if defined(FPL_PLATFORM_WINDOWS)
4679 fpl__Win32SignalHandle win32EventHandle;
4680#elif defined(FPL_PLATFORM_LINUX)
4682 fpl__LinuxSignalHandle linuxEventHandle;
4683#endif
4685
4693
4701
4704#if defined(FPL_PLATFORM_WINDOWS)
4706 fpl__Win32ConditionVariable win32Condition;
4707#elif defined(FPL_SUBPLATFORM_POSIX)
4709 fpl__POSIXConditionVariable posixCondition;
4710#endif
4714
4722
4786fpl_platform_api void fplThreadSleep(const uint32_t milliseconds);
4819fpl_platform_api bool fplThreadWaitForAll(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout);
4829fpl_platform_api bool fplThreadWaitForAny(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout);
4830
4866
4899fpl_platform_api bool fplSignalWaitForAll(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout);
4909fpl_platform_api bool fplSignalWaitForAny(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout);
4924
4963
4971fpl_platform_api bool fplSemaphoreInit(fplSemaphoreHandle *semaphore, const uint32_t initialValue);
5010
5013// ----------------------------------------------------------------------------
5019// ----------------------------------------------------------------------------
5020
5027fpl_common_api bool fplIsStringMatchWildcard(const char *source, const char *wildcard);
5037fpl_common_api bool fplIsStringEqualLen(const char *a, const size_t aLen, const char *b, const size_t bLen);
5044fpl_common_api bool fplIsStringEqual(const char *a, const char *b);
5051fpl_common_api char *fplEnforcePathSeparatorLen(char *path, size_t maxPathLen);
5067fpl_common_api char *fplStringAppendLen(const char *appended, const size_t appendedLen, char *buffer, size_t maxBufferLen);
5075fpl_common_api char *fplStringAppend(const char *appended, char *buffer, size_t maxBufferLen);
5081fpl_common_api size_t fplGetStringLength(const char *str);
5091fpl_common_api char *fplCopyStringLen(const char *source, const size_t sourceLen, char *dest, const size_t maxDestLen);
5100fpl_common_api char *fplCopyString(const char *source, char *dest, const size_t maxDestLen);
5110fpl_platform_api size_t fplWideStringToUTF8String(const wchar_t *wideSource, const size_t wideSourceLen, char *utf8Dest, const size_t maxUtf8DestLen);
5120fpl_platform_api size_t fplUTF8StringToWideString(const char *utf8Source, const size_t utf8SourceLen, wchar_t *wideDest, const size_t maxWideDestLen);
5130fpl_common_api size_t fplStringFormat(char *destBuffer, const size_t maxDestBufferLen, const char *format, ...);
5140fpl_common_api size_t fplStringFormatArgs(char *destBuffer, const size_t maxDestBufferLen, const char *format, va_list argList);
5141
5148fpl_common_api int32_t fplStringToS32Len(const char *str, const size_t len);
5149
5155fpl_common_api int32_t fplStringToS32(const char *str);
5156
5164fpl_common_api size_t fplS32ToString(const int32_t value, char *buffer, const size_t maxBufferLen);
5165
5168// ----------------------------------------------------------------------------
5174// ----------------------------------------------------------------------------
5175
5178#if defined(FPL_PLATFORM_WINDOWS)
5180 fpl__Win32FileHandle win32FileHandle;
5181#elif defined(FPL_SUBPLATFORM_POSIX)
5183 fpl__POSIXFileHandle posixFileHandle;
5184#endif
5186
5194
5204
5214
5228
5242
5244typedef union fplFilePermissions {
5245 struct {
5247 uint8_t user;
5249 uint8_t group;
5251 uint8_t owner;
5253 uint8_t unused;
5254 };
5256 uint32_t umask;
5258
5274
5277#if defined(FPL_PLATFORM_WINDOWS)
5279 fpl__Win32FileHandle win32FileHandle;
5280#elif defined(FPL_SUBPLATFORM_POSIX)
5282 fpl__POSIXDirHandle posixDirHandle;
5283#endif
5285
5293
5295typedef uint64_t fplFileTimeStamp;
5296
5306
5326
5334fpl_platform_api bool fplFileOpenBinary(const char *filePath, fplFileHandle *outHandle);
5342fpl_platform_api bool fplFileCreateBinary(const char *filePath, fplFileHandle *outHandle);
5353fpl_platform_api uint32_t fplFileReadBlock32(const fplFileHandle *fileHandle, const uint32_t sizeToRead, void *targetBuffer, const uint32_t maxTargetBufferSize);
5364fpl_platform_api uint64_t fplFileReadBlock64(const fplFileHandle *fileHandle, const uint64_t sizeToRead, void *targetBuffer, const uint64_t maxTargetBufferSize);
5375fpl_platform_api size_t fplFileReadBlock(const fplFileHandle *fileHandle, const size_t sizeToRead, void *targetBuffer, const size_t maxTargetBufferSize);
5385fpl_platform_api uint32_t fplFileWriteBlock32(const fplFileHandle *fileHandle, void *sourceBuffer, const uint32_t sourceSize);
5395fpl_platform_api uint64_t fplFileWriteBlock64(const fplFileHandle *fileHandle, void *sourceBuffer, const uint64_t sourceSize);
5405fpl_common_api size_t fplFileWriteBlock(const fplFileHandle *fileHandle, void *sourceBuffer, const size_t sourceSize);
5414fpl_platform_api uint32_t fplFileSetPosition32(const fplFileHandle *fileHandle, const int32_t position, const fplFilePositionMode mode);
5423fpl_platform_api uint64_t fplFileSetPosition64(const fplFileHandle *fileHandle, const int64_t position, const fplFilePositionMode mode);
5432fpl_common_api size_t fplFileSetPosition(const fplFileHandle *fileHandle, const intptr_t position, const fplFilePositionMode mode);
5469
5476fpl_platform_api uint32_t fplFileGetSizeFromPath32(const char *filePath);
5483fpl_platform_api uint64_t fplFileGetSizeFromPath64(const char *filePath);
5490fpl_platform_api size_t fplFileGetSizeFromPath(const char *filePath);
5532fpl_platform_api bool fplFileSetTimestamps(const char *filePath, const fplFileTimeStamps *timeStamps);
5538fpl_platform_api bool fplFileExists(const char *filePath);
5546fpl_platform_api bool fplFileCopy(const char *sourceFilePath, const char *targetFilePath, const bool overwrite);
5553fpl_platform_api bool fplFileMove(const char *sourceFilePath, const char *targetFilePath);
5559fpl_platform_api bool fplFileDelete(const char *filePath);
5560
5589fpl_platform_api bool fplDirectoryListBegin(const char *path, const char *filter, fplFileEntry *entry);
5606
5609// ----------------------------------------------------------------------------
5615// ----------------------------------------------------------------------------
5616
5624fpl_platform_api size_t fplGetExecutableFilePath(char *destPath, const size_t maxDestLen);
5632fpl_platform_api size_t fplGetHomePath(char *destPath, const size_t maxDestLen);
5641fpl_common_api size_t fplExtractFilePath(const char *sourcePath, char *destPath, const size_t maxDestLen);
5648fpl_common_api const char *fplExtractFileExtension(const char *sourcePath);
5655fpl_common_api const char *fplExtractFileName(const char *sourcePath);
5665fpl_common_api size_t fplChangeFileExtension(const char *filePath, const char *newFileExtension, char *destPath, const size_t maxDestLen);
5675fpl_common_api size_t fplPathCombine(char *destPath, const size_t maxDestPathLen, const size_t pathCount, ...);
5676
5679#if defined(FPL__ENABLE_WINDOW)
5680// ----------------------------------------------------------------------------
5686// ----------------------------------------------------------------------------
5687
5689typedef enum fplKey {
5690 fplKey_None = 0,
5691
5692 // 0x0-0x07: Undefined
5693
5694 fplKey_Backspace = 0x08,
5695 fplKey_Tab = 0x09,
5696
5697 // 0x0A-0x0B: Reserved
5698
5699 fplKey_Clear = 0x0C,
5700 fplKey_Return = 0x0D,
5701
5702 // 0x0E-0x0F: Undefined
5703
5704 fplKey_Shift = 0x10,
5705 fplKey_Control = 0x11,
5706 fplKey_Alt = 0x12,
5707 fplKey_Pause = 0x13,
5708 fplKey_CapsLock = 0x14,
5709
5710 // 0x15: IME-Keys
5711 // 0x16: Undefined
5712 // 0x17-0x19 IME-Keys
5713 // 0x1A: Undefined
5714
5715 fplKey_Escape = 0x1B,
5716
5717 // 0x1C-0x1F: IME-Keys
5718
5719 fplKey_Space = 0x20,
5720 fplKey_PageUp = 0x21,
5721 fplKey_PageDown = 0x22,
5722 fplKey_End = 0x23,
5723 fplKey_Home = 0x24,
5724 fplKey_Left = 0x25,
5725 fplKey_Up = 0x26,
5726 fplKey_Right = 0x27,
5727 fplKey_Down = 0x28,
5728 fplKey_Select = 0x29,
5729 fplKey_Print = 0x2A,
5730 fplKey_Execute = 0x2B,
5731 fplKey_Snapshot = 0x2C,
5732 fplKey_Insert = 0x2D,
5733 fplKey_Delete = 0x2E,
5734 fplKey_Help = 0x2F,
5735
5736 fplKey_0 = 0x30,
5737 fplKey_1 = 0x31,
5738 fplKey_2 = 0x32,
5739 fplKey_3 = 0x33,
5740 fplKey_4 = 0x34,
5741 fplKey_5 = 0x35,
5742 fplKey_6 = 0x36,
5743 fplKey_7 = 0x37,
5744 fplKey_8 = 0x38,
5745 fplKey_9 = 0x39,
5746
5747 // 0x3A-0x40: Undefined
5748
5749 fplKey_A = 0x41,
5750 fplKey_B = 0x42,
5751 fplKey_C = 0x43,
5752 fplKey_D = 0x44,
5753 fplKey_E = 0x45,
5754 fplKey_F = 0x46,
5755 fplKey_G = 0x47,
5756 fplKey_H = 0x48,
5757 fplKey_I = 0x49,
5758 fplKey_J = 0x4A,
5759 fplKey_K = 0x4B,
5760 fplKey_L = 0x4C,
5761 fplKey_M = 0x4D,
5762 fplKey_N = 0x4E,
5763 fplKey_O = 0x4F,
5764 fplKey_P = 0x50,
5765 fplKey_Q = 0x51,
5766 fplKey_R = 0x52,
5767 fplKey_S = 0x53,
5768 fplKey_T = 0x54,
5769 fplKey_U = 0x55,
5770 fplKey_V = 0x56,
5771 fplKey_W = 0x57,
5772 fplKey_X = 0x58,
5773 fplKey_Y = 0x59,
5774 fplKey_Z = 0x5A,
5775
5776 fplKey_LeftSuper = 0x5B,
5777 fplKey_RightSuper = 0x5C,
5778 fplKey_Apps = 0x5D,
5779
5780 // 0x5E: Reserved
5781
5782 fplKey_Sleep = 0x5F,
5783 fplKey_NumPad0 = 0x60,
5784 fplKey_NumPad1 = 0x61,
5785 fplKey_NumPad2 = 0x62,
5786 fplKey_NumPad3 = 0x63,
5787 fplKey_NumPad4 = 0x64,
5788 fplKey_NumPad5 = 0x65,
5789 fplKey_NumPad6 = 0x66,
5790 fplKey_NumPad7 = 0x67,
5791 fplKey_NumPad8 = 0x68,
5792 fplKey_NumPad9 = 0x69,
5793 fplKey_Multiply = 0x6A,
5794 fplKey_Add = 0x6B,
5795 fplKey_Separator = 0x6C,
5796 fplKey_Substract = 0x6D,
5797 fplKey_Decimal = 0x6E,
5798 fplKey_Divide = 0x6F,
5799 fplKey_F1 = 0x70,
5800 fplKey_F2 = 0x71,
5801 fplKey_F3 = 0x72,
5802 fplKey_F4 = 0x73,
5803 fplKey_F5 = 0x74,
5804 fplKey_F6 = 0x75,
5805 fplKey_F7 = 0x76,
5806 fplKey_F8 = 0x77,
5807 fplKey_F9 = 0x78,
5808 fplKey_F10 = 0x79,
5809 fplKey_F11 = 0x7A,
5810 fplKey_F12 = 0x7B,
5811 fplKey_F13 = 0x7C,
5812 fplKey_F14 = 0x7D,
5813 fplKey_F15 = 0x7E,
5814 fplKey_F16 = 0x7F,
5815 fplKey_F17 = 0x80,
5816 fplKey_F18 = 0x81,
5817 fplKey_F19 = 0x82,
5818 fplKey_F20 = 0x83,
5819 fplKey_F21 = 0x84,
5820 fplKey_F22 = 0x85,
5821 fplKey_F23 = 0x86,
5822 fplKey_F24 = 0x87,
5823
5824 // 0x88-8F: Unassigned
5825
5826 fplKey_NumLock = 0x90,
5827 fplKey_Scroll = 0x91,
5828
5829 // 0x92-9x96: OEM specific
5830 // 0x97-0x9F: Unassigned
5831
5832 fplKey_LeftShift = 0xA0,
5833 fplKey_RightShift = 0xA1,
5834 fplKey_LeftControl = 0xA2,
5835 fplKey_RightControl = 0xA3,
5836 fplKey_LeftAlt = 0xA4,
5837 fplKey_RightAlt = 0xA5,
5838
5839 // 0xA6-0xAC: Dont care
5840
5841 fplKey_VolumeMute = 0xAD,
5842 fplKey_VolumeDown = 0xAE,
5843 fplKey_VolumeUp = 0xAF,
5844 fplKey_MediaNextTrack = 0xB0,
5845 fplKey_MediaPrevTrack = 0xB1,
5846 fplKey_MediaStop = 0xB2,
5847 fplKey_MediaPlayPause = 0xB3,
5848
5849 // 0xB4-0xB9 Dont care
5850
5865
5866 // 0xC1-0xD7 Reserved
5867 // 0xD8-0xDA Unassigned
5868
5877 fplKey_Oem8 = 0xDF,
5878
5879 // 0xE0-0xFE Dont care
5881
5911
5921
5935
5945
5955
5985
5999
6011
6025
6041
6053
6059
6095
6159
6171
6185
6201
6209
6216
6219// ----------------------------------------------------------------------------
6225// ----------------------------------------------------------------------------
6226
6228#define FPL_MAX_KEYBOARD_STATE_COUNT 256
6229
6239
6241#define FPL_MAX_GAMEPAD_STATE_COUNT 4
6242
6248
6258
6277
6283fpl_platform_api bool fplQueryCursorPosition(int32_t *outX, int32_t *outY);
6284
6287// ----------------------------------------------------------------------------
6293// ----------------------------------------------------------------------------
6294
6308
6318
6349fpl_platform_api void fplSetWindowSize(const uint32_t width, const uint32_t height);
6389fpl_platform_api bool fplSetWindowFullscreenSize(const bool value, const uint32_t fullscreenWidth, const uint32_t fullscreenHeight, const uint32_t refreshRate);
6400fpl_platform_api bool fplSetWindowFullscreenRect(const bool value, const int32_t x, const int32_t y, const int32_t width, const int32_t height);
6429fpl_platform_api void fplSetWindowPosition(const int32_t left, const int32_t top);
6434fpl_platform_api void fplSetWindowTitle(const char *title);
6441fpl_common_api char *fplGetWindowTitle(char *outTitle, const size_t maxOutTitleLength);
6459
6462// ----------------------------------------------------------------------------
6468// ----------------------------------------------------------------------------
6469
6483
6485typedef struct fplDisplayMode {
6487 uint32_t width;
6489 uint32_t height;
6491 uint32_t colorBits;
6493 uint32_t refreshRate;
6495
6507fpl_platform_api size_t fplGetDisplays(fplDisplayInfo *outDisplays, const size_t maxDisplayCount);
6527fpl_platform_api bool fplGetDisplayFromPosition(const int32_t x, const int32_t y, fplDisplayInfo *outInfo);
6535fpl_platform_api size_t fplGetDisplayModes(const char *id, fplDisplayMode *outModes, const size_t maxDisplayModeCount);
6536
6539// ----------------------------------------------------------------------------
6545// ----------------------------------------------------------------------------
6546
6553fpl_platform_api bool fplGetClipboardText(char *dest, const uint32_t maxDestLen);
6560
6562#endif // FPL__ENABLE_WINDOW
6563
6564#if defined(FPL__ENABLE_VIDEO)
6565// ----------------------------------------------------------------------------
6571// ----------------------------------------------------------------------------
6572
6574typedef struct fplVideoRect {
6576 int32_t x;
6578 int32_t y;
6580 int32_t width;
6582 int32_t height;
6584
6593fpl_inline fplVideoRect fplCreateVideoRectFromLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom) {
6594 fplVideoRect result = { left, top, (right - left) + 1, (bottom - top) + 1 };
6595 return(result);
6596}
6597
6615
6616#if defined(FPL__ENABLE_VIDEO_VULKAN)
6624#endif
6625
6626#if defined(FPL__ENABLE_VIDEO_OPENGL)
6632#endif
6633
6634#if defined(FPL_PLATFORM_WINDOWS)
6636typedef struct fplVideoWindowWin32 {
6638 fpl__Win32WindowHandle windowHandle;
6640 fpl__Win32DeviceContext deviceContext;
6641} fplVideoWindowWin32;
6642#endif
6643
6644#if defined(FPL_SUBPLATFORM_X11)
6646typedef struct fplVideoWindowX11 {
6648 fpl__X11Window window;
6650 fpl__X11Display display;
6652 fpl__X11Visual visual;
6654 int screen;
6655} fplVideoWindowX11;
6656#endif // FPL_SUBPLATFORM_X11
6657
6659typedef union fplVideoWindow {
6660#if defined(FPL_PLATFORM_WINDOWS)
6661 fplVideoWindowWin32 win32;
6662#elif defined(FPL_SUBPLATFORM_X11)
6663 fplVideoWindowX11 x11;
6664#endif
6668
6670typedef struct fplVideoSurface {
6673
6674#if defined(FPL__ENABLE_VIDEO_VULKAN)
6677#endif
6678
6679#if defined(FPL__ENABLE_VIDEO_OPENGL)
6682#endif
6683
6687
6688#if defined(FPL__ENABLE_VIDEO_VULKAN)
6696#endif // FPL__ENABLE_VIDEO_VULKAN
6697
6698
6701#if defined(FPL__ENABLE_VIDEO_VULKAN)
6704#endif // FPL__ENABLE_VIDEO_VULKAN
6706 int dummy;
6708
6732fpl_common_api bool fplResizeVideoBackBuffer(const uint32_t width, const uint32_t height);
6733
6738
6744fpl_common_api const void *fplGetVideoProcedure(const char *procName);
6745
6751
6759
6761#endif // FPL__ENABLE_VIDEO
6762
6763#if defined(FPL__ENABLE_AUDIO)
6764// ----------------------------------------------------------------------------
6770// ----------------------------------------------------------------------------
6771
6810
6846fpl_common_api uint32_t fplGetAudioDevices(fplAudioDeviceInfo *devices, uint32_t maxDeviceCount);
6871fpl_common_api uint32_t fplGetAudioBufferSizeInFrames(uint32_t sampleRate, uint32_t bufferSizeInMilliSeconds);
6878fpl_common_api uint32_t fplGetAudioBufferSizeInMilliseconds(uint32_t sampleRate, uint32_t frameCount);
6885fpl_common_api uint32_t fplGetAudioFrameSizeInBytes(const fplAudioFormatType format, const uint32_t channelCount);
6893fpl_common_api uint32_t fplGetAudioBufferSizeInBytes(const fplAudioFormatType format, const uint32_t channelCount, const uint32_t frameCount);
6900
6902#endif // FPL__ENABLE_AUDIO
6903
6904// ----------------------------------------------------------------------------
6910// ----------------------------------------------------------------------------
6911
6919
6927fpl_platform_api size_t fplGetUserLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen);
6928
6936fpl_platform_api size_t fplGetSystemLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen);
6937
6945fpl_platform_api size_t fplGetInputLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen);
6946
6949// Ignore any doxygen documentation from here
6951
6952// ****************************************************************************
6953//
6954// > EXPORT
6955//
6956// Internal functions for static library
6957// Entry-Point forward declaration
6958//
6959// ****************************************************************************
6960#if defined(FPL_PLATFORM_WINDOWS)
6961# if defined(FPL_ENTRYPOINT)
6962// @NOTE(final): Required for access "main" from the actual win32 entry point
6963fpl_main int main(int argc, char **args);
6964# endif
6965#endif // FPL_PLATFORM_WINDOWS
6966
6967#endif // FPL_HEADER_H
6968
6969// ****************************************************************************
6970//
6971// > IMPLEMENTATION
6972//
6973// FPL uses several implementation blocks to structure things in categories.
6974// Each block has its own ifdef definition to collapse it down if needed.
6975// But the baseline structure is the following:
6976//
6977// - Compiler settings (Disable warnings, etc.)
6978// - Platform Constants & Types (All required structs, Constants, Global variables, etc.)
6979// - Common implementations
6980// - Actual platform implementations (Win32, Linux)
6981// - Sub platform implementations (X11, POSIX, STD)
6982// - Backend implementations (Video: OpenGL/Software/Vulkan, Audio: DirectSound/Alsa)
6983// - Systems (Audio, Video, Window systems)
6984// - Core (Init & Release of the specific platform by selection)
6985//
6986// You can use the following strings to search for implementation blocks - including the > prefix:
6987//
6988// > COMPILER_CONFIG
6989// > PLATFORM_INCLUDES
6990//
6991// > INTERNAL_TOP
6992// > INTERNAL_LOGGING
6993//
6994// > PLATFORM_CONSTANTS
6995// > UTILITY_FUNCTIONS
6996//
6997// > TYPES
6998// > TYPES_WIN32
6999// > TYPES_POSIX
7000// > TYPES_LINUX
7001// > TYPES_X11
7002//
7003// > PLATFORM_STATES
7004//
7005// > COMMON
7006//
7007// > WIN32_PLATFORM
7008// > POSIX_SUBPLATFORM (Linux, Unix)
7009// > STD_STRINGS_SUBPLATFORM
7010// > STD_CONSOLE_SUBPLATFORM
7011// > X11_SUBPLATFORM
7012// > LINUX_PLATFORM
7013// > UNIX_PLATFORM
7014//
7015// > VIDEO_BACKENDS
7016// > VIDEO_BACKEND_OPENGL_WIN32
7017// > VIDEO_BACKEND_OPENGL_X11
7018// > VIDEO_BACKEND_SOFTWARE_WIN32
7019// > VIDEO_BACKEND_SOFTWARE_X11
7020// > VIDEO_BACKEND_VULKAN
7021//
7022// > AUDIO_BACKENDS
7023// > AUDIO_BACKEND_DIRECTSOUND
7024// > AUDIO_BACKEND_ALSA
7025//
7026// > SYSTEM_AUDIO_L1
7027// > SYSTEM_VIDEO_L1
7028// > SYSTEM_WINDOW
7029// > SYSTEM_AUDIO_L2
7030// > SYSTEM_VIDEO_L2 (Video backbuffer access and present of the frame)
7031// > SYSTEM_INIT (Init & Release of the Platform)
7032//
7033// ****************************************************************************
7034#if (defined(FPL_IMPLEMENTATION) || FPL_IS_IDE) && !defined(FPL__IMPLEMENTED)
7035#define FPL__IMPLEMENTED
7036
7037// ############################################################################
7038//
7039// > COMPILER_CONFIG
7040//
7041// ############################################################################
7042#if !defined(FPL__COMPILER_CONFIG_DEFINED)
7043#define FPL__COMPILER_CONFIG_DEFINED
7044
7045//
7046// Compiler warnings
7047//
7048#if defined(FPL_COMPILER_MSVC)
7049
7050 // Start to overwrite warning settings (MSVC)
7051# pragma warning( push )
7052
7053 // Disable noexcept compiler warning for C++
7054# pragma warning( disable : 4577 )
7055 // Disable "switch statement contains 'default' but no 'case' labels" compiler warning for C++
7056# pragma warning( disable : 4065 )
7057 // Disable "conditional expression is constant" warning
7058# pragma warning( disable : 4127 )
7059 // Disable "unreferenced formal parameter" warning
7060# pragma warning( disable : 4100 )
7061 // Disable "nonstandard extension used: nameless struct/union" warning
7062# pragma warning( disable : 4201 )
7063 // Disable "local variable is initialized but not referenced" warning
7064# pragma warning( disable : 4189 )
7065 // Disable "nonstandard extension used: non-constant aggregate initializer" warning
7066# pragma warning( disable : 4204 )
7067
7068#elif defined(FPL_COMPILER_GCC)
7069
7070 // Start to overwrite warning settings (GCC)
7071# pragma GCC diagnostic push
7072// Disable warning -Wunused-variable
7073# pragma GCC diagnostic ignored "-Wunused-variable"
7074// Disable warning -Wunused-function
7075# pragma GCC diagnostic ignored "-Wunused-function"
7076
7077#elif defined(FPL_COMPILER_CLANG)
7078
7079 // Start to overwrite warning settings (Clang)
7080# pragma clang diagnostic push
7081
7082// Disable warning -Wunused-variable
7083# pragma clang diagnostic ignored "-Wunused-variable"
7084// Disable warning -Wunused-function
7085# pragma clang diagnostic ignored "-Wunused-function"
7086
7087#endif // FPL_COMPILER
7088
7089#endif // FPL__COMPILER_CONFIG_DEFINED
7090
7091// ############################################################################
7092//
7093// > PLATFORM_INCLUDES
7094//
7095// ############################################################################
7096#if !defined(FPL__PLATFORM_INCLUDES_DEFINED)
7097#define FPL__PLATFORM_INCLUDES_DEFINED
7098
7099#if !defined(FPL__HAS_PLATFORM_INCLUDES)
7100# define FPL__HAS_PLATFORM_INCLUDES
7101
7102# if defined(FPL_PLATFORM_WINDOWS)
7103 // @NOTE(final): windef.h defines min/max macros in lowerspace, this will break for example std::min/max so we have to tell the header we dont want this!
7104# if !defined(NOMINMAX)
7105# define NOMINMAX
7106# endif
7107 // @NOTE(final): For now we dont want any network, com or gdi stuff at all, maybe later who knows.
7108# if !defined(WIN32_LEAN_AND_MEAN)
7109# define WIN32_LEAN_AND_MEAN 1
7110# endif
7111 // @STUPID(final): Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here"
7112struct IUnknown;
7113# include <windows.h> // Win32 api
7114# if _WIN32_WINNT < 0x0600
7115# error "Windows Vista or higher required!"
7116# endif
7117# endif // FPL_PLATFORM_WINDOWS
7118
7119# if defined(FPL_SUBPLATFORM_POSIX)
7120# include <pthread.h> // pthread_t, pthread_mutex_, pthread_cond_, pthread_barrier_
7121# include <sched.h> // sched_param, sched_get_priority_max, SCHED_FIFO
7122# include <semaphore.h> // sem_t
7123# include <dirent.h> // DIR, dirent
7124# endif // FPL_SUBPLATFORM_POSIX
7125
7126# if defined(FPL_SUBPLATFORM_X11)
7127# include <X11/X.h> // Window
7128# include <X11/Xlib.h> // Display
7129# include <X11/Xutil.h> // XVisualInfo
7130# include <X11/Xatom.h> // XA_CARDINAL
7131# endif // FPL_SUBPLATFORM_X11
7132
7133#endif // !FPL__HAS_PLATFORM_INCLUDES
7134
7135//
7136// Test OS handles
7137//
7138#if defined(FPL_PLATFORM_WINDOWS)
7139fplStaticAssert(sizeof(fpl__Win32Handle) >= sizeof(HANDLE));
7140fplStaticAssert(sizeof(fpl__Win32LibraryHandle) >= sizeof(HANDLE));
7141fplStaticAssert(sizeof(fpl__Win32FileHandle) >= sizeof(HANDLE));
7142fplStaticAssert(sizeof(fpl__Win32ThreadHandle) >= sizeof(HANDLE));
7143fplStaticAssert(sizeof(fpl__Win32MutexHandle) >= sizeof(CRITICAL_SECTION));
7144fplStaticAssert(sizeof(fpl__Win32SemaphoreHandle) >= sizeof(HANDLE));
7145fplStaticAssert(sizeof(fpl__Win32ConditionVariable) >= sizeof(CONDITION_VARIABLE));
7146#elif defined(FPL_SUBPLATFORM_POSIX)
7147fplStaticAssert(sizeof(fpl__POSIXLibraryHandle) >= sizeof(void *));
7148fplStaticAssert(sizeof(fpl__POSIXFileHandle) >= sizeof(int));
7149fplStaticAssert(sizeof(fpl__POSIXDirHandle) >= sizeof(DIR *));
7150fplStaticAssert(sizeof(fpl__POSIXThreadHandle) >= sizeof(pthread_t));
7151fplStaticAssert(sizeof(fpl__POSIXMutexHandle) >= sizeof(pthread_mutex_t));
7152fplStaticAssert(sizeof(fpl__POSIXSemaphoreHandle) >= sizeof(sem_t));
7153fplStaticAssert(sizeof(fpl__POSIXConditionVariable) >= sizeof(pthread_cond_t));
7154#endif // FPL_PLATFORM_WINDOWS / FPL_SUBPLATFORM_POSIX
7155#if defined(FPL_PLATFORM_LINUX)
7156fplStaticAssert(sizeof(fpl__LinuxSignalHandle) >= sizeof(int));
7157#endif // FPL_PLATFORM_LINUX
7158
7159//
7160// Compiler Includes
7161//
7162#if defined(FPL_COMPILER_MSVC)
7163# include <intrin.h> // __cpuid, _Interlocked*
7164#elif defined(FPL_COMPILER_GCC) || defined(FPL_COMPILER_CLANG)
7165# if defined(FPL_ARCH_X86) || defined(FPL_ARCH_X64)
7166# include <cpuid.h> // __cpuid_count
7167# endif // X86 or X64
7168#endif
7169
7170// Only include C-Runtime functions when CRT is enabled
7171#if !defined(FPL_NO_CRT)
7172# include <stdio.h> // stdin, stdout, stderr, fprintf, vfprintf, vsnprintf, getchar
7173# include <stdlib.h> // wcstombs, mbstowcs, getenv
7174# include <locale.h> // setlocale, struct lconv, localeconv
7175#endif
7176
7177#endif // FPL__PLATFORM_INCLUDES_DEFINED
7178
7179// ############################################################################
7180//
7181// > INTERNAL_TOP
7182//
7183// ############################################################################
7184#if !defined(FPL__INTERNAL_TOP_DEFINED)
7185#define FPL__INTERNAL_TOP_DEFINED
7186
7187// Module constants used for logging
7188#define FPL__MODULE_CORE "Core"
7189#define FPL__MODULE_FILES "Files"
7190#define FPL__MODULE_THREADING "Threading"
7191#define FPL__MODULE_MEMORY "Memory"
7192#define FPL__MODULE_WINDOW "Window"
7193#define FPL__MODULE_LIBRARIES "Libraries"
7194#define FPL__MODULE_OS "OS"
7195#define FPL__MODULE_HARDWARE "Hardware"
7196#define FPL__MODULE_STRINGS "Strings"
7197#define FPL__MODULE_PATHS "Paths"
7198#define FPL__MODULE_ARGS "Arguments"
7199
7200#define FPL__MODULE_AUDIO "Audio"
7201#define FPL__MODULE_AUDIO_DIRECTSOUND "DirectSound"
7202#define FPL__MODULE_AUDIO_ALSA "ALSA"
7203
7204#define FPL__MODULE_VIDEO "Video"
7205#define FPL__MODULE_VIDEO_OPENGL "OpenGL"
7206#define FPL__MODULE_VIDEO_VULKAN "Vulkan"
7207#define FPL__MODULE_VIDEO_SOFTWARE "Software"
7208
7209#define FPL__MODULE_WIN32 "Win32"
7210#define FPL__MODULE_XINPUT "XInput"
7211
7212#define FPL__MODULE_LINUX "Linux"
7213#define FPL__MODULE_UNIX "Unix"
7214#define FPL__MODULE_POSIX "POSIX"
7215#define FPL__MODULE_PTHREAD "pthread"
7216#define FPL__MODULE_X11 "X11"
7217#define FPL__MODULE_GLX "GLX"
7218
7219//
7220// Enum macros
7221//
7222#define FPL__ENUM_COUNT(first, last) ((last) - (first) + 1)
7223#define FPL__ENUM_VALUE_TO_ARRAY_INDEX(value, first, last) (((value) >= (first) && (value) <= (last)) ? ((value) - (first)) : 0)
7224
7225//
7226// Internal memory
7227//
7228fpl_internal void *fpl__AllocateMemory(const fplMemoryAllocationSettings *allocSettings, const size_t size, const size_t alignment);
7229fpl_internal void fpl__ReleaseMemory(const fplMemoryAllocationSettings *allocSettings, void *ptr);
7230
7231#endif // FPL__INTERNAL_TOP_DEFINED
7232
7233// ############################################################################
7234//
7235// > INTERNAL_LOGGING
7236//
7237// ############################################################################
7238#if !defined(FPL__INTERNAL_LOGGING_DEFINED)
7239#define FPL__INTERNAL_LOGGING_DEFINED
7240
7241#define FPL__MODULE_CONCAT(mod, format) "[" mod "] " format
7242
7243#if defined(FPL__ENABLE_LOGGING)
7244fpl_globalvar fplLogSettings fpl__global__LogSettings = fplZeroInit;
7245
7246#define FPL__LOGLEVEL_COUNT FPL__ENUM_COUNT(fplLogLevel_First, fplLogLevel_Last)
7247fpl_globalvar const char *fpl__LogLevelNameTable[] = {
7248 "All", // fplLogLevel_All (-1)
7249 "Critical", // fplLogLevel_Critical (0)
7250 "Error", // fplLogLevel_Error (1)
7251 "Warning", // fplLogLevel_Warning (2)
7252 "Info", // fplLogLevel_Info (3)
7253 "Verbose", // fplLogLevel_Verbose (4)
7254 "Debug", // fplLogLevel_Debug (5)
7255 "Trace", // fplLogLevel_Trace (6)
7256};
7257fplStaticAssert(fplArrayCount(fpl__LogLevelNameTable) == FPL__LOGLEVEL_COUNT);
7258
7259fpl_internal const char *fpl__LogLevelToString(const fplLogLevel level) {
7260 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(level, fplLogLevel_First, fplLogLevel_Last);
7261 const char *result = fpl__LogLevelNameTable[index];
7262 return(result);
7263}
7264
7265fpl_internal void fpl__LogWrite(const char *funcName, const int lineNumber, const fplLogLevel level, const char *message) {
7266 fplLogSettings *settings = &fpl__global__LogSettings;
7267 if (!settings->isInitialized) {
7268#if defined(FPL_LOG_MULTIPLE_WRITERS)
7269 settings->criticalWriter.console.logToError = true;
7270 settings->criticalWriter.flags = fplLogWriterFlags_ErrorConsole | fplLogWriterFlags_DebugOut;
7271 settings->errorWriter = settings->criticalWriter;
7272 settings->warningWriter = settings->criticalWriter;
7274 settings->verboseWriter = settings->infoWriter;
7275 settings->debugWriter.flags = fplLogWriterFlags_DebugOut;
7276#else
7278#endif
7279 settings->maxLevel = fplLogLevel_Warning;
7280 settings->isInitialized = true;
7281 }
7282
7283 if ((settings->maxLevel == -1) || (level <= settings->maxLevel)) {
7284#if defined(FPL_LOG_MULTIPLE_WRITERS)
7285 fplAssert(level < fplArrayCount(settings->writers));
7286 const fplLogWriter *writer = &settings->writers[(int)level];
7287#else
7288 const fplLogWriter *writer = &settings->writers[0];
7289#endif
7290 const char *levelStr = fpl__LogLevelToString(level);
7291
7293 fplConsoleFormatOut("[%s:%d][%s] %s\n", funcName, lineNumber, levelStr, message);
7294 }
7295 if (writer->flags & fplLogWriterFlags_ErrorConsole) {
7296 fplConsoleFormatError("[%s:%d][%s] %s\n", funcName, lineNumber, levelStr, message);
7297 }
7298 if (writer->flags & fplLogWriterFlags_DebugOut) {
7299 fplDebugFormatOut("[%s:%d][%s] %s\n", funcName, lineNumber, levelStr, message);
7300 }
7301 if (writer->flags & fplLogWriterFlags_Custom && writer->custom.callback != fpl_null) {
7302 writer->custom.callback(funcName, lineNumber, level, message);
7303 }
7304 }
7305}
7306fpl_internal void fpl__LogWriteArgs(const char *funcName, const int lineNumber, const fplLogLevel level, const char *format, va_list argList) {
7307 va_list listCopy;
7308 va_copy(listCopy, argList);
7309 char buffer[FPL_MAX_BUFFER_LENGTH];
7310 size_t formattedLen = fplStringFormatArgs(buffer, fplArrayCount(buffer), format, listCopy);
7311 if (formattedLen > 0) {
7312 fpl__LogWrite(funcName, lineNumber, level, buffer);
7313 }
7314 va_end(listCopy);
7315}
7316
7317fpl_internal void fpl__LogWriteVarArgs(const char *funcName, const int lineNumber, const fplLogLevel level, const char *format, ...) {
7318 va_list argList;
7319 va_start(argList, format);
7320 fpl__LogWriteArgs(funcName, lineNumber, level, format, argList);
7321 va_end(argList);
7322}
7323
7324# define FPL_LOG(lvl, mod, format, ...) fpl__LogWriteVarArgs(FPL_FUNCTION_NAME, __LINE__, lvl, FPL__MODULE_CONCAT(mod, format), ## __VA_ARGS__)
7325# define FPL_LOG_CRITICAL(mod, format, ...) FPL_LOG(fplLogLevel_Critical, mod, format, ## __VA_ARGS__)
7326# define FPL_LOG_ERROR(mod, format, ...) FPL_LOG(fplLogLevel_Error, mod, format, ## __VA_ARGS__)
7327# define FPL_LOG_WARN(mod, format, ...) FPL_LOG(fplLogLevel_Warning, mod, format, ## __VA_ARGS__)
7328# define FPL_LOG_INFO(mod, format, ...) FPL_LOG(fplLogLevel_Info, mod, format, ## __VA_ARGS__)
7329# define FPL_LOG_VERBOSE(mod, format, ...) FPL_LOG(fplLogLevel_Verbose, mod, format, ## __VA_ARGS__)
7330# define FPL_LOG_DEBUG(mod, format, ...) FPL_LOG(fplLogLevel_Debug, mod, format, ## __VA_ARGS__)
7331# define FPL_LOG_TRACE(mod, format, ...) FPL_LOG(fplLogLevel_Trace, mod, format, ## __VA_ARGS__)
7332
7333# define FPL__LOG_FUNCTION_N(mod, name) FPL_LOG(fplLogLevel_Debug, mod, "-> %s()", name)
7334# define FPL_LOG_FUNCTION(mod) FPL__LOG_FUNCTION_N(mod, FPL_FUNCTION_NAME)
7335
7336#else
7337
7338# define FPL_LOG(lvl, mod, format, ...)
7339# define FPL_LOG_CRITICAL(mod, format, ...)
7340# define FPL_LOG_ERROR(mod, format, ...)
7341# define FPL_LOG_WARN(mod, format, ...)
7342# define FPL_LOG_INFO(mod, format, ...)
7343# define FPL_LOG_VERBOSE(mod, format, ...)
7344# define FPL_LOG_DEBUG(mod, format, ...)
7345# define FPL_LOG_FUNCTION(mod)
7346
7347#endif
7348
7349//
7350// Error handling
7351//
7352
7353#define FPL__M_CRITICAL(funcName, line, mod, format, ...) fpl__HandleError(funcName, line, fplLogLevel_Critical, FPL__MODULE_CONCAT(mod, format), ## __VA_ARGS__)
7354#define FPL__M_ERROR(funcName, line, mod, format, ...) fpl__HandleError(funcName, line, fplLogLevel_Error, FPL__MODULE_CONCAT(mod, format), ## __VA_ARGS__)
7355#define FPL__M_WARNING(funcName, line, mod, format, ...) fpl__HandleError(funcName, line, fplLogLevel_Warning, FPL__MODULE_CONCAT(mod, format), ## __VA_ARGS__)
7356
7357#define FPL__CRITICAL(mod, format, ...) FPL__M_CRITICAL(FPL_FUNCTION_NAME, __LINE__, mod, format, ## __VA_ARGS__)
7358#define FPL__ERROR(mod, format, ...) FPL__M_ERROR(FPL_FUNCTION_NAME, __LINE__, mod, format, ## __VA_ARGS__)
7359#define FPL__WARNING(mod, format, ...) FPL__M_WARNING(FPL_FUNCTION_NAME, __LINE__, mod, format, ## __VA_ARGS__)
7360
7361#endif // FPL__INTERNAL_LOGGING_DEFINED
7362
7363// ############################################################################
7364//
7365// > PLATFORM_CONSTANTS
7366//
7367// ############################################################################
7368#if !defined(FPL__PLATFORM_CONSTANTS_DEFINED)
7369#define FPL__PLATFORM_CONSTANTS_DEFINED
7370
7371// One cacheline worth of padding
7372#define FPL__ARBITARY_PADDING 64
7373// Small padding to split sections in memory blocks
7374#define FPL__MEMORY_PADDING sizeof(uintptr_t)
7375
7376fpl_globalvar struct fpl__PlatformAppState *fpl__global__AppState = fpl_null;
7377
7378fpl_internal void fpl__HandleError(const char *funcName, const int lineNumber, const fplLogLevel level, const char *format, ...);
7379#endif // FPL__PLATFORM_CONSTANTS_DEFINED
7380
7381// ############################################################################
7382//
7383// > UTILITY_FUNCTIONS
7384//
7385// ############################################################################
7386fpl_internal void *fpl__AllocateMemory(const fplMemoryAllocationSettings *allocSettings, const size_t size, const size_t alignment) {
7387 if (allocSettings->mode == fplMemoryAllocationMode_Custom) {
7388 if (allocSettings->allocateCallback != fpl_null && allocSettings->releaseCallback != fpl_null) {
7389 return allocSettings->allocateCallback(allocSettings->userData, size, alignment);
7390 }
7391 }
7392 return fplMemoryAlignedAllocate(size, alignment);
7393}
7394
7395fpl_internal void fpl__ReleaseMemory(const fplMemoryAllocationSettings *allocSettings, void *ptr) {
7396 if (allocSettings->mode == fplMemoryAllocationMode_Custom) {
7397 if (allocSettings->allocateCallback != fpl_null && allocSettings->releaseCallback != fpl_null) {
7398 allocSettings->releaseCallback(allocSettings->userData, ptr);
7399 return;
7400 }
7401 }
7403}
7404
7405// Forward declarations of internal memory
7406fpl_internal void *fpl__AllocateDynamicMemory(const size_t size, const size_t alignment);
7407fpl_internal void fpl__ReleaseDynamicMemory(void *ptr);
7408fpl_internal void *fpl__AllocateTemporaryMemory(const size_t size, const size_t alignment);
7409fpl_internal void fpl__ReleaseTemporaryMemory(void *ptr);
7410
7411fpl_internal uint32_t fpl__NextPowerOfTwo(const uint32_t input) {
7412 uint32_t x = input;
7413 x--;
7414 x |= x >> 1;
7415 x |= x >> 2;
7416 x |= x >> 4;
7417 x |= x >> 8;
7418 x |= x >> 16;
7419 x++;
7420 return(x);
7421}
7422fpl_internal uint32_t fpl__PrevPowerOfTwo(const uint32_t input) {
7423 uint32_t result = fpl__NextPowerOfTwo(input) >> 1;
7424 return(result);
7425}
7426
7427fpl_internal uint32_t fpl__RoundToPowerOfTwo(const uint32_t input) {
7428 uint32_t prev = fpl__PrevPowerOfTwo(input);
7429 uint32_t next = fpl__NextPowerOfTwo(input);
7430 if ((next - input) < (input - prev)) {
7431 return prev;
7432 } else {
7433 return next;
7434 }
7435}
7436
7437fpl_internal bool fpl__AddLineWhenAnyMatches(const char *line, const char **wildcards, const size_t maxWildcardCount, const size_t maxLineSize, const size_t maxLineCount, char **outLines, size_t *outCount) {
7438 for (size_t i = 0; i < maxWildcardCount; ++i) {
7439 const char *wildcard = wildcards[i];
7440 if (fplIsStringMatchWildcard(line, wildcard)) {
7441 size_t index = *outCount;
7442 char *target = outLines[index];
7443 fplCopyString(line, target, maxLineSize);
7444 *outCount = index + 1;
7445 break;
7446 }
7447 }
7448 bool result = *outCount < maxLineCount;
7449 return(result);
7450}
7451
7452fpl_internal size_t fpl__ParseTextFile(const char *filePath, const char **wildcards, const size_t maxWildcardCount, const size_t maxLineSize, const size_t maxLineCount, char **outLines) {
7453 if (filePath == fpl_null || wildcards == fpl_null || maxWildcardCount == 0 || maxLineSize == 0 || maxLineCount == 0 || outLines == fpl_null) {
7454 return(0);
7455 }
7456 // @NOTE(final): Forced Zero-Terminator is not nessecary here, but we do it here to debug it better
7457 // This function supports maxLineSize < fplArrayCount(buffer)
7458 // We allocate the line buffer on the stack because we do not know how large the line will be on compile time
7459 size_t result = 0;
7460 fplFileHandle fileHandle = fplZeroInit;
7461 if (fplFileOpenBinary(filePath, &fileHandle)) {
7462 char *line = (char *)fpl__AllocateTemporaryMemory(maxLineSize, 8);
7463 char buffer[FPL_MAX_BUFFER_LENGTH];
7464 const size_t maxBufferSize = fplArrayCount(buffer) - 1;
7465 size_t bytesRead = 0;
7466 size_t posLineBytes = 0;
7467 bool done = false;
7468 while (!done && ((bytesRead = fplFileReadBlock(&fileHandle, maxBufferSize, &buffer[0], maxBufferSize)) > 0)) {
7469 buffer[bytesRead] = 0;
7470 char *start = &buffer[0];
7471 char *p = start;
7472 size_t readPos = 0;
7473 size_t lineSizeToRead = 0;
7474 while (readPos < bytesRead) {
7475 if (*p == '\n') {
7476 size_t remainingLineBytes = maxLineSize - posLineBytes;
7477 char *lineTargetP = line + posLineBytes;
7478 if (lineSizeToRead < remainingLineBytes) {
7479 fplCopyStringLen(start, lineSizeToRead, lineTargetP, remainingLineBytes);
7480 } else {
7481 fplCopyStringLen(start, remainingLineBytes - 1, lineTargetP, remainingLineBytes);
7482 }
7483 if (!fpl__AddLineWhenAnyMatches(line, wildcards, maxWildcardCount, maxLineSize, maxLineCount, outLines, &result)) {
7484 done = true;
7485 break;
7486 }
7487 start = p + 1;
7488 line[0] = 0;
7489 lineSizeToRead = 0;
7490 posLineBytes = 0;
7491 } else {
7492 ++lineSizeToRead;
7493 }
7494 ++p;
7495 ++readPos;
7496 }
7497 if (done) {
7498 break;
7499 }
7500 if (lineSizeToRead > 0) {
7501 size_t remainingLineBytes = maxLineSize - posLineBytes;
7502 char *lineTargetP = line + posLineBytes;
7503 if (lineSizeToRead < remainingLineBytes) {
7504 fplCopyStringLen(start, lineSizeToRead, lineTargetP, remainingLineBytes);
7505 posLineBytes += lineSizeToRead;
7506 if (bytesRead <= maxBufferSize) {
7507 if (!fpl__AddLineWhenAnyMatches(line, wildcards, maxWildcardCount, maxLineSize, maxLineCount, outLines, &result)) {
7508 done = true;
7509 }
7510 }
7511 } else {
7512 fplCopyStringLen(start, remainingLineBytes - 1, lineTargetP, remainingLineBytes);
7513 line[0] = 0;
7514 lineSizeToRead = 0;
7515 posLineBytes = 0;
7516 if (!fpl__AddLineWhenAnyMatches(line, wildcards, maxWildcardCount, maxLineSize, maxLineCount, outLines, &result)) {
7517 done = true;
7518 }
7519 }
7520 }
7521 }
7522 fpl__ReleaseTemporaryMemory(line);
7523 fplFileClose(&fileHandle);
7524 }
7525 return(result);
7526}
7527
7528fpl_internal void fpl__ParseVersionString(const char *versionStr, fplVersionInfo *versionInfo) {
7529 fplCopyString(versionStr, versionInfo->fullName, fplArrayCount(versionInfo->fullName));
7530 if (versionStr != fpl_null) {
7531 const char *p = versionStr;
7532 for (int i = 0; i < 4; ++i) {
7533 const char *digitStart = p;
7534 while (*p >= '0' && *p <= '9') {
7535 ++p;
7536 }
7537 size_t len = p - digitStart;
7538 if (len <= fplArrayCount(versionInfo->values[i])) {
7539 fplCopyStringLen(digitStart, len, versionInfo->values[i], fplArrayCount(versionInfo->values[i]));
7540 } else {
7541 versionInfo->values[i][0] = 0;
7542 }
7543 if (*p != '.' && *p != '-') break;
7544 ++p;
7545 }
7546 }
7547}
7548
7549// ****************************************************************************
7550//
7551// > TYPES
7552//
7553// This implementation block includes all the required platform-specific
7554// header files and defines all the constants, structures and types.
7555//
7556// ****************************************************************************
7557
7558// ############################################################################
7559//
7560// > TYPES_WIN32
7561//
7562// ############################################################################
7563#if defined(FPL_PLATFORM_WINDOWS)
7564# include <windowsx.h> // Macros for window messages
7565# include <shlobj.h> // SHGetFolderPath
7566# include <xinput.h> // XInputGetState
7567# include <shellapi.h> // HDROP
7568
7569# if defined(FPL_IS_CPP)
7570# define fpl__Win32IsEqualGuid(a, b) InlineIsEqualGUID(a, b)
7571# else
7572# define fpl__Win32IsEqualGuid(a, b) InlineIsEqualGUID(&a, &b)
7573# endif
7574
7575fpl_internal const char *fpl__Win32FormatGuidString(char *buffer, const size_t maxBufferLen, const GUID *guid) {
7576 fplStringFormat(buffer, maxBufferLen, "{%08lX-%04hX-%04hX-%02hhX%02hhX-%02hhX%02hhX%02hhX%02hhX%02hhX%02hhX}",
7577 guid->Data1, guid->Data2, guid->Data3,
7578 guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
7579 guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
7580 return(buffer);
7581}
7582
7583// Little macro to not write 5 lines of code all the time
7584#define FPL__WIN32_LOAD_LIBRARY_BREAK(mod, target, libName) \
7585 (target) = LoadLibraryA(libName); \
7586 if((target) == fpl_null) { \
7587 FPL__WARNING(mod, "Failed loading library '%s'", (libName)); \
7588 break; \
7589 }
7590#define FPL__WIN32_GET_FUNCTION_ADDRESS_BREAK(mod, libHandle, libName, target, type, name) \
7591 (target)->name = (type *)GetProcAddress(libHandle, #name); \
7592 if ((target)->name == fpl_null) { \
7593 FPL__WARNING(mod, "Failed getting procedure address '%s' from library '%s'", #name, libName); \
7594 break; \
7595 }
7596#if !defined(FPL_NO_RUNTIME_LINKING)
7597# define FPL__WIN32_LOAD_LIBRARY FPL__WIN32_LOAD_LIBRARY_BREAK
7598# define FPL__WIN32_GET_FUNCTION_ADDRESS FPL__WIN32_GET_FUNCTION_ADDRESS_BREAK
7599#else
7600# define FPL__WIN32_LOAD_LIBRARY(mod, target, libName)
7601# define FPL__WIN32_GET_FUNCTION_ADDRESS(mod, libHandle, libName, target, type, name) \
7602 (target)->name = name
7603#endif
7604
7605//
7606// XInput
7607//
7608#define FPL__FUNC_XINPUT_XInputGetState(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
7609typedef FPL__FUNC_XINPUT_XInputGetState(fpl__win32_func_XInputGetState);
7610FPL__FUNC_XINPUT_XInputGetState(fpl__Win32XInputGetStateStub) {
7611 return(ERROR_DEVICE_NOT_CONNECTED);
7612}
7613#define FPL__FUNC_XINPUT_XInputGetCapabilities(name) DWORD WINAPI name(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES* pCapabilities)
7614typedef FPL__FUNC_XINPUT_XInputGetCapabilities(fpl__win32_func_XInputGetCapabilities);
7615FPL__FUNC_XINPUT_XInputGetCapabilities(fpl__Win32XInputGetCapabilitiesStub) {
7616 return(ERROR_DEVICE_NOT_CONNECTED);
7617}
7618typedef struct fpl__Win32XInputApi {
7619 HMODULE xinputLibrary;
7620 fpl__win32_func_XInputGetState *XInputGetState;
7621 fpl__win32_func_XInputGetCapabilities *XInputGetCapabilities;
7622} fpl__Win32XInputApi;
7623
7624fpl_internal void fpl__Win32UnloadXInputApi(fpl__Win32XInputApi *xinputApi) {
7625 fplAssert(xinputApi != fpl_null);
7626 if (xinputApi->xinputLibrary) {
7627 FPL_LOG_DEBUG("XInput", "Unload XInput Library");
7628 FreeLibrary(xinputApi->xinputLibrary);
7629 xinputApi->xinputLibrary = fpl_null;
7630 xinputApi->XInputGetState = fpl__Win32XInputGetStateStub;
7631 xinputApi->XInputGetCapabilities = fpl__Win32XInputGetCapabilitiesStub;
7632 }
7633}
7634
7635fpl_internal void fpl__Win32LoadXInputApi(fpl__Win32XInputApi *xinputApi) {
7636 fplAssert(xinputApi != fpl_null);
7637 const char *xinputFileNames[] = {
7638 "xinput1_4.dll",
7639 "xinput1_3.dll",
7640 "xinput9_1_0.dll",
7641 };
7642 bool result = false;
7643 for (uint32_t index = 0; index < fplArrayCount(xinputFileNames); ++index) {
7644 const char *libName = xinputFileNames[index];
7645 fplClearStruct(xinputApi);
7646 do {
7647 HMODULE libHandle = fpl_null;
7648 FPL__WIN32_LOAD_LIBRARY_BREAK(FPL__MODULE_XINPUT, libHandle, libName);
7649 xinputApi->xinputLibrary = libHandle;
7650 FPL__WIN32_GET_FUNCTION_ADDRESS_BREAK(FPL__MODULE_XINPUT, libHandle, libName, xinputApi, fpl__win32_func_XInputGetState, XInputGetState);
7651 FPL__WIN32_GET_FUNCTION_ADDRESS_BREAK(FPL__MODULE_XINPUT, libHandle, libName, xinputApi, fpl__win32_func_XInputGetCapabilities, XInputGetCapabilities);
7652 result = true;
7653 } while (0);
7654 if (result) {
7655 break;
7656 }
7657 fpl__Win32UnloadXInputApi(xinputApi);
7658 }
7659
7660 if (!result) {
7661 xinputApi->XInputGetState = fpl__Win32XInputGetStateStub;
7662 xinputApi->XInputGetCapabilities = fpl__Win32XInputGetCapabilitiesStub;
7663 }
7664}
7665
7666//
7667// WINAPI functions
7668//
7669
7670// GDI32
7671#define FPL__FUNC_WIN32_ChoosePixelFormat(name) int WINAPI name(HDC hdc, CONST PIXELFORMATDESCRIPTOR *ppfd)
7672typedef FPL__FUNC_WIN32_ChoosePixelFormat(fpl__win32_func_ChoosePixelFormat);
7673#define FPL__FUNC_WIN32_SetPixelFormat(name) BOOL WINAPI name(HDC hdc, int format, CONST PIXELFORMATDESCRIPTOR *ppfd)
7674typedef FPL__FUNC_WIN32_SetPixelFormat(fpl__win32_func_SetPixelFormat);
7675#define FPL__FUNC_WIN32_DescribePixelFormat(name) int WINAPI name(HDC hdc, int iPixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR ppfd)
7676typedef FPL__FUNC_WIN32_DescribePixelFormat(fpl__win32_func_DescribePixelFormat);
7677#define FPL__FUNC_WIN32_GetDeviceCaps(name) int WINAPI name(HDC hdc, int index)
7678typedef FPL__FUNC_WIN32_GetDeviceCaps(fpl__win32_func_GetDeviceCaps);
7679#define FPL__FUNC_WIN32_StretchDIBits(name) int WINAPI name(HDC hdc, int xDest, int yDest, int DestWidth, int DestHeight, int xSrc, int ySrc, int SrcWidth, int SrcHeight, CONST VOID *lpBits, CONST BITMAPINFO *lpbmi, UINT iUsage, DWORD rop)
7680typedef FPL__FUNC_WIN32_StretchDIBits(fpl__win32_func_StretchDIBits);
7681#define FPL__FUNC_WIN32_DeleteObject(name) BOOL WINAPI name( _In_ HGDIOBJ ho)
7682typedef FPL__FUNC_WIN32_DeleteObject(fpl__win32_func_DeleteObject);
7683#define FPL__FUNC_WIN32_SwapBuffers(name) BOOL WINAPI name(HDC)
7684typedef FPL__FUNC_WIN32_SwapBuffers(fpl__win32_func_SwapBuffers);
7685#define FPL__FUNC_WIN32_CreateDIBSection(name) HBITMAP WINAPI name(HDC hdc, CONST BITMAPINFO *pbmi, UINT usage, VOID **ppvBits, HANDLE hSection, DWORD offset)
7686typedef FPL__FUNC_WIN32_CreateDIBSection(fpl__win32_func_CreateDIBSection);
7687#define FPL__FUNC_WIN32_CreateBitmap(name) HBITMAP WINAPI name(int nWidth, int nHeight, UINT nPlanes, UINT nBitCount, CONST VOID *lpBits)
7688typedef FPL__FUNC_WIN32_CreateBitmap(fpl__win32_func_CreateBitmap);
7689#define FPL__FUNC_WIN32_CreateSolidBrush(name) HBRUSH WINAPI name(COLORREF color)
7690typedef FPL__FUNC_WIN32_CreateSolidBrush(fpl__win32_func_CreateSolidBrush);
7691
7692// ShellAPI
7693#define FPL__FUNC_WIN32_SHGetFolderPathW(name) HRESULT WINAPI name(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath)
7694typedef FPL__FUNC_WIN32_SHGetFolderPathW(fpl__win32_func_SHGetFolderPathW);
7695#define FPL__FUNC_WIN32_DragQueryFileW(name) UINT WINAPI name(HDROP hDrop, UINT iFile, LPWSTR lpszFile, UINT cch)
7696typedef FPL__FUNC_WIN32_DragQueryFileW(fpl__win32_func_DragQueryFileW);
7697#define FPL__FUNC_WIN32_DragAcceptFiles(name) void WINAPI name(HWND hWnd, BOOL fAccept)
7698typedef FPL__FUNC_WIN32_DragAcceptFiles(fpl__win32_func_DragAcceptFiles);
7699
7700// User32
7701#define FPL__FUNC_WIN32_RegisterClassExW(name) ATOM WINAPI name(CONST WNDCLASSEXW *)
7702typedef FPL__FUNC_WIN32_RegisterClassExW(fpl__win32_func_RegisterClassExW);
7703#define FPL__FUNC_WIN32_UnregisterClassW(name) BOOL WINAPI name(LPCWSTR lpClassName, HINSTANCE hInstance)
7704typedef FPL__FUNC_WIN32_UnregisterClassW(fpl__win32_func_UnregisterClassW);
7705#define FPL__FUNC_WIN32_ShowWindow(name) BOOL WINAPI name(HWND hWnd, int nCmdShow)
7706typedef FPL__FUNC_WIN32_ShowWindow(fpl__win32_func_ShowWindow);
7707#define FPL__FUNC_WIN32_DestroyWindow(name) BOOL WINAPI name(HWND hWnd)
7708typedef FPL__FUNC_WIN32_DestroyWindow(fpl__win32_func_DestroyWindow);
7709#define FPL__FUNC_WIN32_UpdateWindow(name) BOOL WINAPI name(HWND hWnd)
7710typedef FPL__FUNC_WIN32_UpdateWindow(fpl__win32_func_UpdateWindow);
7711#define FPL__FUNC_WIN32_TranslateMessage(name) BOOL WINAPI name(CONST MSG *lpMsg)
7712typedef FPL__FUNC_WIN32_TranslateMessage(fpl__win32_func_TranslateMessage);
7713#define FPL__FUNC_WIN32_DispatchMessageW(name) LRESULT WINAPI name(CONST MSG *lpMsg)
7714typedef FPL__FUNC_WIN32_DispatchMessageW(fpl__win32_func_DispatchMessageW);
7715#define FPL__FUNC_WIN32_PeekMessageW(name) BOOL WINAPI name(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg)
7716typedef FPL__FUNC_WIN32_PeekMessageW(fpl__win32_func_PeekMessageW);
7717#define FPL__FUNC_WIN32_DefWindowProcW(name) LRESULT WINAPI name(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
7718typedef FPL__FUNC_WIN32_DefWindowProcW(fpl__win32_func_DefWindowProcW);
7719#define FPL__FUNC_WIN32_CreateWindowExW(name) HWND WINAPI name(DWORD dwExStyle, LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
7720typedef FPL__FUNC_WIN32_CreateWindowExW(fpl__win32_func_CreateWindowExW);
7721#define FPL__FUNC_WIN32_SetWindowPos(name) BOOL WINAPI name(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags)
7722typedef FPL__FUNC_WIN32_SetWindowPos(fpl__win32_func_SetWindowPos);
7723#define FPL__FUNC_WIN32_GetWindowPlacement(name) BOOL WINAPI name(HWND hWnd, WINDOWPLACEMENT *lpwndpl)
7724typedef FPL__FUNC_WIN32_GetWindowPlacement(fpl__win32_func_GetWindowPlacement);
7725#define FPL__FUNC_WIN32_SetWindowPlacement(name) BOOL WINAPI name(HWND hWnd, CONST WINDOWPLACEMENT *lpwndpl)
7726typedef FPL__FUNC_WIN32_SetWindowPlacement(fpl__win32_func_SetWindowPlacement);
7727#define FPL__FUNC_WIN32_GetClientRect(name) BOOL WINAPI name(HWND hWnd, LPRECT lpRect)
7728typedef FPL__FUNC_WIN32_GetClientRect(fpl__win32_func_GetClientRect);
7729#define FPL__FUNC_WIN32_GetWindowRect(name) BOOL WINAPI name(HWND hWnd, LPRECT lpRect)
7730typedef FPL__FUNC_WIN32_GetWindowRect(fpl__win32_func_GetWindowRect);
7731#define FPL__FUNC_WIN32_AdjustWindowRect(name) BOOL WINAPI name(LPRECT lpRect, DWORD dwStyle, BOOL bMenu)
7732typedef FPL__FUNC_WIN32_AdjustWindowRect(fpl__win32_func_AdjustWindowRect);
7733#define FPL__FUNC_WIN32_ClientToScreen(name) BOOL WINAPI name(HWND hWnd, LPPOINT lpPoint)
7734typedef FPL__FUNC_WIN32_ClientToScreen(fpl__win32_func_ClientToScreen);
7735#define FPL__FUNC_WIN32_GetAsyncKeyState(name) SHORT WINAPI name(int vKey)
7736typedef FPL__FUNC_WIN32_GetAsyncKeyState(fpl__win32_func_GetAsyncKeyState);
7737#define FPL__FUNC_WIN32_GetKeyState(name) SHORT WINAPI name(int vKey)
7738typedef FPL__FUNC_WIN32_GetKeyState(fpl__win32_func_GetKeyState);
7739#define FPL__FUNC_WIN32_MapVirtualKeyW(name) UINT WINAPI name(UINT uCode, UINT uMapType)
7740typedef FPL__FUNC_WIN32_MapVirtualKeyW(fpl__win32_func_MapVirtualKeyW);
7741#define FPL__FUNC_WIN32_SetCursor(name) HCURSOR WINAPI name(HCURSOR hCursor)
7742typedef FPL__FUNC_WIN32_SetCursor(fpl__win32_func_SetCursor);
7743#define FPL__FUNC_WIN32_GetCursor(name) HCURSOR WINAPI name(VOID)
7744typedef FPL__FUNC_WIN32_GetCursor(fpl__win32_func_GetCursor);
7745#define FPL__FUNC_WIN32_GetCursorPos(name) BOOL WINAPI name(LPPOINT lpPoint)
7746typedef FPL__FUNC_WIN32_GetCursorPos(fpl__win32_func_GetCursorPos);
7747#define FPL__FUNC_WIN32_WindowFromPoint(name) HWND WINAPI name(POINT Point)
7748typedef FPL__FUNC_WIN32_WindowFromPoint(fpl__win32_func_WindowFromPoint);
7749#define FPL__FUNC_WIN32_PtInRect(name) BOOL WINAPI name(CONST RECT *lprc, POINT pt)
7750typedef FPL__FUNC_WIN32_PtInRect(fpl__win32_func_PtInRect);
7751#define FPL__FUNC_WIN32_LoadCursorA(name) HCURSOR WINAPI name(HINSTANCE hInstance, LPCSTR lpCursorName)
7752typedef FPL__FUNC_WIN32_LoadCursorA(fpl__win32_func_LoadCursorA);
7753#define FPL__FUNC_WIN32_LoadCursorW(name) HCURSOR WINAPI name(HINSTANCE hInstance, LPCWSTR lpCursorName)
7754typedef FPL__FUNC_WIN32_LoadCursorW(fpl__win32_func_LoadCursorW);
7755#define FPL__FUNC_WIN32_LoadIconA(name) HICON WINAPI name(HINSTANCE hInstance, LPCSTR lpIconName)
7756typedef FPL__FUNC_WIN32_LoadIconA(fpl__win32_func_LoadIconA);
7757#define FPL__FUNC_WIN32_LoadIconW(name) HICON WINAPI name(HINSTANCE hInstance, LPCWSTR lpIconName)
7758typedef FPL__FUNC_WIN32_LoadIconW(fpl__win32_func_LoadIconW);
7759#define FPL__FUNC_WIN32_SetWindowTextW(name) BOOL WINAPI name(HWND hWnd, LPCWSTR lpString)
7760typedef FPL__FUNC_WIN32_SetWindowTextW(fpl__win32_func_SetWindowTextW);
7761#define FPL__FUNC_WIN32_GetWindowTextW(name) int WINAPI name(HWND hWnd, LPWSTR lpString, int nMaxCount)
7762typedef FPL__FUNC_WIN32_GetWindowTextW(fpl__win32_func_GetWindowTextW);
7763#define FPL__FUNC_WIN32_SetWindowLongW(name) LONG WINAPI name(HWND hWnd, int nIndex, LONG dwNewLong)
7764typedef FPL__FUNC_WIN32_SetWindowLongW(fpl__win32_func_SetWindowLongW);
7765#define FPL__FUNC_WIN32_GetWindowLongW(name) LONG WINAPI name(HWND hWnd, int nIndex)
7766typedef FPL__FUNC_WIN32_GetWindowLongW(fpl__win32_func_GetWindowLongW);
7767
7768#if defined(FPL_ARCH_X64)
7769#define FPL__FUNC_WIN32_SetWindowLongPtrW(name) LONG_PTR WINAPI name(HWND hWnd, int nIndex, LONG_PTR dwNewLong)
7770typedef FPL__FUNC_WIN32_SetWindowLongPtrW(fpl__win32_func_SetWindowLongPtrW);
7771#define FPL__FUNC_WIN32_GetWindowLongPtrW(name) LONG_PTR WINAPI name(HWND hWnd, int nIndex)
7772typedef FPL__FUNC_WIN32_GetWindowLongPtrW(fpl__win32_func_GetWindowLongPtrW);
7773#endif
7774
7775#define FPL__FUNC_WIN32_ReleaseDC(name) int WINAPI name(HWND hWnd, HDC hDC)
7776typedef FPL__FUNC_WIN32_ReleaseDC(fpl__win32_func_ReleaseDC);
7777#define FPL__FUNC_WIN32_GetDC(name) HDC WINAPI name(HWND hWnd)
7778typedef FPL__FUNC_WIN32_GetDC(fpl__win32_func_GetDC);
7779#define FPL__FUNC_WIN32_ChangeDisplaySettingsW(name) LONG WINAPI name(DEVMODEW* lpDevMode, DWORD dwFlags)
7780typedef FPL__FUNC_WIN32_ChangeDisplaySettingsW(fpl__win32_func_ChangeDisplaySettingsW);
7781#define FPL__FUNC_WIN32_EnumDisplaySettingsW(name) BOOL WINAPI name(LPCWSTR lpszDeviceName, DWORD iModeNum, DEVMODEW* lpDevMode)
7782typedef FPL__FUNC_WIN32_EnumDisplaySettingsW(fpl__win32_func_EnumDisplaySettingsW);
7783#define FPL__FUNC_WIN32_OpenClipboard(name) BOOL WINAPI name(HWND hWndNewOwner)
7784typedef FPL__FUNC_WIN32_OpenClipboard(fpl__win32_func_OpenClipboard);
7785#define FPL__FUNC_WIN32_CloseClipboard(name) BOOL WINAPI name(VOID)
7786typedef FPL__FUNC_WIN32_CloseClipboard(fpl__win32_func_CloseClipboard);
7787#define FPL__FUNC_WIN32_EmptyClipboard(name) BOOL WINAPI name(VOID)
7788typedef FPL__FUNC_WIN32_EmptyClipboard(fpl__win32_func_EmptyClipboard);
7789#define FPL__FUNC_WIN32_IsClipboardFormatAvailable(name) BOOL WINAPI name(UINT format)
7790typedef FPL__FUNC_WIN32_IsClipboardFormatAvailable(fpl__win32_func_IsClipboardFormatAvailable);
7791#define FPL__FUNC_WIN32_SetClipboardData(name) HANDLE WINAPI name(UINT uFormat, HANDLE hMem)
7792typedef FPL__FUNC_WIN32_SetClipboardData(fpl__win32_func_SetClipboardData);
7793#define FPL__FUNC_WIN32_GetClipboardData(name) HANDLE WINAPI name(UINT uFormat)
7794typedef FPL__FUNC_WIN32_GetClipboardData(fpl__win32_func_GetClipboardData);
7795#define FPL__FUNC_WIN32_GetDesktopWindow(name) HWND WINAPI name(VOID)
7796typedef FPL__FUNC_WIN32_GetDesktopWindow(fpl__win32_func_GetDesktopWindow);
7797#define FPL__FUNC_WIN32_GetForegroundWindow(name) HWND WINAPI name(VOID)
7798typedef FPL__FUNC_WIN32_GetForegroundWindow(fpl__win32_func_GetForegroundWindow);
7799#define FPL__FUNC_WIN32_IsZoomed(name) BOOL WINAPI name(HWND hWnd)
7800typedef FPL__FUNC_WIN32_IsZoomed(fpl__win32_func_IsZoomed);
7801#define FPL__FUNC_WIN32_IsIconic(name) BOOL WINAPI name(HWND hWnd)
7802typedef FPL__FUNC_WIN32_IsIconic(fpl__win32_func_IsIconic);
7803#define FPL__FUNC_WIN32_SendMessageW(name) LRESULT WINAPI name(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
7804typedef FPL__FUNC_WIN32_SendMessageW(fpl__win32_func_SendMessageW);
7805#define FPL__FUNC_WIN32_GetMonitorInfoW(name) BOOL WINAPI name(HMONITOR hMonitor, LPMONITORINFO lpmi)
7806typedef FPL__FUNC_WIN32_GetMonitorInfoW(fpl__win32_func_GetMonitorInfoW);
7807#define FPL__FUNC_WIN32_EnumDisplayMonitors(name) BOOL WINAPI name(HDC hdc, LPCRECT lprcClip, MONITORENUMPROC lpfnEnum,LPARAM dwData)
7808typedef FPL__FUNC_WIN32_EnumDisplayMonitors(fpl__win32_func_EnumDisplayMonitors);
7809#define FPL__FUNC_WIN32_MonitorFromRect(name) HMONITOR WINAPI name(LPCRECT lprc, DWORD dwFlags)
7810typedef FPL__FUNC_WIN32_MonitorFromRect(fpl__win32_func_MonitorFromRect);
7811#define FPL__FUNC_WIN32_MonitorFromPoint(name) HMONITOR WINAPI name(POINT pt, DWORD dwFlags)
7812typedef FPL__FUNC_WIN32_MonitorFromPoint(fpl__win32_func_MonitorFromPoint);
7813#define FPL__FUNC_WIN32_MonitorFromWindow(name) HMONITOR WINAPI name(HWND hwnd, DWORD dwFlags)
7814typedef FPL__FUNC_WIN32_MonitorFromWindow(fpl__win32_func_MonitorFromWindow);
7815#define FPL__FUNC_WIN32_RegisterRawInputDevices(name) BOOL WINAPI name(PCRAWINPUTDEVICE pRawInputDevices, UINT uiNumDevices, UINT cbSize)
7816typedef FPL__FUNC_WIN32_RegisterRawInputDevices(fpl__win32_func_RegisterRawInputDevices);
7817#define FPL__FUNC_WIN32_ClipCursor(name) BOOL WINAPI name(CONST RECT *lpRect)
7818typedef FPL__FUNC_WIN32_ClipCursor(fpl__win32_func_ClipCursor);
7819#define FPL__FUNC_WIN32_PostQuitMessage(name) VOID WINAPI name(int nExitCode)
7820typedef FPL__FUNC_WIN32_PostQuitMessage(fpl__win32_func_PostQuitMessage);
7821#define FPL__FUNC_WIN32_CreateIconIndirect(name) HICON WINAPI name(PICONINFO piconinfo)
7822typedef FPL__FUNC_WIN32_CreateIconIndirect(fpl__win32_func_CreateIconIndirect);
7823#define FPL__FUNC_WIN32_GetKeyboardLayout(name) HKL WINAPI name(DWORD idThread)
7824typedef FPL__FUNC_WIN32_GetKeyboardLayout(fpl__win32_func_GetKeyboardLayout);
7825#define FPL__FUNC_WIN32_SetCapture(name) HWND WINAPI name(HWND hWnd)
7826typedef FPL__FUNC_WIN32_SetCapture(fpl__win32_func_SetCapture);
7827#define FPL__FUNC_WIN32_ReleaseCapture(name) BOOL WINAPI name(VOID)
7828typedef FPL__FUNC_WIN32_ReleaseCapture(fpl__win32_func_ReleaseCapture);
7829#define FPL__FUNC_WIN32_ScreenToClient(name) BOOL WINAPI name(HWND hWnd, LPPOINT lpPoint)
7830typedef FPL__FUNC_WIN32_ScreenToClient(fpl__win32_func_ScreenToClient);
7831#define FPL__FUNC_WIN32_BeginPaint(name) HDC WINAPI name(_In_ HWND hWnd, _Out_ LPPAINTSTRUCT lpPaint)
7832typedef FPL__FUNC_WIN32_BeginPaint(fpl__win32_func_BeginPaint);
7833#define FPL__FUNC_WIN32_EndPaint(name) BOOL WINAPI name(_In_ HWND hWnd, _In_ CONST PAINTSTRUCT *lpPaint)
7834typedef FPL__FUNC_WIN32_EndPaint(fpl__win32_func_EndPaint);
7835#define FPL__FUNC_WIN32_SetForegroundWindow(name) BOOL WINAPI name(_In_ HWND hWnd)
7836typedef FPL__FUNC_WIN32_SetForegroundWindow(fpl__win32_func_SetForegroundWindow);
7837#define FPL__FUNC_WIN32_SetFocus(name) HWND WINAPI name(_In_opt_ HWND hWnd)
7838typedef FPL__FUNC_WIN32_SetFocus(fpl__win32_func_SetFocus);
7839#define FPL__FUNC_WIN32_SetTimer(name) UINT_PTR WINAPI name(_In_opt_ HWND hWnd, _In_ UINT_PTR nIDEvent, _In_ UINT uElapse, _In_opt_ TIMERPROC lpTimerFunc)
7840typedef FPL__FUNC_WIN32_SetTimer(fpl__win32_func_SetTimer);
7841#define FPL__FUNC_WIN32_GetSysColorBrush(name) HBRUSH WINAPI name(_In_ int nIndex)
7842typedef FPL__FUNC_WIN32_GetSysColorBrush(fpl__win32_func_GetSysColorBrush);
7843#define FPL__FUNC_WIN32_GetSysColorBrush(name) HBRUSH WINAPI name(_In_ int nIndex)
7844typedef FPL__FUNC_WIN32_GetSysColorBrush(fpl__win32_func_GetSysColorBrush);
7845
7846// OLE32
7847#define FPL__FUNC_WIN32_CoInitializeEx(name) HRESULT WINAPI name(LPVOID pvReserved, DWORD dwCoInit)
7848typedef FPL__FUNC_WIN32_CoInitializeEx(fpl__win32_func_CoInitializeEx);
7849#define FPL__FUNC_WIN32_CoUninitialize(name) void WINAPI name(void)
7850typedef FPL__FUNC_WIN32_CoUninitialize(fpl__win32_func_CoUninitialize);
7851#define FPL__FUNC_WIN32_CoCreateInstance(name) HRESULT WINAPI name(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv)
7852typedef FPL__FUNC_WIN32_CoCreateInstance(fpl__win32_func_CoCreateInstance);
7853#define FPL__FUNC_WIN32_CoTaskMemFree(name) void WINAPI name(LPVOID pv)
7854typedef FPL__FUNC_WIN32_CoTaskMemFree(fpl__win32_func_CoTaskMemFree);
7855#define FPL__FUNC_WIN32_PropVariantClear(name) HRESULT WINAPI name(PROPVARIANT *pvar)
7856typedef FPL__FUNC_WIN32_PropVariantClear(fpl__win32_func_PropVariantClear);
7857
7858typedef struct fpl__Win32GdiApi {
7859 HMODULE gdiLibrary;
7860 fpl__win32_func_ChoosePixelFormat *ChoosePixelFormat;
7861 fpl__win32_func_SetPixelFormat *SetPixelFormat;
7862 fpl__win32_func_DescribePixelFormat *DescribePixelFormat;
7863 fpl__win32_func_GetDeviceCaps *GetDeviceCaps;
7864 fpl__win32_func_StretchDIBits *StretchDIBits;
7865 fpl__win32_func_DeleteObject *DeleteObject;
7866 fpl__win32_func_SwapBuffers *SwapBuffers;
7867 fpl__win32_func_CreateDIBSection *CreateDIBSection;
7868 fpl__win32_func_CreateBitmap *CreateBitmap;
7869 fpl__win32_func_CreateSolidBrush *CreateSolidBrush;
7870} fpl__Win32GdiApi;
7871
7872typedef struct fpl__Win32ShellApi {
7873 HMODULE shellLibrary;
7874 fpl__win32_func_SHGetFolderPathW *SHGetFolderPathW;
7875 fpl__win32_func_DragQueryFileW *DragQueryFileW;
7876 fpl__win32_func_DragAcceptFiles *DragAcceptFiles;
7877} fpl__Win32ShellApi;
7878
7879typedef struct fpl__Win32UserApi {
7880 HMODULE userLibrary;
7881 fpl__win32_func_RegisterClassExW *RegisterClassExW;
7882 fpl__win32_func_UnregisterClassW *UnregisterClassW;
7883 fpl__win32_func_ShowWindow *ShowWindow;
7884 fpl__win32_func_DestroyWindow *DestroyWindow;
7885 fpl__win32_func_UpdateWindow *UpdateWindow;
7886 fpl__win32_func_TranslateMessage *TranslateMessage;
7887 fpl__win32_func_DispatchMessageW *DispatchMessageW;
7888 fpl__win32_func_PeekMessageW *PeekMessageW;
7889 fpl__win32_func_DefWindowProcW *DefWindowProcW;
7890 fpl__win32_func_CreateWindowExW *CreateWindowExW;
7891 fpl__win32_func_SetWindowPos *SetWindowPos;
7892 fpl__win32_func_GetWindowPlacement *GetWindowPlacement;
7893 fpl__win32_func_SetWindowPlacement *SetWindowPlacement;
7894 fpl__win32_func_GetClientRect *GetClientRect;
7895 fpl__win32_func_GetWindowRect *GetWindowRect;
7896 fpl__win32_func_AdjustWindowRect *AdjustWindowRect;
7897 fpl__win32_func_GetAsyncKeyState *GetAsyncKeyState;
7898 fpl__win32_func_MapVirtualKeyW *MapVirtualKeyW;
7899 fpl__win32_func_SetCursor *SetCursor;
7900 fpl__win32_func_GetCursor *GetCursor;
7901 fpl__win32_func_LoadCursorA *LoadCursorA;
7902 fpl__win32_func_LoadCursorW *LoadCursorW;
7903 fpl__win32_func_LoadIconA *LoadIconA;
7904 fpl__win32_func_LoadIconW *LoadIconW;
7905 fpl__win32_func_SetWindowTextW *SetWindowTextW;
7906 fpl__win32_func_GetWindowTextW *GetWindowTextW;
7907 fpl__win32_func_SetWindowLongW *SetWindowLongW;
7908 fpl__win32_func_GetWindowLongW *GetWindowLongW;
7909#if defined(FPL_ARCH_X64)
7910 fpl__win32_func_SetWindowLongPtrW *SetWindowLongPtrW;
7911 fpl__win32_func_GetWindowLongPtrW *GetWindowLongPtrW;
7912#endif
7913 fpl__win32_func_ReleaseDC *ReleaseDC;
7914 fpl__win32_func_GetDC *GetDC;
7915 fpl__win32_func_ChangeDisplaySettingsW *ChangeDisplaySettingsW;
7916 fpl__win32_func_EnumDisplaySettingsW *EnumDisplaySettingsW;
7917 fpl__win32_func_OpenClipboard *OpenClipboard;
7918 fpl__win32_func_CloseClipboard *CloseClipboard;
7919 fpl__win32_func_EmptyClipboard *EmptyClipboard;
7920 fpl__win32_func_IsClipboardFormatAvailable *IsClipboardFormatAvailable;
7921 fpl__win32_func_SetClipboardData *SetClipboardData;
7922 fpl__win32_func_GetClipboardData *GetClipboardData;
7923 fpl__win32_func_GetDesktopWindow *GetDesktopWindow;
7924 fpl__win32_func_GetForegroundWindow *GetForegroundWindow;
7925 fpl__win32_func_IsZoomed *IsZoomed;
7926 fpl__win32_func_IsIconic *IsIconic;
7927 fpl__win32_func_SendMessageW *SendMessageW;
7928 fpl__win32_func_GetMonitorInfoW *GetMonitorInfoW;
7929 fpl__win32_func_EnumDisplayMonitors *EnumDisplayMonitors;
7930 fpl__win32_func_MonitorFromRect *MonitorFromRect;
7931 fpl__win32_func_MonitorFromPoint *MonitorFromPoint;
7932 fpl__win32_func_MonitorFromWindow *MonitorFromWindow;
7933 fpl__win32_func_GetCursorPos *GetCursorPos;
7934 fpl__win32_func_WindowFromPoint *WindowFromPoint;
7935 fpl__win32_func_ClientToScreen *ClientToScreen;
7936 fpl__win32_func_PtInRect *PtInRect;
7937 fpl__win32_func_RegisterRawInputDevices *RegisterRawInputDevices;
7938 fpl__win32_func_ClipCursor *ClipCursor;
7939 fpl__win32_func_PostQuitMessage *PostQuitMessage;
7940 fpl__win32_func_CreateIconIndirect *CreateIconIndirect;
7941 fpl__win32_func_GetKeyboardLayout *GetKeyboardLayout;
7942 fpl__win32_func_GetKeyState *GetKeyState;
7943 fpl__win32_func_SetCapture *SetCapture;
7944 fpl__win32_func_ReleaseCapture *ReleaseCapture;
7945 fpl__win32_func_ScreenToClient *ScreenToClient;
7946 fpl__win32_func_BeginPaint *BeginPaint;
7947 fpl__win32_func_EndPaint *EndPaint;
7948 fpl__win32_func_SetForegroundWindow *SetForegroundWindow;
7949 fpl__win32_func_SetFocus *SetFocus;
7950 fpl__win32_func_SetTimer *SetTimer;
7951 fpl__win32_func_GetSysColorBrush *GetSysColorBrush;
7952} fpl__Win32UserApi;
7953
7954typedef struct fpl__Win32OleApi {
7955 HMODULE oleLibrary;
7956 fpl__win32_func_CoInitializeEx *CoInitializeEx;
7957 fpl__win32_func_CoUninitialize *CoUninitialize;
7958 fpl__win32_func_CoCreateInstance *CoCreateInstance;
7959 fpl__win32_func_CoTaskMemFree *CoTaskMemFree;
7960 fpl__win32_func_PropVariantClear *PropVariantClear;
7961} fpl__Win32OleApi;
7962
7963typedef struct fpl__Win32Api {
7964 fpl__Win32GdiApi gdi;
7965 fpl__Win32ShellApi shell;
7966 fpl__Win32UserApi user;
7967 fpl__Win32OleApi ole;
7968 fpl_b32 isValid;
7969} fpl__Win32Api;
7970
7971fpl_internal void fpl__Win32UnloadApi(fpl__Win32Api *wapi) {
7972 fplAssert(wapi != fpl_null);
7973 if (wapi->ole.oleLibrary != fpl_null) {
7974 FreeLibrary(wapi->ole.oleLibrary);
7975 }
7976 fplClearStruct(&wapi->ole);
7977 if (wapi->gdi.gdiLibrary != fpl_null) {
7978 FreeLibrary(wapi->gdi.gdiLibrary);
7979 }
7980 fplClearStruct(&wapi->gdi);
7981 if (wapi->user.userLibrary != fpl_null) {
7982 FreeLibrary(wapi->user.userLibrary);
7983 }
7984 fplClearStruct(&wapi->user);
7985 if (wapi->shell.shellLibrary != fpl_null) {
7986 FreeLibrary(wapi->shell.shellLibrary);
7987 }
7988 fplClearStruct(&wapi->shell);
7989 wapi->isValid = false;
7990}
7991
7992fpl_internal bool fpl__Win32LoadApi(fpl__Win32Api *wapi) {
7993 fplAssert(wapi != fpl_null);
7994 bool result = false;
7995 fplClearStruct(wapi);
7996 do {
7997 // Shell32
7998 const char *shellLibraryName = "shell32.dll";
7999 HMODULE shellLibrary = fpl_null;
8000 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_WIN32, shellLibrary, shellLibraryName);
8001 wapi->shell.shellLibrary = shellLibrary;
8002 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, shellLibrary, shellLibraryName, &wapi->shell, fpl__win32_func_SHGetFolderPathW, SHGetFolderPathW);
8003 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, shellLibrary, shellLibraryName, &wapi->shell, fpl__win32_func_DragQueryFileW, DragQueryFileW);
8004 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, shellLibrary, shellLibraryName, &wapi->shell, fpl__win32_func_DragAcceptFiles, DragAcceptFiles);
8005
8006 // User32
8007 const char *userLibraryName = "user32.dll";
8008 HMODULE userLibrary = fpl_null;
8009 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_WIN32, userLibrary, userLibraryName);
8010 wapi->user.userLibrary = userLibrary;
8011 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_RegisterClassExW, RegisterClassExW);
8012 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_UnregisterClassW, UnregisterClassW);
8013 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ShowWindow, ShowWindow);
8014 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_DestroyWindow, DestroyWindow);
8015 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_UpdateWindow, UpdateWindow);
8016 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_TranslateMessage, TranslateMessage);
8017 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_DispatchMessageW, DispatchMessageW);
8018 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_PeekMessageW, PeekMessageW);
8019 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_DefWindowProcW, DefWindowProcW);
8020 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_CreateWindowExW, CreateWindowExW);
8021 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetWindowPos, SetWindowPos);
8022 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetWindowPlacement, GetWindowPlacement);
8023 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetWindowPlacement, SetWindowPlacement);
8024 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetClientRect, GetClientRect);
8025 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetWindowRect, GetWindowRect);
8026 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_AdjustWindowRect, AdjustWindowRect);
8027 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetAsyncKeyState, GetAsyncKeyState);
8028 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_MapVirtualKeyW, MapVirtualKeyW);
8029 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetCursor, SetCursor);
8030 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetCursor, GetCursor);
8031 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_LoadCursorA, LoadCursorA);
8032 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_LoadCursorW, LoadCursorW);
8033 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetCursorPos, GetCursorPos);
8034 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_WindowFromPoint, WindowFromPoint);
8035 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_LoadIconA, LoadIconA);
8036 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_LoadIconW, LoadIconW);
8037 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetWindowTextW, SetWindowTextW);
8038 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetWindowLongW, SetWindowLongW);
8039 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetWindowLongW, GetWindowLongW);
8040
8041# if defined(FPL_ARCH_X64)
8042 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetWindowLongPtrW, SetWindowLongPtrW);
8043 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetWindowLongPtrW, GetWindowLongPtrW);
8044# endif
8045
8046 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ReleaseDC, ReleaseDC);
8047 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetDC, GetDC);
8048 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ChangeDisplaySettingsW, ChangeDisplaySettingsW);
8049 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_EnumDisplaySettingsW, EnumDisplaySettingsW);
8050 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_IsClipboardFormatAvailable, IsClipboardFormatAvailable);
8051 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_OpenClipboard, OpenClipboard);
8052 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_CloseClipboard, CloseClipboard);
8053 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_EmptyClipboard, EmptyClipboard);
8054 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetClipboardData, SetClipboardData);
8055 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetClipboardData, GetClipboardData);
8056 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetDesktopWindow, GetDesktopWindow);
8057 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetForegroundWindow, GetForegroundWindow);
8058 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_IsZoomed, IsZoomed);
8059 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_IsIconic, IsIconic);
8060 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SendMessageW, SendMessageW);
8061 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetMonitorInfoW, GetMonitorInfoW);
8062 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_EnumDisplayMonitors, EnumDisplayMonitors);
8063 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_MonitorFromRect, MonitorFromRect);
8064 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_MonitorFromPoint, MonitorFromPoint);
8065 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_MonitorFromWindow, MonitorFromWindow);
8066 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ClientToScreen, ClientToScreen);
8067 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_PtInRect, PtInRect);
8068 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_RegisterRawInputDevices, RegisterRawInputDevices);
8069 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ClipCursor, ClipCursor);
8070 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_PostQuitMessage, PostQuitMessage);
8071 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_CreateIconIndirect, CreateIconIndirect);
8072 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetKeyboardLayout, GetKeyboardLayout);
8073 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetKeyState, GetKeyState);
8074 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetCapture, SetCapture);
8075 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ReleaseCapture, ReleaseCapture);
8076 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_ScreenToClient, ScreenToClient);
8077 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_BeginPaint, BeginPaint);
8078 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_EndPaint, EndPaint);
8079 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetForegroundWindow, SetForegroundWindow);
8080 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetFocus, SetFocus);
8081 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_SetTimer, SetTimer);
8082 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, userLibrary, userLibraryName, &wapi->user, fpl__win32_func_GetSysColorBrush, GetSysColorBrush);
8083
8084 // GDI32
8085 const char *gdiLibraryName = "gdi32.dll";
8086 HMODULE gdiLibrary = fpl_null;
8087 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName);
8088 wapi->gdi.gdiLibrary = gdiLibrary;
8089 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_ChoosePixelFormat, ChoosePixelFormat);
8090 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_SetPixelFormat, SetPixelFormat);
8091 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_DescribePixelFormat, DescribePixelFormat);
8092 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_StretchDIBits, StretchDIBits);
8093 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_DeleteObject, DeleteObject);
8094 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_SwapBuffers, SwapBuffers);
8095 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_GetDeviceCaps, GetDeviceCaps);
8096 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_CreateDIBSection, CreateDIBSection);
8097 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_CreateBitmap, CreateBitmap);
8098 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, gdiLibrary, gdiLibraryName, &wapi->gdi, fpl__win32_func_CreateSolidBrush, CreateSolidBrush);
8099
8100 // OLE32
8101 const char *oleLibraryName = "ole32.dll";
8102 HMODULE oleLibrary = fpl_null;
8103 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_WIN32, oleLibrary, oleLibraryName);
8104 wapi->ole.oleLibrary = oleLibrary;
8105 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, oleLibrary, oleLibraryName, &wapi->ole, fpl__win32_func_CoInitializeEx, CoInitializeEx);
8106 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, oleLibrary, oleLibraryName, &wapi->ole, fpl__win32_func_CoUninitialize, CoUninitialize);
8107 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, oleLibrary, oleLibraryName, &wapi->ole, fpl__win32_func_CoCreateInstance, CoCreateInstance);
8108 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, oleLibrary, oleLibraryName, &wapi->ole, fpl__win32_func_CoTaskMemFree, CoTaskMemFree);
8109 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_WIN32, oleLibrary, oleLibraryName, &wapi->ole, fpl__win32_func_PropVariantClear, PropVariantClear);
8110
8111 result = true;
8112 } while (0);
8113 if (!result) {
8114 fpl__Win32UnloadApi(wapi);
8115 }
8116 wapi->isValid = result;
8117 return(result);
8118}
8119
8120// Win32 unicode dependend stuff
8121#define FPL__WIN32_CLASSNAME L"FPLWindowClassW"
8122#define FPL__WIN32_UNNAMED_WINDOW L"Unnamed FPL Unicode Window"
8123#define FPL__WIN32_UNNAMED_CONSOLE L"Unnamed FPL Unicode Console"
8124#if defined(FPL_ARCH_X64)
8125# define fpl__win32_SetWindowLongPtr fpl__global__AppState->win32.winApi.user.SetWindowLongPtrW
8126#else
8127# define fpl__win32_SetWindowLongPtr fpl__global__AppState->win32.winApi.user.SetWindowLongW
8128#endif
8129#define fpl__win32_SetWindowLong fpl__global__AppState->win32.winApi.user.SetWindowLongW
8130#define fpl__win32_GetWindowLong fpl__global__AppState->win32.winApi.user.GetWindowLongW
8131#if UNICODE
8132# define fpl__win32_LoadIcon fpl__global__AppState->win32.winApi.user.LoadIconW
8133# define fpl__win32_LoadCursor fpl__global__AppState->win32.winApi.user.LoadCursorW
8134#else
8135# define fpl__win32_LoadIcon fpl__global__AppState->win32.winApi.user.LoadIconA
8136# define fpl__win32_LoadCursor fpl__global__AppState->win32.winApi.user.LoadCursorA
8137#endif
8138
8139typedef char fpl__GameControllerName[FPL_MAX_NAME_LENGTH];
8140
8141typedef struct fpl__Win32XInputState {
8142 fpl__GameControllerName deviceNames[XUSER_MAX_COUNT];
8143 fpl_b32 isConnected[XUSER_MAX_COUNT];
8144 fpl__Win32XInputApi xinputApi;
8145 LARGE_INTEGER lastDeviceSearchTime;
8146} fpl__Win32XInputState;
8147
8148typedef struct fpl__Win32InitState {
8149 HINSTANCE appInstance;
8150 LARGE_INTEGER qpf;
8151} fpl__Win32InitState;
8152
8153typedef struct fpl__Win32AppState {
8154 fpl__Win32XInputState xinput;
8155 fpl__Win32Api winApi;
8156} fpl__Win32AppState;
8157
8158#if defined(FPL__ENABLE_WINDOW)
8159typedef struct fpl__Win32LastWindowInfo {
8160 WINDOWPLACEMENT placement;
8161 DWORD style;
8162 DWORD exStyle;
8163 fpl_b32 isMaximized;
8164 fpl_b32 isMinimized;
8165 fpl_b32 wasResolutionChanged;
8166} fpl__Win32LastWindowInfo;
8167
8168typedef struct fpl__Win32WindowState {
8169 wchar_t windowClass[256];
8170 fpl__Win32LastWindowInfo lastFullscreenInfo;
8171 void *mainFiber;
8172 void *messageFiber;
8173 HWND windowHandle;
8174 HDC deviceContext;
8175 HBRUSH backgroundBrush;
8176 HCURSOR defaultCursor;
8177 int pixelFormat;
8178 fpl_b32 isCursorActive;
8179 fpl_b32 isFrameInteraction;
8180} fpl__Win32WindowState;
8181#endif // FPL__ENABLE_WINDOW
8182
8183#endif // FPL_PLATFORM_WINDOWS
8184
8185// ############################################################################
8186//
8187// > TYPES_POSIX
8188//
8189// ############################################################################
8190#if defined(FPL_SUBPLATFORM_POSIX)
8191# include <sys/types.h> // data types
8192# include <sys/mman.h> // mmap, munmap
8193# include <sys/stat.h> // mkdir
8194# include <sys/errno.h> // errno
8195# include <sys/time.h> // gettimeofday
8196# include <sys/utsname.h> // uname
8197# include <signal.h> // pthread_kill
8198# include <time.h> // clock_gettime, nanosleep
8199# include <dlfcn.h> // dlopen, dlclose
8200# include <fcntl.h> // open
8201# include <unistd.h> // read, write, close, access, rmdir, getpid, sysconf, geteuid
8202# include <ctype.h> // isspace
8203# include <pwd.h> // getpwuid
8204
8205// @TODO(final): Detect the case of (Older POSIX versions where st_atim != st_atime)
8206#if !defined(FPL_PLATFORM_ANDROID)
8207# define st_atime st_atim.tv_sec
8208# define st_mtime st_mtim.tv_sec
8209# define st_ctime st_ctim.tv_sec
8210#endif
8211
8212#if defined(FPL_PLATFORM_LINUX)
8213# define fpl__lseek64 lseek64
8214# define fpl__off64_t off64_t
8215#else
8216# define fpl__lseek64 lseek
8217# define fpl__off64_t off_t
8218#endif
8219
8220// Little macros for loading a library and getting proc address for POSIX
8221#define FPL__POSIX_LOAD_LIBRARY_BREAK(mod, target, libName) \
8222 (target) = dlopen(libName, FPL__POSIX_DL_LOADTYPE); \
8223 if((target) == fpl_null) { \
8224 FPL__WARNING(mod, "Failed loading library '%s'", (libName)); \
8225 break; \
8226 }
8227
8228#define FPL__POSIX_GET_FUNCTION_ADDRESS_OPTIONAL(mod, libHandle, libName, target, type, name) \
8229 (target)->name = (type *)dlsym(libHandle, #name)
8230
8231#define FPL__POSIX_GET_FUNCTION_ADDRESS_BREAK(mod, libHandle, libName, target, type, name) \
8232 (target)->name = (type *)dlsym(libHandle, #name); \
8233 if ((target)->name == fpl_null) { \
8234 FPL__WARNING(mod, "Failed getting procedure address '%s' from library '%s'", #name, libName); \
8235 break; \
8236 }
8237#if !defined(FPL_NO_RUNTIME_LINKING)
8238# define FPL__POSIX_LOAD_LIBRARY FPL__POSIX_LOAD_LIBRARY_BREAK
8239# define FPL__POSIX_GET_FUNCTION_ADDRESS FPL__POSIX_GET_FUNCTION_ADDRESS_BREAK
8240#else
8241# define FPL__POSIX_LOAD_LIBRARY(mod, target, libName)
8242# define FPL__POSIX_GET_FUNCTION_ADDRESS_OPTIONAL(mod, libHandle, libName, target, type, name) \
8243 (target)->name = name
8244# define FPL__POSIX_GET_FUNCTION_ADDRESS(mod, libHandle, libName, target, type, name) \
8245 (target)->name = name
8246#endif
8247
8248#define FPL__FUNC_PTHREAD_pthread_self(name) pthread_t name(void)
8249typedef FPL__FUNC_PTHREAD_pthread_self(fpl__pthread_func_pthread_self);
8250#define FPL__FUNC_PTHREAD_pthread_setschedparam(name) int name(pthread_t thread, int policy, const struct sched_param *param)
8251typedef FPL__FUNC_PTHREAD_pthread_setschedparam(fpl__pthread_func_pthread_setschedparam);
8252#define FPL__FUNC_PTHREAD_pthread_getschedparam(name) int name(pthread_t thread, int *policy, struct sched_param *param)
8253typedef FPL__FUNC_PTHREAD_pthread_getschedparam(fpl__pthread_func_pthread_getschedparam);
8254#define FPL__FUNC_PTHREAD_pthread_setschedprio(name) int name(pthread_t thread, int prio)
8255typedef FPL__FUNC_PTHREAD_pthread_setschedprio(fpl__pthread_func_pthread_setschedprio);
8256
8257#define FPL__FUNC_PTHREAD_pthread_attr_init(name) int name(pthread_attr_t *attr)
8258typedef FPL__FUNC_PTHREAD_pthread_attr_init(fpl__pthread_func_pthread_attr_init);
8259#define FPL__FUNC_PTHREAD_pthread_attr_getschedparam(name) int name(const pthread_attr_t *__restrict__ attr, struct sched_param *__restrict__ param)
8260typedef FPL__FUNC_PTHREAD_pthread_attr_getschedparam(fpl__pthread_func_pthread_attr_getschedparam);
8261#define FPL__FUNC_PTHREAD_pthread_attr_setschedparam(name) int name(pthread_attr_t *__restrict__ attr, const struct sched_param *__restrict__ param)
8262typedef FPL__FUNC_PTHREAD_pthread_attr_setschedparam(fpl__pthread_func_pthread_attr_setschedparam);
8263#define FPL__FUNC_PTHREAD_pthread_attr_setstacksize(name) int name(pthread_attr_t *attr, size_t stacksize)
8264typedef FPL__FUNC_PTHREAD_pthread_attr_setstacksize(fpl__pthread_func_pthread_attr_setstacksize);
8265#define FPL__FUNC_PTHREAD_pthread_attr_setdetachstate(name) int name(pthread_attr_t *attr, int detachstate);
8266typedef FPL__FUNC_PTHREAD_pthread_attr_setdetachstate(fpl__pthread_func_pthread_attr_setdetachstate);
8267#define FPL__FUNC_PTHREAD_pthread_attr_setschedpolicy(name) int name(pthread_attr_t *__attr, int __policy)
8268typedef FPL__FUNC_PTHREAD_pthread_attr_setschedpolicy(fpl__pthread_func_pthread_attr_setschedpolicy);
8269
8270#define FPL__FUNC_PTHREAD_pthread_create(name) int name(pthread_t *, const pthread_attr_t *, void *(*__start_routine) (void *), void *)
8271typedef FPL__FUNC_PTHREAD_pthread_create(fpl__pthread_func_pthread_create);
8272#define FPL__FUNC_PTHREAD_pthread_kill(name) int name(pthread_t thread, int sig)
8273typedef FPL__FUNC_PTHREAD_pthread_kill(fpl__pthread_func_pthread_kill);
8274#define FPL__FUNC_PTHREAD_pthread_join(name) int name(pthread_t __th, void **retval)
8275typedef FPL__FUNC_PTHREAD_pthread_join(fpl__pthread_func_pthread_join);
8276#define FPL__FUNC_PTHREAD_pthread_exit(name) void name(void *__retval)
8277typedef FPL__FUNC_PTHREAD_pthread_exit(fpl__pthread_func_pthread_exit);
8278#define FPL__FUNC_PTHREAD_pthread_yield(name) int name(void)
8279typedef FPL__FUNC_PTHREAD_pthread_yield(fpl__pthread_func_pthread_yield);
8280#define FPL__FUNC_PTHREAD_pthread_timedjoin_np(name) int name(pthread_t thread, void **retval, const struct timespec *abstime)
8281typedef FPL__FUNC_PTHREAD_pthread_timedjoin_np(fpl__pthread_func_pthread_timedjoin_np);
8282
8283#define FPL__FUNC_PTHREAD_pthread_mutex_init(name) int name(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
8284typedef FPL__FUNC_PTHREAD_pthread_mutex_init(fpl__pthread_func_pthread_mutex_init);
8285#define FPL__FUNC_PTHREAD_pthread_mutex_destroy(name) int name(pthread_mutex_t *mutex)
8286typedef FPL__FUNC_PTHREAD_pthread_mutex_destroy(fpl__pthread_func_pthread_mutex_destroy);
8287#define FPL__FUNC_PTHREAD_pthread_mutex_lock(name) int name(pthread_mutex_t *mutex)
8288typedef FPL__FUNC_PTHREAD_pthread_mutex_lock(fpl__pthread_func_pthread_mutex_lock);
8289#define FPL__FUNC_PTHREAD_pthread_mutex_trylock(name) int name(pthread_mutex_t *mutex)
8290typedef FPL__FUNC_PTHREAD_pthread_mutex_trylock(fpl__pthread_func_pthread_mutex_trylock);
8291#define FPL__FUNC_PTHREAD_pthread_mutex_unlock(name) int name(pthread_mutex_t *mutex)
8292typedef FPL__FUNC_PTHREAD_pthread_mutex_unlock(fpl__pthread_func_pthread_mutex_unlock);
8293
8294#define FPL__FUNC_PTHREAD_pthread_cond_init(name) int name(pthread_cond_t *cond, const pthread_condattr_t *attr)
8295typedef FPL__FUNC_PTHREAD_pthread_cond_init(fpl__pthread_func_pthread_cond_init);
8296#define FPL__FUNC_PTHREAD_pthread_cond_destroy(name) int name(pthread_cond_t *cond)
8297typedef FPL__FUNC_PTHREAD_pthread_cond_destroy(fpl__pthread_func_pthread_cond_destroy);
8298#define FPL__FUNC_PTHREAD_pthread_cond_timedwait(name) int name(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
8299typedef FPL__FUNC_PTHREAD_pthread_cond_timedwait(fpl__pthread_func_pthread_cond_timedwait);
8300#define FPL__FUNC_PTHREAD_pthread_cond_wait(name) int name(pthread_cond_t *cond, pthread_mutex_t *mutex)
8301typedef FPL__FUNC_PTHREAD_pthread_cond_wait(fpl__pthread_func_pthread_cond_wait);
8302#define FPL__FUNC_PTHREAD_pthread_cond_broadcast(name) int name(pthread_cond_t *cond)
8303typedef FPL__FUNC_PTHREAD_pthread_cond_broadcast(fpl__pthread_func_pthread_cond_broadcast);
8304#define FPL__FUNC_PTHREAD_pthread_cond_signal(name) int name(pthread_cond_t *cond)
8305typedef FPL__FUNC_PTHREAD_pthread_cond_signal(fpl__pthread_func_pthread_cond_signal);
8306
8307#define FPL__FUNC_PTHREAD_sem_init(name) int name(sem_t *__sem, int __pshared, unsigned int __value)
8308typedef FPL__FUNC_PTHREAD_sem_init(fpl__pthread_func_sem_init);
8309#define FPL__FUNC_PTHREAD_sem_destroy(name) int name(sem_t *__sem)
8310typedef FPL__FUNC_PTHREAD_sem_destroy(fpl__pthread_func_sem_destroy);
8311#define FPL__FUNC_PTHREAD_sem_wait(name) int name(sem_t *__sem)
8312typedef FPL__FUNC_PTHREAD_sem_wait(fpl__pthread_func_sem_wait);
8313#define FPL__FUNC_PTHREAD_sem_timedwait(name) int name(sem_t *__restrict __sem, const struct timespec *__restrict __abstime)
8314typedef FPL__FUNC_PTHREAD_sem_timedwait(fpl__pthread_func_sem_timedwait);
8315#define FPL__FUNC_PTHREAD_sem_trywait(name) int name(sem_t *__sem)
8316typedef FPL__FUNC_PTHREAD_sem_trywait(fpl__pthread_func_sem_trywait);
8317#define FPL__FUNC_PTHREAD_sem_post(name) int name(sem_t *__sem)
8318typedef FPL__FUNC_PTHREAD_sem_post(fpl__pthread_func_sem_post);
8319#define FPL__FUNC_PTHREAD_sem_getvalue(name) int name(sem_t *__restrict __sem, int *__restrict __sval)
8320typedef FPL__FUNC_PTHREAD_sem_getvalue(fpl__pthread_func_sem_getvalue);
8321
8322typedef struct fpl__PThreadApi {
8323 void *libHandle;
8324
8325 // pthread_t
8326 fpl__pthread_func_pthread_self *pthread_self;
8327 fpl__pthread_func_pthread_setschedparam *pthread_setschedparam;
8328 fpl__pthread_func_pthread_getschedparam *pthread_getschedparam;
8329 fpl__pthread_func_pthread_setschedprio *pthread_setschedprio;
8330
8331 fpl__pthread_func_pthread_create *pthread_create;
8332 fpl__pthread_func_pthread_kill *pthread_kill;
8333 fpl__pthread_func_pthread_join *pthread_join;
8334 fpl__pthread_func_pthread_exit *pthread_exit;
8335 fpl__pthread_func_pthread_yield *pthread_yield;
8336 fpl__pthread_func_pthread_timedjoin_np *pthread_timedjoin_np;
8337
8338 // pthread_attr_t
8339 fpl__pthread_func_pthread_attr_init *pthread_attr_init;
8340 fpl__pthread_func_pthread_attr_getschedparam *pthread_attr_getschedparam;
8341 fpl__pthread_func_pthread_attr_setschedparam *pthread_attr_setschedparam;
8342 fpl__pthread_func_pthread_attr_setstacksize *pthread_attr_setstacksize;
8343 fpl__pthread_func_pthread_attr_setdetachstate *pthread_attr_setdetachstate;
8344 fpl__pthread_func_pthread_attr_setschedpolicy *pthread_attr_setschedpolicy;
8345
8346 // pthread_mutex_t
8347 fpl__pthread_func_pthread_mutex_init *pthread_mutex_init;
8348 fpl__pthread_func_pthread_mutex_destroy *pthread_mutex_destroy;
8349 fpl__pthread_func_pthread_mutex_lock *pthread_mutex_lock;
8350 fpl__pthread_func_pthread_mutex_trylock *pthread_mutex_trylock;
8351 fpl__pthread_func_pthread_mutex_unlock *pthread_mutex_unlock;
8352
8353 // pthread_cond_t
8354 fpl__pthread_func_pthread_cond_init *pthread_cond_init;
8355 fpl__pthread_func_pthread_cond_destroy *pthread_cond_destroy;
8356 fpl__pthread_func_pthread_cond_timedwait *pthread_cond_timedwait;
8357 fpl__pthread_func_pthread_cond_wait *pthread_cond_wait;
8358 fpl__pthread_func_pthread_cond_broadcast *pthread_cond_broadcast;
8359 fpl__pthread_func_pthread_cond_signal *pthread_cond_signal;
8360
8361 // sem_t
8362 fpl__pthread_func_sem_init *sem_init;
8363 fpl__pthread_func_sem_destroy *sem_destroy;
8364 fpl__pthread_func_sem_wait *sem_wait;
8365 fpl__pthread_func_sem_timedwait *sem_timedwait;
8366 fpl__pthread_func_sem_trywait *sem_trywait;
8367 fpl__pthread_func_sem_post *sem_post;
8368 fpl__pthread_func_sem_getvalue *sem_getvalue;
8369} fpl__PThreadApi;
8370
8371#define FPL__POSIX_DL_LOADTYPE RTLD_NOW
8372
8373fpl_internal void fpl__PThreadUnloadApi(fpl__PThreadApi *pthreadApi) {
8374 fplAssert(pthreadApi != fpl_null);
8375 if (pthreadApi->libHandle != fpl_null) {
8376 dlclose(pthreadApi->libHandle);
8377 }
8378 fplClearStruct(pthreadApi);
8379}
8380
8381fpl_internal bool fpl__PThreadLoadApi(fpl__PThreadApi *pthreadApi) {
8382 const char *libpthreadFileNames[] = {
8383 "libpthread.so",
8384 "libpthread.so.0",
8385 };
8386 bool result = false;
8387 for (uint32_t index = 0; index < fplArrayCount(libpthreadFileNames); ++index) {
8388 const char *libName = libpthreadFileNames[index];
8389 fplClearStruct(pthreadApi);
8390 do {
8391 void *libHandle = fpl_null;
8392 FPL__POSIX_LOAD_LIBRARY(FPL__MODULE_PTHREAD, libHandle, libName);
8393
8394 // pthread_t
8395 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_self, pthread_self);
8396 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_setschedparam, pthread_setschedparam);
8397 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_getschedparam, pthread_getschedparam);
8398 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_setschedprio, pthread_setschedprio);
8399
8400 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_create, pthread_create);
8401 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_kill, pthread_kill);
8402 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_join, pthread_join);
8403 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_exit, pthread_exit);
8404 FPL__POSIX_GET_FUNCTION_ADDRESS_OPTIONAL(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_yield, pthread_yield);
8405 FPL__POSIX_GET_FUNCTION_ADDRESS_OPTIONAL(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_timedjoin_np, pthread_timedjoin_np);
8406
8407 // pthread_attr_t
8408 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_init, pthread_attr_init);
8409 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_getschedparam, pthread_attr_getschedparam);
8410 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_setschedparam, pthread_attr_setschedparam);
8411 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_setstacksize, pthread_attr_setstacksize);
8412 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_setdetachstate, pthread_attr_setdetachstate);
8413 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_attr_setschedpolicy, pthread_attr_setschedpolicy);
8414
8415 // pthread_mutex_t
8416 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_mutex_init, pthread_mutex_init);
8417 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_mutex_destroy, pthread_mutex_destroy);
8418 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_mutex_lock, pthread_mutex_lock);
8419 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_mutex_trylock, pthread_mutex_trylock);
8420 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_mutex_unlock, pthread_mutex_unlock);
8421
8422 // pthread_cond_t
8423 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_init, pthread_cond_init);
8424 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_destroy, pthread_cond_destroy);
8425 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_timedwait, pthread_cond_timedwait);
8426 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_wait, pthread_cond_wait);
8427 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_broadcast, pthread_cond_broadcast);
8428 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_pthread_cond_signal, pthread_cond_signal);
8429
8430 // sem_t
8431 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_init, sem_init);
8432 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_destroy, sem_destroy);
8433 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_wait, sem_wait);
8434 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_timedwait, sem_timedwait);
8435 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_trywait, sem_trywait);
8436 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_post, sem_post);
8437 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_PTHREAD, libHandle, libName, pthreadApi, fpl__pthread_func_sem_getvalue, sem_getvalue);
8438
8439 pthreadApi->libHandle = libHandle;
8440 result = true;
8441 } while (0);
8442 if (result) {
8443 break;
8444 }
8445 fpl__PThreadUnloadApi(pthreadApi);
8446 }
8447 return(result);
8448}
8449
8450typedef struct fpl__PosixInitState {
8451 int dummy;
8452} fpl__PosixInitState;
8453
8454typedef struct fpl__PosixAppState {
8455 fpl__PThreadApi pthreadApi;
8456} fpl__PosixAppState;
8457#endif // FPL_SUBPLATFORM_POSIX
8458
8459// ############################################################################
8460//
8461// > TYPES_LINUX
8462//
8463// ############################################################################
8464#if defined(FPL_PLATFORM_LINUX)
8465typedef struct fpl__LinuxInitState {
8466 int dummy;
8467} fpl__LinuxInitState;
8468
8469#if defined(FPL__ENABLE_WINDOW)
8470#define FPL__LINUX_MAX_GAME_CONTROLLER_COUNT 4
8471typedef struct fpl__LinuxGameController {
8472 char deviceName[512 + 1];
8473 char displayName[FPL_MAX_NAME_LENGTH];
8474 int fd;
8475 uint8_t axisCount;
8476 uint8_t buttonCount;
8477 fplGamepadState state;
8478} fpl__LinuxGameController;
8479
8480typedef struct fpl__LinuxGameControllersState {
8481 fpl__LinuxGameController controllers[FPL__LINUX_MAX_GAME_CONTROLLER_COUNT];
8482 uint64_t lastCheckTime;
8483} fpl__LinuxGameControllersState;
8484#endif
8485
8486typedef struct fpl__LinuxAppState {
8487#if defined(FPL__ENABLE_WINDOW)
8488 fpl__LinuxGameControllersState controllersState;
8489#endif
8490 int dummy;
8491} fpl__LinuxAppState;
8492
8493// Forward declarations
8494#if defined(FPL__ENABLE_WINDOW)
8495fpl_internal void fpl__LinuxFreeGameControllers(fpl__LinuxGameControllersState *controllersState);
8496fpl_internal void fpl__LinuxPollGameControllers(const fplSettings *settings, fpl__LinuxGameControllersState *controllersState, const bool useEvents);
8497#endif
8498
8499#endif // FPL_PLATFORM_LINUX
8500
8501// ############################################################################
8502//
8503// > TYPES_UNIX
8504//
8505// ############################################################################
8506#if defined(FPL_PLATFORM_UNIX)
8507typedef struct fpl__UnixInitState {
8508 int dummy;
8509} fpl__UnixInitState;
8510
8511typedef struct fpl__UnixAppState {
8512 int dummy;
8513} fpl__UnixAppState;
8514#endif // FPL_PLATFORM_UNIX
8515
8516// ############################################################################
8517//
8518// > TYPES_X11
8519//
8520// ############################################################################
8521#if defined(FPL_SUBPLATFORM_X11)
8522
8523#include <X11/keysym.h> // Keyboard symbols (XK_Escape, etc.)
8524
8525//
8526// X11 Api
8527//
8528#define FPL__FUNC_X11_XFree(name) int name(void *data)
8529typedef FPL__FUNC_X11_XFree(fpl__func_x11_XFree);
8530#define FPL__FUNC_X11_XFlush(name) int name(Display *display)
8531typedef FPL__FUNC_X11_XFlush(fpl__func_x11_XFlush);
8532#define FPL__FUNC_X11_XOpenDisplay(name) Display *name(char *display_name)
8533typedef FPL__FUNC_X11_XOpenDisplay(fpl__func_x11_XOpenDisplay);
8534#define FPL__FUNC_X11_XCloseDisplay(name) int name(Display *display)
8535typedef FPL__FUNC_X11_XCloseDisplay(fpl__func_x11_XCloseDisplay);
8536#define FPL__FUNC_X11_XDefaultScreen(name) int name(Display *display)
8537typedef FPL__FUNC_X11_XDefaultScreen(fpl__func_x11_XDefaultScreen);
8538#define FPL__FUNC_X11_XRootWindow(name) Window name(Display *display, int screen_number)
8539typedef FPL__FUNC_X11_XRootWindow(fpl__func_x11_XRootWindow);
8540#define FPL__FUNC_X11_XCreateWindow(name) Window name(Display *display, Window parent, int x, int y, unsigned int width, unsigned int height, unsigned int border_width, int depth, unsigned int clazz, Visual *visual, unsigned long valuemask, XSetWindowAttributes *attributes)
8541typedef FPL__FUNC_X11_XCreateWindow(fpl__func_x11_XCreateWindow);
8542#define FPL__FUNC_X11_XDestroyWindow(name) int name(Display *display, Window w)
8543typedef FPL__FUNC_X11_XDestroyWindow(fpl__func_x11_XDestroyWindow);
8544#define FPL__FUNC_X11_XCreateColormap(name) Colormap name(Display *display, Window w, Visual *visual, int alloc)
8545typedef FPL__FUNC_X11_XCreateColormap(fpl__func_x11_XCreateColormap);
8546#define FPL__FUNC_X11_XDefaultColormap(name) Colormap name(Display *display, int screen_number)
8547typedef FPL__FUNC_X11_XDefaultColormap(fpl__func_x11_XDefaultColormap);
8548#define FPL__FUNC_X11_XFreeColormap(name) int name(Display *display, Colormap colormap)
8549typedef FPL__FUNC_X11_XFreeColormap(fpl__func_x11_XFreeColormap);
8550#define FPL__FUNC_X11_XMapWindow(name) int name(Display *display, Window w)
8551typedef FPL__FUNC_X11_XMapWindow(fpl__func_x11_XMapWindow);
8552#define FPL__FUNC_X11_XUnmapWindow(name) int name(Display *display, Window w)
8553typedef FPL__FUNC_X11_XUnmapWindow(fpl__func_x11_XUnmapWindow);
8554#define FPL__FUNC_X11_XStoreName(name) int name(Display *display, Window w, _Xconst char *window_name)
8555typedef FPL__FUNC_X11_XStoreName(fpl__func_x11_XStoreName);
8556#define FPL__FUNC_X11_XDefaultVisual(name) Visual *name(Display *display, int screen_number)
8557typedef FPL__FUNC_X11_XDefaultVisual(fpl__func_x11_XDefaultVisual);
8558#define FPL__FUNC_X11_XDefaultDepth(name) int name(Display *display, int screen_number)
8559typedef FPL__FUNC_X11_XDefaultDepth(fpl__func_x11_XDefaultDepth);
8560#define FPL__FUNC_X11_XInternAtom(name) Atom name(Display *display, const char *atom_name, Bool only_if_exists)
8561typedef FPL__FUNC_X11_XInternAtom(fpl__func_x11_XInternAtom);
8562#define FPL__FUNC_X11_XSetWMProtocols(name) Status name(Display *display, Window w, Atom *protocols, int count)
8563typedef FPL__FUNC_X11_XSetWMProtocols(fpl__func_x11_XSetWMProtocols);
8564#define FPL__FUNC_X11_XPending(name) int name(Display *display)
8565typedef FPL__FUNC_X11_XPending(fpl__func_x11_XPending);
8566#define FPL__FUNC_X11_XSync(name) int name(Display *display, Bool discard)
8567typedef FPL__FUNC_X11_XSync(fpl__func_x11_XSync);
8568#define FPL__FUNC_X11_XNextEvent(name) int name(Display *display, XEvent *event_return)
8569typedef FPL__FUNC_X11_XNextEvent(fpl__func_x11_XNextEvent);
8570#define FPL__FUNC_X11_XPeekEvent(name) int name(Display *display, XEvent *event_return)
8571typedef FPL__FUNC_X11_XPeekEvent(fpl__func_x11_XPeekEvent);
8572#define FPL__FUNC_X11_XEventsQueued(name) int name(Display *display, int mode)
8573typedef FPL__FUNC_X11_XEventsQueued(fpl__func_x11_XEventsQueued);
8574#define FPL__FUNC_X11_XGetWindowAttributes(name) Status name(Display *display, Window w, XWindowAttributes *window_attributes_return)
8575typedef FPL__FUNC_X11_XGetWindowAttributes(fpl__func_x11_XGetWindowAttributes);
8576#define FPL__FUNC_X11_XResizeWindow(name) int name(Display *display, Window w, unsigned int width, unsigned int height)
8577typedef FPL__FUNC_X11_XResizeWindow(fpl__func_x11_XResizeWindow);
8578#define FPL__FUNC_X11_XMoveWindow(name) int name(Display *display, Window w, int x, int y)
8579typedef FPL__FUNC_X11_XMoveWindow(fpl__func_x11_XMoveWindow);
8580#define FPL__FUNC_X11_XGetKeyboardMapping(name) KeySym *name(Display *display, KeyCode first_keycode, int keycode_count, int *keysyms_per_keycode_return)
8581typedef FPL__FUNC_X11_XGetKeyboardMapping(fpl__func_x11_XGetKeyboardMapping);
8582#define FPL__FUNC_X11_XLookupString(name) int name(XKeyEvent* event_struct, char* buffer_return, int bytes_buffer, KeySym* keysym_return, XComposeStatus* status_in_out)
8583typedef FPL__FUNC_X11_XLookupString(fpl__func_x11_XLookupString);
8584#define FPL__FUNC_X11_XSendEvent(name) Status name(Display *display, Window w, Bool propagate, long event_mask, XEvent *event_send)
8585typedef FPL__FUNC_X11_XSendEvent(fpl__func_x11_XSendEvent);
8586#define FPL__FUNC_X11_XMatchVisualInfo(name) Status name(Display* display, int screen, int depth, int clazz, XVisualInfo* vinfo_return)
8587typedef FPL__FUNC_X11_XMatchVisualInfo(fpl__func_x11_XMatchVisualInfo);
8588#define FPL__FUNC_X11_XCreateGC(name) GC name(Display* display, Drawable d, unsigned long valuemask, XGCValues* values)
8589typedef FPL__FUNC_X11_XCreateGC(fpl__func_x11_XCreateGC);
8590#define FPL__FUNC_X11_XGetImage(name) XImage *name(Display* display, Drawable d, int x, int y, unsigned int width, unsigned int height, unsigned long plane_mask, int format)
8591typedef FPL__FUNC_X11_XGetImage(fpl__func_x11_XGetImage);
8592#define FPL__FUNC_X11_XCreateImage(name) XImage *name(Display *display, Visual *visual, unsigned int depth, int format, int offset, char *data, unsigned int width, unsigned int height, int bitmap_pad, int bytes_per_line)
8593typedef FPL__FUNC_X11_XCreateImage(fpl__func_x11_XCreateImage);
8594#define FPL__FUNC_X11_XPutImage(name) int name(Display *display, Drawable d, GC gc, XImage *image, int src_x, int src_y, int dest_x, int dest_y, unsigned int width, unsigned int height)
8595typedef FPL__FUNC_X11_XPutImage(fpl__func_x11_XPutImage);
8596#define FPL__FUNC_X11_XMapRaised(name) int name(Display *display, Window w)
8597typedef FPL__FUNC_X11_XMapRaised(fpl__func_x11_XMapRaised);
8598#define FPL__FUNC_X11_XCreatePixmap(name) Pixmap name(Display * display, Drawable d, unsigned int width, unsigned int height, unsigned int depth)
8599typedef FPL__FUNC_X11_XCreatePixmap(fpl__func_x11_XCreatePixmap);
8600#define FPL__FUNC_X11_XSelectInput(name) int name(Display * display, Window w, long eventMask)
8601typedef FPL__FUNC_X11_XSelectInput(fpl__func_x11_XSelectInput);
8602#define FPL__FUNC_X11_XGetWindowProperty(name) int name(Display* display, Window w, Atom prop, long long_offset, long long_length, Bool del, Atom req_type, Atom* actual_type_return, int* actual_format_return, unsigned long* nitems_return, unsigned long* bytes_after_return, unsigned char** prop_return)
8603typedef FPL__FUNC_X11_XGetWindowProperty(fpl__func_x11_XGetWindowProperty);
8604#define FPL__FUNC_X11_XChangeProperty(name) int name(Display* display, Window w, Atom property, Atom type, int format, int mode, _Xconst unsigned char* data, int nelements)
8605typedef FPL__FUNC_X11_XChangeProperty(fpl__func_x11_XChangeProperty);
8606#define FPL__FUNC_X11_XDeleteProperty(name) int name(Display* display, Window w,Atom prop)
8607typedef FPL__FUNC_X11_XDeleteProperty(fpl__func_x11_XDeleteProperty);
8608#define FPL__FUNC_X11_XStringListToTextProperty(name) Status name(char** list, int count, XTextProperty* text_prop_return)
8609typedef FPL__FUNC_X11_XStringListToTextProperty(fpl__func_x11_XStringListToTextProperty);
8610#define FPL__FUNC_X11_XSetWMIconName(name) void name(Display* display, Window w, XTextProperty *text_prop)
8611typedef FPL__FUNC_X11_XSetWMIconName(fpl__func_x11_XSetWMIconName);
8612#define FPL__FUNC_X11_XSetWMName(name) void name(Display* display, Window w, XTextProperty *text_prop)
8613typedef FPL__FUNC_X11_XSetWMName(fpl__func_x11_XSetWMName);
8614#define FPL__FUNC_X11_XQueryKeymap(name) int name(Display* display, char [32])
8615typedef FPL__FUNC_X11_XQueryKeymap(fpl__func_x11_XQueryKeymap);
8616#define FPL__FUNC_X11_XQueryPointer(name) Bool name(Display* display, Window w, Window* root_return, Window* child_return, int* root_x_return, int* root_y_return, int* win_x_return, int* win_y_return, unsigned int* mask_return)
8617typedef FPL__FUNC_X11_XQueryPointer(fpl__func_x11_XQueryPointer);
8618#define FPL__FUNC_X11_XConvertSelection(name) int name(Display *display, Atom selection, Atom target, Atom property, Window requestor, Time time)
8619typedef FPL__FUNC_X11_XConvertSelection(fpl__func_x11_XConvertSelection);
8620#define FPL__FUNC_X11_XInitThreads(name) Status name(void)
8621typedef FPL__FUNC_X11_XInitThreads(fpl__func_x11_XInitThreads);
8622#define FPL__FUNC_X11_XSetErrorHandler(name) XErrorHandler name(XErrorHandler *handler)
8623typedef FPL__FUNC_X11_XSetErrorHandler(fpl__func_x11_XSetErrorHandler);
8624
8625typedef struct fpl__X11Api {
8626 void *libHandle;
8627 fpl__func_x11_XFlush *XFlush;
8628 fpl__func_x11_XFree *XFree;
8629 fpl__func_x11_XOpenDisplay *XOpenDisplay;
8630 fpl__func_x11_XCloseDisplay *XCloseDisplay;
8631 fpl__func_x11_XDefaultScreen *XDefaultScreen;
8632 fpl__func_x11_XRootWindow *XRootWindow;
8633 fpl__func_x11_XCreateWindow *XCreateWindow;
8634 fpl__func_x11_XDestroyWindow *XDestroyWindow;
8635 fpl__func_x11_XCreateColormap *XCreateColormap;
8636 fpl__func_x11_XFreeColormap *XFreeColormap;
8637 fpl__func_x11_XDefaultColormap *XDefaultColormap;
8638 fpl__func_x11_XMapWindow *XMapWindow;
8639 fpl__func_x11_XUnmapWindow *XUnmapWindow;
8640 fpl__func_x11_XStoreName *XStoreName;
8641 fpl__func_x11_XDefaultVisual *XDefaultVisual;
8642 fpl__func_x11_XDefaultDepth *XDefaultDepth;
8643 fpl__func_x11_XInternAtom *XInternAtom;
8644 fpl__func_x11_XSetWMProtocols *XSetWMProtocols;
8645 fpl__func_x11_XPending *XPending;
8646 fpl__func_x11_XSync *XSync;
8647 fpl__func_x11_XNextEvent *XNextEvent;
8648 fpl__func_x11_XPeekEvent *XPeekEvent;
8649 fpl__func_x11_XEventsQueued *XEventsQueued;
8650 fpl__func_x11_XGetWindowAttributes *XGetWindowAttributes;
8651 fpl__func_x11_XResizeWindow *XResizeWindow;
8652 fpl__func_x11_XMoveWindow *XMoveWindow;
8653 fpl__func_x11_XGetKeyboardMapping *XGetKeyboardMapping;
8654 fpl__func_x11_XLookupString *XLookupString;
8655 fpl__func_x11_XSendEvent *XSendEvent;
8656 fpl__func_x11_XMatchVisualInfo *XMatchVisualInfo;
8657 fpl__func_x11_XCreateGC *XCreateGC;
8658 fpl__func_x11_XGetImage *XGetImage;
8659 fpl__func_x11_XPutImage *XPutImage;
8660 fpl__func_x11_XMapRaised *XMapRaised;
8661 fpl__func_x11_XCreateImage *XCreateImage;
8662 fpl__func_x11_XCreatePixmap *XCreatePixmap;
8663 fpl__func_x11_XSelectInput *XSelectInput;
8664 fpl__func_x11_XGetWindowProperty *XGetWindowProperty;
8665 fpl__func_x11_XChangeProperty *XChangeProperty;
8666 fpl__func_x11_XDeleteProperty *XDeleteProperty;
8667 fpl__func_x11_XStringListToTextProperty *XStringListToTextProperty;
8668 fpl__func_x11_XSetWMIconName *XSetWMIconName;
8669 fpl__func_x11_XSetWMName *XSetWMName;
8670 fpl__func_x11_XQueryKeymap *XQueryKeymap;
8671 fpl__func_x11_XQueryPointer *XQueryPointer;
8672 fpl__func_x11_XConvertSelection *XConvertSelection;
8673 fpl__func_x11_XInitThreads *XInitThreads;
8674 fpl__func_x11_XSetErrorHandler *XSetErrorHandler;
8675} fpl__X11Api;
8676
8677fpl_internal void fpl__UnloadX11Api(fpl__X11Api *x11Api) {
8678 fplAssert(x11Api != fpl_null);
8679 if (x11Api->libHandle != fpl_null) {
8680 dlclose(x11Api->libHandle);
8681 }
8682 fplClearStruct(x11Api);
8683}
8684
8685fpl_internal bool fpl__LoadX11Api(fpl__X11Api *x11Api) {
8686 fplAssert(x11Api != fpl_null);
8687 const char *libFileNames[] = {
8688 "libX11.so",
8689 "libX11.so.7",
8690 "libX11.so.6",
8691 "libX11.so.5",
8692 };
8693 bool result = false;
8694 for (uint32_t index = 0; index < fplArrayCount(libFileNames); ++index) {
8695 const char *libName = libFileNames[index];
8696 fplClearStruct(x11Api);
8697 do {
8698 void *libHandle = fpl_null;
8699 FPL__POSIX_LOAD_LIBRARY(FPL__MODULE_X11, libHandle, libName);
8700 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XFlush, XFlush);
8701 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XFree, XFree);
8702 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XOpenDisplay, XOpenDisplay);
8703 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCloseDisplay, XCloseDisplay);
8704 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDefaultScreen, XDefaultScreen);
8705 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XRootWindow, XRootWindow);
8706 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCreateWindow, XCreateWindow);
8707 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDestroyWindow, XDestroyWindow);
8708 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCreateColormap, XCreateColormap);
8709 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XFreeColormap, XFreeColormap);
8710 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDefaultColormap, XDefaultColormap);
8711 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XMapWindow, XMapWindow);
8712 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XUnmapWindow, XUnmapWindow);
8713 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XStoreName, XStoreName);
8714 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDefaultVisual, XDefaultVisual);
8715 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDefaultDepth, XDefaultDepth);
8716 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XInternAtom, XInternAtom);
8717 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSetWMProtocols, XSetWMProtocols);
8718 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XPending, XPending);
8719 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSync, XSync);
8720 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XNextEvent, XNextEvent);
8721 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XPeekEvent, XPeekEvent);
8722 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XEventsQueued, XEventsQueued);
8723 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XGetWindowAttributes, XGetWindowAttributes);
8724 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XResizeWindow, XResizeWindow);
8725 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XMoveWindow, XMoveWindow);
8726 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XGetKeyboardMapping, XGetKeyboardMapping);
8727 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XLookupString, XLookupString);
8728 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSendEvent, XSendEvent);
8729 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XMatchVisualInfo, XMatchVisualInfo);
8730 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCreateGC, XCreateGC);
8731 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XGetImage, XGetImage);
8732 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XPutImage, XPutImage);
8733 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XMapRaised, XMapRaised);
8734 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCreateImage, XCreateImage);
8735 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XCreatePixmap, XCreatePixmap);
8736 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSelectInput, XSelectInput);
8737 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XGetWindowProperty, XGetWindowProperty);
8738 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XChangeProperty, XChangeProperty);
8739 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XDeleteProperty, XDeleteProperty);
8740 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XStringListToTextProperty, XStringListToTextProperty);
8741 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSetWMIconName, XSetWMIconName);
8742 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSetWMName, XSetWMName);
8743 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XQueryKeymap, XQueryKeymap);
8744 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XQueryPointer, XQueryPointer);
8745 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XConvertSelection, XConvertSelection);
8746 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XInitThreads, XInitThreads);
8747 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_X11, libHandle, libName, x11Api, fpl__func_x11_XSetErrorHandler, XSetErrorHandler);
8748 x11Api->libHandle = libHandle;
8749 result = true;
8750 } while (0);
8751 if (result) {
8752 break;
8753 }
8754 fpl__UnloadX11Api(x11Api);
8755 }
8756 return(result);
8757}
8758
8759typedef struct fpl__X11SubplatformState {
8760 fpl__X11Api api;
8761} fpl__X11SubplatformState;
8762
8763typedef struct fpl__X11WindowStateInfo {
8764 fplWindowState state;
8765 fplWindowVisibilityState visibility;
8766 fplWindowPosition position;
8767 fplWindowSize size;
8768} fpl__X11WindowStateInfo;
8769
8770typedef struct fpl__X11Xdnd {
8771 int version;
8772 Window source;
8773 Atom format;
8774} fpl__X11Xdnd;
8775
8776#define FPL__FUNC_X11_ErrorHandlerCallback(name) int name(Display *display, XErrorEvent *ev)
8777typedef FPL__FUNC_X11_ErrorHandlerCallback(fpl__func_X11ErrorHandlerCallback);
8778
8779typedef struct fpl__X11WindowState {
8780 fpl__X11WindowStateInfo lastWindowStateInfo;
8781 Colormap colorMap;
8782 Display *display;
8783 fpl__func_X11ErrorHandlerCallback *lastErrorHandler;
8784 fpl__X11Xdnd xdnd;
8785 Window root;
8786 Window window;
8787 Visual *visual;
8788 Atom wmProtocols;
8789 Atom wmDeleteWindow;
8790 Atom wmState;
8791 Atom netWMPing;
8792 Atom netWMState;
8793 Atom netWMStateFocused;
8794 Atom netWMStateFullscreen;
8795 Atom netWMStateHidden;
8796 Atom netWMStateMaximizedVert;
8797 Atom netWMStateMaximizedHorz;
8798 Atom netWMPid;
8799 Atom netWMIcon;
8800 Atom netWMName;
8801 Atom netWMIconName;
8802 Atom utf8String;
8803 Atom motifWMHints;
8804 // drag and drop
8805 Atom xdndAware;
8806 Atom xdndEnter;
8807 Atom xdndPosition;
8808 Atom xdndStatus;
8809 Atom xdndActionCopy;
8810 Atom xdndDrop;
8811 Atom xdndFinished;
8812 Atom xdndSelection;
8813 Atom xdndTypeList;
8814 Atom textUriList;
8815 int screen;
8816 int colorDepth;
8817} fpl__X11WindowState;
8818
8819#define FPL__XDND_VERSION 5
8820
8821#endif // FPL_SUBPLATFORM_X11
8822
8823// ****************************************************************************
8824//
8825// > PLATFORM_STATES
8826//
8827// - Defines the final PlatformInitState and PlatformAppState
8828// - Declares all required global variables
8829//
8830// ****************************************************************************
8831#if !defined(FPL__PLATFORM_STATES_DEFINED)
8832#define FPL__PLATFORM_STATES_DEFINED
8833//
8834// Platform initialization state
8835//
8836typedef struct fpl__PlatformInitSettings {
8837 fplMemorySettings memorySettings;
8838} fpl__PlatformInitSettings;
8839
8840typedef struct fpl__PlatformInitState {
8841#if defined(FPL_SUBPLATFORM_POSIX)
8842 fpl__PosixInitState posix;
8843#endif
8844
8845 fpl__PlatformInitSettings initSettings;
8846 fplPlatformResultType initResult;
8847 fpl_b32 isInitialized;
8848
8849 union {
8850# if defined(FPL_PLATFORM_WINDOWS)
8851 fpl__Win32InitState win32;
8852# elif defined(FPL_PLATFORM_LINUX)
8853 fpl__LinuxInitState plinux;
8854# elif defined(FPL_PLATFORM_UNIX)
8855 fpl__UnixInitState punix;
8856# endif
8857 };
8858} fpl__PlatformInitState;
8859fpl_globalvar fpl__PlatformInitState fpl__global__InitState = fplZeroInit;
8860
8861#if defined(FPL__ENABLE_WINDOW)
8862#define FPL__MAX_EVENT_COUNT 32768
8863typedef struct fpl__EventQueue {
8864 // @FIXME(final): Internal events are not Thread-Safe!
8865 fplEvent events[FPL__MAX_EVENT_COUNT];
8866 uint32_t pollIndex;
8867 uint32_t pushCount;
8868} fpl__EventQueue;
8869
8870typedef struct fpl__PlatformWindowState {
8871 fpl__EventQueue eventQueue;
8872 fplKey keyMap[256];
8873 fplButtonState keyStates[256];
8874 uint64_t keyPressTimes[256];
8875 fplButtonState mouseStates[5];
8876 fpl_b32 isRunning;
8877
8878#if defined(FPL_PLATFORM_WINDOWS)
8879 fpl__Win32WindowState win32;
8880#endif
8881#if defined(FPL_SUBPLATFORM_X11)
8882 fpl__X11WindowState x11;
8883#endif
8884} fpl__PlatformWindowState;
8885#endif // FPL__ENABLE_WINDOW
8886
8887#if defined(FPL__ENABLE_VIDEO)
8888typedef struct fpl__PlatformVideoState {
8889 void *mem; // Points to fpl__VideoState
8890 size_t memSize;
8891} fpl__PlatformVideoState;
8892#endif // FPL__ENABLE_VIDEO
8893
8894#if defined(FPL__ENABLE_AUDIO)
8895typedef struct fpl__PlatformAudioState {
8896 void *mem; // Points to fpl__AudioState
8897 size_t memSize;
8898} fpl__PlatformAudioState;
8899#endif
8900
8901//
8902// Platform application state
8903//
8904typedef struct fpl__PlatformAppState fpl__PlatformAppState;
8905struct fpl__PlatformAppState {
8906 // Subplatforms
8907#if defined(FPL_SUBPLATFORM_POSIX)
8908 fpl__PosixAppState posix;
8909#endif
8910#if defined(FPL_SUBPLATFORM_X11)
8911 fpl__X11SubplatformState x11;
8912#endif
8913
8914 // Window/video/audio
8915#if defined(FPL__ENABLE_WINDOW)
8916 fpl__PlatformWindowState window;
8917#endif
8918#if defined(FPL__ENABLE_VIDEO)
8919 fpl__PlatformVideoState video;
8920#endif
8921#if defined(FPL__ENABLE_AUDIO)
8922 fpl__PlatformAudioState audio;
8923#endif
8924
8925 // Settings
8926 fplSettings initSettings;
8927 fplSettings currentSettings;
8928 fplInitFlags initFlags;
8929
8930 // Platforms
8931 union {
8932# if defined(FPL_PLATFORM_WINDOWS)
8933 fpl__Win32AppState win32;
8934# elif defined(FPL_PLATFORM_LINUX)
8935 fpl__LinuxAppState plinux;
8936# elif defined(FPL_PLATFORM_UNIX)
8937 fpl__UnixAppState plinux;
8938# endif
8939 };
8940};
8941
8942//
8943// Internal window
8944//
8945#if defined(FPL__ENABLE_WINDOW)
8946fpl_internal fplKey fpl__GetMappedKey(const fpl__PlatformWindowState *windowState, const uint64_t keyCode) {
8947 fplKey result;
8948 if (keyCode < fplArrayCount(windowState->keyMap))
8949 result = windowState->keyMap[keyCode];
8950 else
8951 result = fplKey_None;
8952 return(result);
8953}
8954
8955fpl_internal void fpl__ClearInternalEvents() {
8956 fpl__PlatformAppState *appState = fpl__global__AppState;
8957 fplAssert(appState != fpl_null);
8958 fpl__EventQueue *eventQueue = &appState->window.eventQueue;
8959 // @FIXME(final): Internal events are not thread-safe, introduce a proper thread-safe queue here!
8960 uint32_t eventCount = eventQueue->pollIndex;
8961 eventQueue->pollIndex = 0;
8962 for (size_t eventIndex = 0; eventIndex < eventCount; ++eventIndex) {
8963 fplEvent *ev = &eventQueue->events[eventIndex];
8965 fpl__ReleaseDynamicMemory(ev->window.dropFiles.internalMemory.base);
8967 }
8968 }
8969 eventQueue->pushCount = 0;
8970}
8971
8972fpl_internal bool fpl__PollInternalEvent(fplEvent *ev) {
8973 fpl__PlatformAppState *appState = fpl__global__AppState;
8974 bool result = false;
8975 if (appState != fpl_null) {
8976 fpl__EventQueue *eventQueue = &appState->window.eventQueue;
8977 // @FIXME(final): Internal events are not thread-safe, introduce a proper thread-safe queue here!
8978 if (eventQueue->pollIndex < eventQueue->pushCount) {
8979 uint32_t eventIndex = eventQueue->pollIndex++;
8980 *ev = eventQueue->events[eventIndex];
8981 result = true;
8982 } else if (eventQueue->pushCount > 0) {
8983 eventQueue->pollIndex = 0;
8984 eventQueue->pushCount = 0;
8985 }
8986 }
8987 return(result);
8988}
8989
8990fpl_internal void fpl__PushInternalEvent(const fplEvent *event) {
8991 fpl__PlatformAppState *appState = fpl__global__AppState;
8992 fplAssert(appState != fpl_null);
8993 fpl__EventQueue *eventQueue = &appState->window.eventQueue;
8994 // @FIXME(final): Internal events are not thread-safe, introduce a proper thread-safe queue here!
8995 if (eventQueue->pushCount < FPL__MAX_EVENT_COUNT) {
8996 uint32_t eventIndex = eventQueue->pushCount++;
8997 eventQueue->events[eventIndex] = *event;
8998 }
8999}
9000
9001fpl_internal void fpl__PushWindowStateEvent(const fplWindowEventType windowType) {
9002 fplEvent newEvent = fplZeroInit;
9003 newEvent.type = fplEventType_Window;
9004 newEvent.window.type = windowType;
9005 fpl__PushInternalEvent(&newEvent);
9006}
9007
9008fpl_internal void fpl__PushWindowSizeEvent(const fplWindowEventType windowType, uint32_t w, uint32_t h) {
9009 fplEvent newEvent = fplZeroInit;
9010 newEvent.type = fplEventType_Window;
9011 newEvent.window.type = windowType;
9012 newEvent.window.size.width = w;
9013 newEvent.window.size.height = h;
9014 fpl__PushInternalEvent(&newEvent);
9015}
9016
9017fpl_internal void fpl__PushWindowPositionEvent(const fplWindowEventType windowType, int32_t x, int32_t y) {
9018 fplEvent newEvent = fplZeroInit;
9019 newEvent.type = fplEventType_Window;
9020 newEvent.window.type = windowType;
9021 newEvent.window.position.left = x;
9022 newEvent.window.position.top = y;
9023 fpl__PushInternalEvent(&newEvent);
9024}
9025
9026fpl_internal void fpl__PushWindowDropFilesEvent(const char *filePath, const size_t fileCount, const char **files, const fplMemoryBlock *memory) {
9027 fplEvent newEvent = fplZeroInit;
9028 newEvent.type = fplEventType_Window;
9030 newEvent.window.dropFiles.fileCount = fileCount;
9031 newEvent.window.dropFiles.files = files;
9032 newEvent.window.dropFiles.internalMemory = *memory;
9033 fpl__PushInternalEvent(&newEvent);
9034}
9035
9036fpl_internal void fpl__PushKeyboardButtonEvent(const uint64_t keyCode, const fplKey mappedKey, const fplKeyboardModifierFlags modifiers, const fplButtonState buttonState) {
9037 fplEvent newEvent = fplZeroInit;
9038 newEvent.type = fplEventType_Keyboard;
9040 newEvent.keyboard.keyCode = keyCode;
9041 newEvent.keyboard.modifiers = modifiers;
9042 newEvent.keyboard.buttonState = buttonState;
9043 newEvent.keyboard.mappedKey = mappedKey;
9044 fpl__PushInternalEvent(&newEvent);
9045}
9046
9047fpl_internal void fpl__PushKeyboardInputEvent(const uint32_t textCode, const fplKey mappedKey) {
9048 fplEvent newEvent = fplZeroInit;
9049 newEvent.type = fplEventType_Keyboard;
9051 newEvent.keyboard.keyCode = (uint64_t)textCode;
9052 newEvent.keyboard.mappedKey = mappedKey;
9053 fpl__PushInternalEvent(&newEvent);
9054}
9055
9056fpl_internal void fpl__PushMouseButtonEvent(const int32_t x, const int32_t y, const fplMouseButtonType mouseButton, const fplButtonState buttonState) {
9057 fplEvent newEvent = fplZeroInit;
9058 newEvent.type = fplEventType_Mouse;
9060 newEvent.mouse.mouseX = x;
9061 newEvent.mouse.mouseY = y;
9062 newEvent.mouse.mouseButton = mouseButton;
9063 newEvent.mouse.buttonState = buttonState;
9064 fpl__PushInternalEvent(&newEvent);
9065}
9066
9067fpl_internal void fpl__PushMouseWheelEvent(const int32_t x, const int32_t y, const float wheelDelta) {
9068 fplEvent newEvent = fplZeroInit;
9069 newEvent.type = fplEventType_Mouse;
9072 newEvent.mouse.mouseX = x;
9073 newEvent.mouse.mouseY = y;
9074 newEvent.mouse.wheelDelta = wheelDelta;
9075 fpl__PushInternalEvent(&newEvent);
9076}
9077
9078fpl_internal void fpl__PushMouseMoveEvent(const int32_t x, const int32_t y) {
9079 fplEvent newEvent = fplZeroInit;
9080 newEvent.type = fplEventType_Mouse;
9083 newEvent.mouse.mouseX = x;
9084 newEvent.mouse.mouseY = y;
9085 fpl__PushInternalEvent(&newEvent);
9086}
9087
9088fpl_internal void fpl__HandleKeyboardButtonEvent(fpl__PlatformWindowState *windowState, const uint64_t time, const uint64_t keyCode, const fplKeyboardModifierFlags modifiers, const fplButtonState buttonState, const bool force) {
9089#if defined(FPL_LOG_KEY_EVENTS)
9090 const char *buttonStateName = "";
9091 if (buttonState == fplButtonState_Press)
9092 buttonStateName = "Press";
9093 else if (buttonState == fplButtonState_Repeat)
9094 buttonStateName = "Repeat";
9095 else
9096 buttonStateName = "Released";
9097 FPL_LOG_INFO(FPL__MODULE_OS, "[%llu] Keyboard button event with keycode: '%llu', state: '%s'", time, keyCode, buttonStateName);
9098#endif
9099
9100 fplKey mappedKey = fpl__GetMappedKey(windowState, keyCode);
9101 bool repeat = false;
9102 if (force) {
9103 repeat = (buttonState == fplButtonState_Repeat);
9104 windowState->keyStates[keyCode] = buttonState;
9105 } else {
9106 if (keyCode < fplArrayCount(windowState->keyStates)) {
9107 if ((buttonState == fplButtonState_Release) && (windowState->keyStates[keyCode] == fplButtonState_Release)) {
9108 return;
9109 }
9110 if ((buttonState == fplButtonState_Press) && (windowState->keyStates[keyCode] >= fplButtonState_Press)) {
9111 repeat = true;
9112 }
9113 windowState->keyStates[keyCode] = buttonState;
9114 }
9115 }
9116 fpl__PushKeyboardButtonEvent(keyCode, mappedKey, modifiers, repeat ? fplButtonState_Repeat : buttonState);
9117}
9118
9119fpl_internal void fpl__HandleKeyboardInputEvent(fpl__PlatformWindowState *windowState, const uint64_t keyCode, const uint32_t textCode) {
9120 fplKey mappedKey = fpl__GetMappedKey(windowState, keyCode);
9121 fpl__PushKeyboardInputEvent(textCode, mappedKey);
9122}
9123
9124fpl_internal void fpl__HandleMouseButtonEvent(fpl__PlatformWindowState *windowState, const int32_t x, const int32_t y, const fplMouseButtonType mouseButton, const fplButtonState buttonState) {
9125 if (mouseButton < fplArrayCount(windowState->mouseStates)) {
9126 windowState->mouseStates[(int)mouseButton] = buttonState;
9127 }
9128 fpl__PushMouseButtonEvent(x, y, mouseButton, buttonState);
9129}
9130
9131fpl_internal void fpl__HandleMouseMoveEvent(fpl__PlatformWindowState *windowState, const int32_t x, const int32_t y) {
9132 fpl__PushMouseMoveEvent(x, y);
9133}
9134
9135fpl_internal void fpl__HandleMouseWheelEvent(fpl__PlatformWindowState *windowState, const int32_t x, const int32_t y, const float wheelDelta) {
9136 fpl__PushMouseWheelEvent(x, y, wheelDelta);
9137}
9138
9139// @NOTE(final): Callback used for setup a window before it is created
9140#define FPL__FUNC_PREPARE_VIDEO_WINDOW(name) bool name(fpl__PlatformAppState *appState, const fplInitFlags initFlags, const fplSettings *initSettings)
9141typedef FPL__FUNC_PREPARE_VIDEO_WINDOW(callback_PreSetupWindow);
9142
9143// @NOTE(final): Callback used for setup a window after it was created
9144#define FPL__FUNC_FINALIZE_VIDEO_WINDOW(name) bool name(fpl__PlatformAppState *appState, const fplInitFlags initFlags, const fplSettings *initSettings)
9145typedef FPL__FUNC_FINALIZE_VIDEO_WINDOW(callback_PostSetupWindow);
9146
9147typedef struct fpl__SetupWindowCallbacks {
9148 callback_PreSetupWindow *preSetup;
9149 callback_PostSetupWindow *postSetup;
9150} fpl__SetupWindowCallbacks;
9151#endif // FPL__ENABLE_WINDOW
9152
9153#endif // FPL__PLATFORM_STATES_DEFINED
9154
9155// ****************************************************************************
9156//
9157// > COMMON (Common Implementations)
9158//
9159// - Standard C library includes
9160// - Internal macros
9161// - Error state handling
9162// - Threading state handling
9163// - CommonAudioState
9164// - Common Implementations (Strings, Memory, Atomics, Path, etc.)
9165//
9166// ****************************************************************************
9167#if !defined(FPL__COMMON_DEFINED)
9168#define FPL__COMMON_DEFINED
9169
9170//
9171// Internal memory
9172//
9173fpl_internal void *fpl__AllocateDynamicMemory(const size_t size, const size_t alignment) {
9174 void *result = fpl__AllocateMemory(&fpl__global__InitState.initSettings.memorySettings.dynamic, size, alignment);
9175 return(result);
9176}
9177
9178fpl_internal void fpl__ReleaseDynamicMemory(void *ptr) {
9179 fpl__ReleaseMemory(&fpl__global__InitState.initSettings.memorySettings.dynamic, ptr);
9180}
9181
9182fpl_internal void *fpl__AllocateTemporaryMemory(const size_t size, const size_t alignment) {
9183 void *result = fpl__AllocateMemory(&fpl__global__InitState.initSettings.memorySettings.temporary, size, alignment);
9184 return(result);
9185}
9186
9187fpl_internal void fpl__ReleaseTemporaryMemory(void *ptr) {
9188 fpl__ReleaseMemory(&fpl__global__InitState.initSettings.memorySettings.temporary, ptr);
9189}
9190
9191//
9192// Audio constants
9193//
9194static const uint32_t FPL__DEFAULT_AUDIO_SAMPLERATE = 44100;
9195static const fplAudioFormatType FPL__DEFAULT_AUDIO_FORMAT = fplAudioFormatType_S16;
9196static const uint32_t FPL__DEFAULT_AUDIO_CHANNELS = 2;
9197static const uint32_t FPL__DEFAULT_AUDIO_PERIODS = 3;
9198static const uint32_t FPL__DEFAULT_AUDIO_BUFFERSIZE_LOWLATENCY_IN_MSECS = 10;
9199static const uint32_t FPL__DEFAULT_AUDIO_BUFFERSIZE_CONSERVATIVE_IN_MSECS = 25;
9200
9201//
9202// Internal types and functions
9203//
9204#define FPL__MAX_LAST_ERROR_STRING_LENGTH 256
9205#define FPL__MAX_ERRORSTATE_COUNT 256
9206
9207typedef struct fpl__ErrorState {
9208 char errors[FPL__MAX_ERRORSTATE_COUNT][FPL__MAX_LAST_ERROR_STRING_LENGTH];
9209 uint32_t count;
9210} fpl__ErrorState;
9211
9212fpl_globalvar fpl__ErrorState fpl__global__LastErrorState = fplZeroInit;
9213
9214fpl_internal void fpl__PushError_Formatted(const char *funcName, const int lineNumber, const fplLogLevel level, const char *format, va_list argList) {
9215 fplAssert(format != fpl_null);
9216
9217 if (level <= fplLogLevel_Error) {
9218 fpl__ErrorState *state = &fpl__global__LastErrorState;
9219 char buffer[FPL__MAX_LAST_ERROR_STRING_LENGTH] = fplZeroInit;
9220 size_t formattedLen = fplStringFormatArgs(buffer, fplArrayCount(buffer), format, argList);
9221 if (formattedLen > 0) {
9222 fplAssert(state->count < FPL__MAX_ERRORSTATE_COUNT);
9223 size_t errorIndex = state->count;
9224 state->count = (state->count + 1) % FPL__MAX_ERRORSTATE_COUNT;
9225 fplCopyStringLen(buffer, formattedLen, state->errors[errorIndex], FPL__MAX_LAST_ERROR_STRING_LENGTH);
9226 }
9227 }
9228
9229#if defined(FPL__ENABLE_LOGGING)
9230 va_list listCopy;
9231 va_copy(listCopy, argList);
9232 fpl__LogWriteArgs(funcName, lineNumber, level, format, listCopy);
9233 va_end(listCopy);
9234#endif
9235}
9236
9237fpl_internal void fpl__HandleError(const char *funcName, const int lineNumber, const fplLogLevel level, const char *format, ...) {
9238 va_list valist;
9239 va_start(valist, format);
9240 fpl__PushError_Formatted(funcName, lineNumber, level, format, valist);
9241 va_end(valist);
9242
9243#if defined(FPL_CRASH_ON_ERROR) || defined(FPL_CRASH_ON_WARNING)
9244 fplLogLevel minErrorLevel = fplLogLevel_Error;
9245# if defined(FPL_CRASH_ON_WARNING)
9246 minErrorLevel = fplLogLevel_Warning;
9247# endif
9248 if (level >= minErrorLevel) {
9249 // @NOTE(final): Force a null pointer assignment crash here
9250 *(int *)0 = 0;
9251 }
9252#endif
9253}
9254
9255//
9256// Argument Errors
9257//
9258
9259//
9260
9261
9262fpl_internal void fpl__ArgumentInvalidError(const char *funcName, const int line, const char *paramName) {
9263 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter are not valid", paramName);
9264}
9265fpl_internal void fpl__ArgumentNullError(const char *funcName, const int line, const char *paramName) {
9266 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter are not allowed to be null", paramName);
9267}
9268fpl_internal void fpl__ArgumentZeroError(const char *funcName, const int line, const char *paramName) {
9269 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter must be greater than zero", paramName);
9270}
9271fpl_internal void fpl__ArgumentMinError(const char *funcName, const int line, const char *paramName, const size_t value, const size_t minValue) {
9272 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter '%zu' must be greater or equal than '%zu'", paramName, value, minValue);
9273}
9274fpl_internal void fpl__ArgumentMaxError(const char *funcName, const int line, const char *paramName, const size_t value, const size_t maxValue) {
9275 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter '%zu' must be less or equal than '%zu'", paramName, value, maxValue);
9276}
9277fpl_internal void fpl__ArgumentRangeError(const char *funcName, const int line, const char *paramName, const size_t value, const size_t minValue, const size_t maxValue) {
9278 FPL__M_ERROR(funcName, line, FPL__MODULE_ARGS, "%s parameter '%zu' must be in range of '%zu' to '%zu'", paramName, value, minValue, maxValue);
9279}
9280
9281#define FPL__CheckArgumentInvalid(arg, cond, ret) \
9282 if((cond)) { \
9283 fpl__ArgumentInvalidError(FPL_FUNCTION_NAME, __LINE__, #arg); \
9284 return (ret); \
9285 }
9286
9287#define FPL__CheckArgumentInvalidNoRet(arg, cond) \
9288 if((cond)) { \
9289 fpl__ArgumentInvalidError(FPL_FUNCTION_NAME, __LINE__, #arg); \
9290 return; \
9291 }
9292#define FPL__CheckArgumentNull(arg, ret) \
9293 if((arg) == fpl_null) { \
9294 fpl__ArgumentNullError(FPL_FUNCTION_NAME, __LINE__, #arg); \
9295 return (ret); \
9296 }
9297#define FPL__CheckArgumentNullNoRet(arg) \
9298 if((arg) == fpl_null) { \
9299 fpl__ArgumentNullError(FPL_FUNCTION_NAME, __LINE__, #arg); \
9300 return; \
9301 }
9302#define FPL__CheckArgumentZero(arg, ret) \
9303 if((arg) == 0) { \
9304 fpl__ArgumentZeroError(FPL_FUNCTION_NAME, __LINE__, #arg); \
9305 return (ret); \
9306 }
9307#define FPL__CheckArgumentZeroNoRet(arg) \
9308 if((arg) == 0) { \
9309 fpl__ArgumentZeroError(FPL_FUNCTION_NAME, __LINE__, #arg); \
9310 return; \
9311 }
9312#define FPL__CheckArgumentMin(arg, minValue, ret) \
9313 if((arg) < (minValue)) { \
9314 fpl__ArgumentMinError(FPL_FUNCTION_NAME, __LINE__, #arg, arg, minValue); \
9315 return (ret); \
9316 }
9317#define FPL__CheckArgumentMax(arg, maxValue, ret) \
9318 if((arg) > (maxValue)) { \
9319 fpl__ArgumentMaxError(FPL_FUNCTION_NAME, __LINE__, #arg, arg, maxValue); \
9320 return (ret); \
9321 }
9322#define FPL__CheckPlatform(ret) \
9323 if(fpl__global__AppState == fpl_null) { \
9324 FPL__ERROR(FPL__MODULE_CORE, "[%s] Platform is not initialized", FPL_FUNCTION_NAME); \
9325 return (ret); \
9326 }
9327#define FPL__CheckPlatformNoRet() \
9328 if(fpl__global__AppState == fpl_null) { \
9329 FPL__ERROR(FPL__MODULE_CORE, "[%s] Platform is not initialized", FPL_FUNCTION_NAME); \
9330 return; \
9331 }
9332
9333#define FPL__CheckApi(cond, name, ret) \
9334 if(!(cond)) { \
9335 FPL__ERROR(FPL__MODULE_API, "The API '%s' is not loaded", (name)); \
9336 return (ret); \
9337 }
9338#define FPL__CheckApiNoRet(cond, name) \
9339 if(!(cond)) { \
9340 FPL__ERROR(FPL__MODULE_API, "The API '%s' is not loaded", (name)); \
9341 return; \
9342 }
9343
9344#if !defined(FPL_MAX_THREAD_COUNT)
9345 // Maximum number of active threads you can have in your process
9346# define FPL_MAX_THREAD_COUNT 256
9347#endif
9348
9349#if !defined(FPL_MAX_SIGNAL_COUNT)
9350 // Maximum number of active signals you can wait for
9351# define FPL_MAX_SIGNAL_COUNT 256
9352#endif
9353
9354typedef struct fpl__ThreadState {
9355 fplThreadHandle mainThread;
9356 fplThreadHandle threads[FPL_MAX_THREAD_COUNT];
9357} fpl__ThreadState;
9358
9359fpl_globalvar fpl__ThreadState fpl__global__ThreadState = fplZeroInit;
9360
9361fpl_internal fplThreadHandle *fpl__GetFreeThread() {
9362 fplThreadHandle *result = fpl_null;
9363 for (uint32_t index = 0; index < FPL_MAX_THREAD_COUNT; ++index) {
9364 fplThreadHandle *thread = fpl__global__ThreadState.threads + index;
9365 fplThreadState state = fplGetThreadState(thread);
9366 if (state == fplThreadState_Stopped) {
9367 result = thread;
9368 break;
9369 }
9370 }
9371 return(result);
9372}
9373
9374fpl_internal bool fpl__IsEqualsMemory(const void *a, const void *b, const size_t size) {
9375 const uint8_t *ptrA = (const uint8_t *)a;
9376 const uint8_t *ptrB = (const uint8_t *)b;
9377 size_t s = size;
9378 // @SPEED(final): This may be very slow, so we should use a faster function for comparing memory.
9379 bool result = true;
9380#if 1
9381 while (s-- > 0) {
9382 if (*ptrA != *ptrB) {
9383 result = false;
9384 break;
9385 }
9386 ptrA++;
9387 ptrB++;
9388 }
9389#else
9390 result = memcmp(a, b, size) == 0;
9391#endif
9392 return(result);
9393}
9394
9395fpl_internal bool fpl__IsZeroMemory(const void *memory, const size_t size) {
9396 const uint8_t *ptr = (const uint8_t *)memory;
9397 // @SPEED(final): This may be very slow, so we should use a faster function for comparing memory.
9398 bool result = true;
9399#if 1
9400 size_t s = size;
9401 while (s-- > 0) {
9402 if (*ptr) {
9403 result = false;
9404 break;
9405 }
9406 ptr++;
9407 }
9408#else
9409 result = memcmp(a, b, size) == 0;
9410#endif
9411 return(result);
9412}
9413
9414//
9415// Common Strings
9416//
9417#if !defined(FPL__COMMON_STRINGS_DEFINED)
9418#define FPL__COMMON_STRINGS_DEFINED
9419
9420fpl_common_api bool fplIsStringMatchWildcard(const char *source, const char *wildcard) {
9421 // Supported patterns:
9422 // * = Match zero or more characters
9423 // ? = Match one character
9424 if (source == fpl_null || wildcard == fpl_null) {
9425 return false;
9426 }
9427 const char *s = source;
9428 const char *w = wildcard;
9429 while (*w) {
9430 if (*w == '?') {
9431 if (!*s) {
9432 return false;
9433 }
9434 ++s;
9435 } else if (*w == '*') {
9436 while (*s) {
9437 char nw = w[1];
9438 if (nw != 0) {
9439 if ((*s == nw) || (nw == '?') || (nw == '*')) {
9440 break;
9441 }
9442 }
9443 ++s;
9444 }
9445 } else {
9446 if (*s != *w) {
9447 return false;
9448 }
9449 ++s;
9450 }
9451 ++w;
9452 }
9453 return true;
9454}
9455
9456fpl_common_api bool fplIsStringEqualLen(const char *a, const size_t aLen, const char *b, const size_t bLen) {
9457 if ((a == fpl_null) || (b == fpl_null)) {
9458 return false;
9459 }
9460 if (aLen != bLen) {
9461 return false;
9462 }
9463 bool result = true;
9464 for (size_t index = 0; index < aLen; ++index) {
9465 char aChar = a[index];
9466 char bChar = b[index];
9467 if (aChar != bChar) {
9468 result = false;
9469 break;
9470 }
9471 }
9472 return(result);
9473}
9474
9475fpl_common_api bool fplIsStringEqual(const char *a, const char *b) {
9476 if ((a == fpl_null) || (b == fpl_null)) {
9477 return (a == b);
9478 }
9479 bool result = true;
9480 for (;;) {
9481 const char aChar = *(a++);
9482 const char bChar = *(b++);
9483 if (aChar == 0 || bChar == 0) {
9484 result = (aChar == bChar);
9485 break;
9486 }
9487 if (aChar != bChar) {
9488 result = false;
9489 break;
9490 }
9491 }
9492 return(result);
9493}
9494
9495fpl_common_api char *fplEnforcePathSeparatorLen(char *path, size_t maxPathLen) {
9496 FPL__CheckArgumentNull(path, fpl_null);
9497 FPL__CheckArgumentZero(maxPathLen, fpl_null);
9498 char *end = path;
9499 while (*end) {
9500 end++;
9501 }
9502 size_t len = end - path;
9503 char *result = fpl_null;
9504 if (len > 0) {
9505 if (path[len - 1] != FPL_PATH_SEPARATOR) {
9506 if (len + 1 <= maxPathLen) {
9507 path[len] = FPL_PATH_SEPARATOR;
9508 path[len + 1] = 0;
9509 result = &path[len + 1];
9510 } else {
9511 FPL__ERROR(FPL__MODULE_PATHS, "Cannot append path separator: Max length '%zu' of path '%s' is exceeded", maxPathLen, path);
9512 }
9513 } else {
9514 result = &path[len];
9515 }
9516 }
9517 return(result);
9518}
9519
9520fpl_common_api char *fplEnforcePathSeparator(char *path) {
9521 FPL__CheckArgumentNull(path, fpl_null);
9522 char *end = path;
9523 while (*end) {
9524 end++;
9525 }
9526 size_t len = end - path;
9527 char *result = fpl_null;
9528 if (len > 0) {
9529 if (path[len - 1] != FPL_PATH_SEPARATOR) {
9530 path[len] = FPL_PATH_SEPARATOR;
9531 path[len + 1] = 0;
9532 result = &path[len + 1];
9533 } else {
9534 result = &path[len];
9535 }
9536 }
9537 return(result);
9538}
9539
9540fpl_common_api char *fplStringAppendLen(const char *appended, const size_t appendedLen, char *buffer, size_t maxBufferLen) {
9541 FPL__CheckArgumentNull(appended, fpl_null);
9542 FPL__CheckArgumentZero(maxBufferLen, fpl_null);
9543 if (appendedLen == 0) {
9544 return buffer;
9545 }
9546 size_t curBufferLen = fplGetStringLength(buffer);
9547 size_t requiredSize = curBufferLen + appendedLen + 1;
9548 FPL__CheckArgumentMin(maxBufferLen, requiredSize, fpl_null);
9549 char *start = buffer + curBufferLen;
9550 size_t remainingBufferSize = maxBufferLen - (curBufferLen > 0 ? curBufferLen + 1 : 0);
9551 char *result = fplCopyStringLen(appended, appendedLen, start, remainingBufferSize);
9552 return(result);
9553}
9554
9555fpl_common_api char *fplStringAppend(const char *appended, char *buffer, size_t maxBufferLen) {
9556 size_t appendedLen = fplGetStringLength(appended);
9557 char *result = fplStringAppendLen(appended, appendedLen, buffer, maxBufferLen);
9558 return(result);
9559}
9560
9561fpl_common_api size_t fplGetStringLength(const char *str) {
9562 uint32_t result = 0;
9563 if (str != fpl_null) {
9564 while (*str++) {
9565 result++;
9566 }
9567 }
9568 return(result);
9569}
9570
9571fpl_common_api char *fplCopyStringLen(const char *source, const size_t sourceLen, char *dest, const size_t maxDestLen) {
9572 if (source != fpl_null && dest != fpl_null) {
9573 size_t requiredLen = sourceLen + 1;
9574 FPL__CheckArgumentMin(maxDestLen, requiredLen, fpl_null);
9575 fplMemoryCopy(source, sourceLen * sizeof(char), dest);
9576 char *result = dest + sourceLen;
9577 *result = 0;
9578 return(result);
9579 } else {
9580 return(fpl_null);
9581 }
9582}
9583
9584fpl_common_api char *fplCopyString(const char *source, char *dest, const size_t maxDestLen) {
9585 char *result = fpl_null;
9586 if (source != fpl_null) {
9587 size_t sourceLen = fplGetStringLength(source);
9588 result = fplCopyStringLen(source, sourceLen, dest, maxDestLen);
9589 }
9590 return(result);
9591}
9592
9593fpl_common_api size_t fplStringFormatArgs(char *destBuffer, const size_t maxDestBufferLen, const char *format, va_list argList) {
9594 FPL__CheckArgumentNull(format, 0);
9595
9596 va_list listCopy;
9597 va_copy(listCopy, argList);
9598
9599 // @NOTE(final): Need to clear the first character, otherwise vsnprintf() does weird things... O_o
9600 if (destBuffer != fpl_null) {
9601 FPL__CheckArgumentMin(maxDestBufferLen, 1, 0);
9602 destBuffer[0] = 0;
9603 }
9604
9605 int charCount = 0;
9606# if defined(FPL_NO_CRT)
9607# if defined(FPL_USERFUNC_vsnprintf)
9608 charCount = FPL_USERFUNC_vsnprintf(destBuffer, maxDestBufferLen, format, listCopy);
9609# else
9610 charCount = 0;
9611# endif
9612# else
9613 charCount = vsnprintf(destBuffer, maxDestBufferLen, format, listCopy);
9614# endif
9615 if (charCount < 0) {
9616 FPL__ERROR(FPL__MODULE_STRINGS, "Format parameter are '%s' are invalid", format);
9617 return 0;
9618 }
9619 size_t result = charCount;
9620 if (destBuffer != fpl_null) {
9621 size_t requiredMaxDestBufferLen = charCount + 1;
9622 FPL__CheckArgumentMin(maxDestBufferLen, requiredMaxDestBufferLen, 0);
9623 destBuffer[charCount] = 0;
9624 }
9625 va_end(listCopy);
9626 return(result);
9627}
9628
9629fpl_common_api size_t fplStringFormat(char *destBuffer, const size_t maxDestBufferLen, const char *format, ...) {
9630 FPL__CheckArgumentNull(format, 0);
9631 va_list argList;
9632 va_start(argList, format);
9633 size_t result = fplStringFormatArgs(destBuffer, maxDestBufferLen, format, argList);
9634 va_end(argList);
9635 return(result);
9636}
9637
9638fpl_common_api size_t fplS32ToString(const int32_t value, char *buffer, const size_t maxBufferLen) {
9639 int32_t v = value;
9640
9641 bool isNegative = false;
9642 if (v < 0) {
9643 isNegative = true;
9644 v = -v;
9645 }
9646
9647 int32_t tmp = v;
9648 size_t digitCount = 0;
9649 do {
9650 tmp = tmp / 10;
9651 ++digitCount;
9652 } while (tmp != 0);
9653
9654 size_t result = digitCount + (isNegative ? 1 : 0);
9655
9656 if (buffer != fpl_null) {
9657 size_t requiredLen = result + 1;
9658 FPL__CheckArgumentMin(maxBufferLen, requiredLen, 0);
9659
9660 char *p = buffer;
9661 if (isNegative) {
9662 *p++ = '-';
9663 }
9664 p += digitCount;// Go back to the very end, because we are writing the digits back in reverse order
9665 char *lastP = p;
9666
9667 const char *digits = "0123456789";
9668 tmp = value;
9669 do {
9670 *--p = digits[tmp % 10];
9671 tmp /= 10;
9672 } while (tmp != 0);
9673
9674 *lastP = 0;
9675 }
9676
9677 return (result);
9678}
9679
9680fpl_common_api int32_t fplStringToS32Len(const char *str, const size_t len) {
9681 FPL__CheckArgumentNull(str, 0);
9682 FPL__CheckArgumentZero(len, 0);
9683 const char *p = str;
9684 bool isNegative = false;
9685 if (*p == '-') {
9686 if (len == 1) {
9687 return 0;
9688 }
9689 isNegative = true;
9690 ++p;
9691 }
9692 uint32_t value = 0;
9693 while (*p && ((size_t)(p - str) < len)) {
9694 char c = *p;
9695 if (c < '0' || c > '9') {
9696 return(0);
9697 }
9698 int v = (int)(*p - '0');
9699 value *= 10;
9700 value += (uint32_t)v;
9701 ++p;
9702 }
9703 int32_t result = isNegative ? -(int32_t)value : (int32_t)value;
9704 return(result);
9705}
9706
9707fpl_common_api int32_t fplStringToS32(const char *str) {
9708 size_t len = fplGetStringLength(str);
9709 int32_t result = fplStringToS32Len(str, len);
9710 return(result);
9711}
9712#endif // FPL__COMMON_STRINGS_DEFINED
9713
9714//
9715// Common Console
9716//
9717#if !defined(FPL__COMMON_CONSOLE_DEFINED)
9718#define FPL__COMMON_CONSOLE_DEFINED
9719
9720fpl_common_api void fplConsoleFormatOut(const char *format, ...) {
9721 FPL__CheckArgumentNullNoRet(format);
9722 char buffer[FPL_MAX_BUFFER_LENGTH];
9723 va_list argList;
9724 va_start(argList, format);
9725 size_t len = fplStringFormatArgs(buffer, fplArrayCount(buffer), format, argList);
9726 va_end(argList);
9727 if (len > 0) {
9728 fplConsoleOut(buffer);
9729 }
9730}
9731
9732fpl_common_api void fplConsoleFormatError(const char *format, ...) {
9733 FPL__CheckArgumentNullNoRet(format);
9734 char buffer[FPL_MAX_BUFFER_LENGTH];
9735 va_list argList;
9736 va_start(argList, format);
9737 size_t len = fplStringFormatArgs(buffer, fplArrayCount(buffer), format, argList);
9738 va_end(argList);
9739 if (len > 0) {
9740 fplConsoleError(buffer);
9741 }
9742}
9743#endif // FPL__COMMON_CONSOLE_DEFINED
9744
9745//
9746// Common Memory
9747//
9748#if !defined(FPL__COMMON_MEMORY_DEFINED)
9749#define FPL__COMMON_MEMORY_DEFINED
9750
9751fpl_common_api void *fplMemoryAlignedAllocate(const size_t size, const size_t alignment) {
9752 FPL__CheckArgumentZero(size, fpl_null);
9753 FPL__CheckArgumentZero(alignment, fpl_null);
9754 if (alignment & (alignment - 1)) {
9755 FPL__ERROR(FPL__MODULE_MEMORY, "Alignment parameter '%zu' must be a power of two", alignment);
9756 return fpl_null;
9757 }
9758 // Allocate empty memory to hold a size of a pointer + alignment padding + the actual data
9759 size_t newSize = sizeof(void *) + (alignment << 1) + size;
9760 void *basePtr = fplMemoryAllocate(newSize);
9761 // The resulting address starts after the stored base pointer
9762 void *alignedPtr = (void *)((uint8_t *)basePtr + sizeof(void *));
9763 // Move the resulting address to a aligned one when not aligned
9764 uintptr_t mask = alignment - 1;
9765 if ((alignment > 1) && (((uintptr_t)alignedPtr & mask) != 0)) {
9766 uintptr_t offset = ((uintptr_t)alignment - ((uintptr_t)alignedPtr & mask));
9767 alignedPtr = (uint8_t *)alignedPtr + offset;
9768 }
9769 // Write the base pointer before the alignment pointer
9770 *(void **)((void *)((uint8_t *)alignedPtr - sizeof(void *))) = basePtr;
9771 // Ensure alignment
9772 fplAssert(fplIsAligned(alignedPtr, alignment));
9773 return(alignedPtr);
9774}
9775
9776fpl_common_api void fplMemoryAlignedFree(void *ptr) {
9777 FPL__CheckArgumentNullNoRet(ptr);
9778 // Free the base pointer which is stored to the left from the given pointer
9779 void *basePtr = *(void **)((void *)((uint8_t *)ptr - sizeof(void *)));
9780 fplAssert(basePtr != fpl_null);
9781 fplMemoryFree(basePtr);
9782}
9783
9784#define FPL__MEM_SHIFT_64 3
9785#define FPL__MEM_MASK_64 0x00000007
9786#define FPL__MEM_SHIFT_32 2
9787#define FPL__MEM_MASK_32 0x00000003
9788#define FPL__MEM_SHIFT_16 1
9789#define FPL__MEM_MASK_16 0x0000000
9790
9791// Clearing memory in chunks
9792#define FPL__MEMORY_SET(T, memory, size, shift, mask, value) \
9793 do { \
9794 size_t setBytes = 0; \
9795 if (sizeof(T) > sizeof(uint8_t)) { \
9796 T setValue = 0; \
9797 for (int bytesIncrement = 0; bytesIncrement < sizeof(T); ++bytesIncrement) { \
9798 int bitShift = bytesIncrement * 8; \
9799 setValue |= ((T)value << bitShift); \
9800 } \
9801 T *dataBlock = (T *)(memory); \
9802 T *dataBlockEnd = (T *)(memory) + (size >> shift); \
9803 while (dataBlock < dataBlockEnd) { \
9804 *dataBlock++ = setValue; \
9805 setBytes += sizeof(T); \
9806 } \
9807 } \
9808 uint8_t *data8 = (uint8_t *)memory + setBytes; \
9809 uint8_t *data8End = (uint8_t *)memory + size; \
9810 while (data8 < data8End) { \
9811 *data8++ = value; \
9812 } \
9813 } while (0);
9814
9815#define FPL__MEMORY_CLEAR(T, memory, size, shift, mask) \
9816 do { \
9817 size_t clearBytes = 0; \
9818 if (sizeof(T) > sizeof(uint8_t)) { \
9819 T *dataBlock = (T *)(memory); \
9820 T *dataBlockEnd = (T *)(memory) + (size >> shift); \
9821 while (dataBlock < dataBlockEnd) { \
9822 *dataBlock++ = 0; \
9823 clearBytes += sizeof(T); \
9824 } \
9825 } \
9826 uint8_t *data8 = (uint8_t *)memory + clearBytes; \
9827 uint8_t *data8End = (uint8_t *)memory + size; \
9828 while (data8 < data8End) { \
9829 *data8++ = 0; \
9830 } \
9831 } while (0);
9832
9833#define FPL__MEMORY_COPY(T, source, sourceSize, dest, shift, mask) \
9834 do { \
9835 size_t copiedBytes = 0; \
9836 if (sizeof(T) > sizeof(uint8_t)) { \
9837 const T *sourceDataBlock = (const T *)(source); \
9838 const T *sourceDataBlockEnd = (const T *)(source) + (sourceSize >> shift); \
9839 T *destDataBlock = (T *)(dest); \
9840 while (sourceDataBlock < sourceDataBlockEnd) { \
9841 *destDataBlock++ = *sourceDataBlock++; \
9842 copiedBytes += sizeof(T); \
9843 } \
9844 } \
9845 const uint8_t *sourceData8 = (const uint8_t *)source + copiedBytes; \
9846 const uint8_t *sourceData8End = (const uint8_t *)source + sourceSize; \
9847 uint8_t *destData8 = (uint8_t *)dest + copiedBytes; \
9848 while (sourceData8 < sourceData8End) { \
9849 *destData8++ = *sourceData8++; \
9850 } \
9851 } while (0);
9852
9853fpl_common_api void fplMemorySet(void *mem, const uint8_t value, const size_t size) {
9854 FPL__CheckArgumentNullNoRet(mem);
9855 FPL__CheckArgumentZeroNoRet(size);
9856#if defined(FPL__ENABLE_MEMORY_MACROS)
9857 if (size % 8 == 0) {
9858 FPL__MEMORY_SET(uint64_t, mem, size, FPL__MEM_SHIFT_64, FPL__MEM_MASK_64, value);
9859 } else if (size % 4 == 0) {
9860 FPL__MEMORY_SET(uint32_t, mem, size, FPL__MEM_SHIFT_32, FPL__MEM_MASK_32, value);
9861 } else if (size % 2 == 0) {
9862 FPL__MEMORY_SET(uint16_t, mem, size, FPL__MEM_SHIFT_16, FPL__MEM_MASK_16, value);
9863 } else {
9864 FPL__MEMORY_SET(uint8_t, mem, size, 0, 0, value);
9865 }
9866#elif defined(FPL_PLATFORM_WINDOWS)
9867 FillMemory(mem, size, value);
9868#else
9869 memset(mem, value, size);
9870#endif
9871}
9872
9873fpl_common_api void fplMemoryClear(void *mem, const size_t size) {
9874 FPL__CheckArgumentNullNoRet(mem);
9875 FPL__CheckArgumentZeroNoRet(size);
9876#if defined(FPL__ENABLE_MEMORY_MACROS)
9877 if (size % 8 == 0) {
9878 FPL__MEMORY_CLEAR(uint64_t, mem, size, FPL__MEM_SHIFT_64, FPL__MEM_MASK_64);
9879 } else if (size % 4 == 0) {
9880 FPL__MEMORY_CLEAR(uint32_t, mem, size, FPL__MEM_SHIFT_32, FPL__MEM_MASK_32);
9881 } else if (size % 2 == 0) {
9882 FPL__MEMORY_CLEAR(uint16_t, mem, size, FPL__MEM_SHIFT_16, FPL__MEM_MASK_16);
9883 } else {
9884 FPL__MEMORY_CLEAR(uint8_t, mem, size, 0, 0);
9885 }
9886#elif defined(FPL_PLATFORM_WINDOWS)
9887 ZeroMemory(mem, size);
9888#else
9889 memset(mem, 0, size);
9890#endif
9891}
9892
9893fpl_common_api void fplMemoryCopy(const void *sourceMem, const size_t sourceSize, void *targetMem) {
9894 FPL__CheckArgumentNullNoRet(sourceMem);
9895 FPL__CheckArgumentZeroNoRet(sourceSize);
9896 FPL__CheckArgumentNullNoRet(targetMem);
9897#if defined(FPL__ENABLE_MEMORY_MACROS)
9898 if (sourceSize % 8 == 0) {
9899 FPL__MEMORY_COPY(uint64_t, sourceMem, sourceSize, targetMem, FPL__MEM_SHIFT_64, FPL__MEM_MASK_64);
9900 } else if (sourceSize % 4 == 0) {
9901 FPL__MEMORY_COPY(uint32_t, sourceMem, sourceSize, targetMem, FPL__MEM_SHIFT_32, FPL__MEM_MASK_32);
9902 } else if (sourceSize % 2 == 0) {
9903 FPL__MEMORY_COPY(uint16_t, sourceMem, sourceSize, targetMem, FPL__MEM_SHIFT_16, FPL__MEM_MASK_16);
9904 } else {
9905 FPL__MEMORY_COPY(uint8_t, sourceMem, sourceSize, targetMem, 0, 0);
9906 }
9907#elif defined(FPL_PLATFORM_WINDOWS)
9908 CopyMemory(targetMem, sourceMem, sourceSize);
9909#else
9910 memcpy(targetMem, sourceMem, sourceSize);
9911#endif
9912}
9913#endif // FPL__COMMON_MEMORY_DEFINED
9914
9915//
9916// Common Hardware
9917//
9918// https://github.com/google/cpu_features
9919
9920//
9921// X86/X64 only (CPUID, XCR0, RDTSC)
9922//
9923#if defined(FPL_ARCH_X64) || defined(FPL_ARCH_X86)
9924
9925# define FPL__CPU_BRAND_BUFFER_SIZE 0x40
9926
9927# if defined(FPL_COMPILER_MSVC)
9928
9929 // CPUID/XCR0 for MSVC
9930# if _MSC_VER >= 1400
9931# define fpl__m_CPUID(outLeaf, functionId) __cpuid((int *)(outLeaf)->raw, (int)(functionId))
9932# endif
9933# if _MSC_VER >= 1600
9934# define fpl__m_GetXCR0() ((uint64_t)_xgetbv(0))
9935# endif
9936
9937 // RDTSC for MSVC
9938# define fpl__m_RDTSC() ((uint64_t)__rdtsc())
9939
9940# elif defined(FPL_COMPILER_GCC) ||defined(FPL_COMPILER_CLANG)
9941
9942 // CPUID for GCC/CLANG
9943fpl_internal void fpl__m_CPUID(fplCPUIDLeaf *outLeaf, const uint32_t functionId) {
9944 int eax = 0, ebx = 0, ecx = 0, edx = 0;
9945 __cpuid_count(functionId, 0, eax, ebx, ecx, edx);
9946 outLeaf->eax = eax;
9947 outLeaf->ebx = ebx;
9948 outLeaf->ecx = ecx;
9949 outLeaf->edx = edx;
9950}
9951
9952 // XCR0 for GCC/CLANG
9953fpl_internal uint64_t fpl__m_GetXCR0(void) {
9954 uint32_t eax, edx;
9955 __asm(".byte 0x0F, 0x01, 0xd0" : "=a"(eax), "=d"(edx) : "c"(0));
9956 return eax;
9957}
9958
9959 // RDTSC for non-MSVC
9960# if defined(FPL_ARCH_X86)
9961fpl_force_inline uint64_t fpl__m_RDTSC(void) {
9962 unsigned long long int result;
9963 __asm__ volatile (".byte 0x0f, 0x31" : "=A" (result));
9964 return((uint64_t)result);
9965}
9966# elif defined(FPL_ARCH_X64)
9967fpl_force_inline uint64_t fpl__m_RDTSC(void) {
9968 unsigned hi, lo;
9969 __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
9970 uint64_t result = (uint64_t)(((unsigned long long)lo) | (((unsigned long long)hi) << 32));
9971 return (result);
9972}
9973# endif
9974# endif
9975
9976fpl_common_api void fplCPUID(fplCPUIDLeaf *outLeaf, const uint32_t functionId) {
9977#if defined(fpl__m_CPUID)
9978 fpl__m_CPUID(outLeaf, functionId);
9979#endif
9980}
9981
9982fpl_common_api uint64_t fplCPUXCR0() {
9983#if defined(fpl__m_GetXCR0)
9984 uint64_t result = fpl__m_GetXCR0();
9985 return(result);
9986#else
9987 return(0ULL);
9988#endif
9989}
9990
9991fpl_common_api uint64_t fplCPURDTSC() {
9992#if defined(fpl__m_RDTSC)
9993 uint64_t result = fpl__m_RDTSC();
9994 return(result);
9995#else
9996 return(0ULL);
9997#endif
9998}
9999
10001 fplClearStruct(outCaps);
10002
10003 fplCPUIDLeaf info0 = fplZeroInit;
10004 fplCPUIDLeaf info1 = fplZeroInit;
10005 fplCPUIDLeaf info7 = fplZeroInit;
10006
10007 fplCPUID(&info0, 0);
10008 uint32_t maxFunctionId = info0.eax;
10009
10010 if (1 <= maxFunctionId) {
10011 fplCPUID(&info1, 1);
10012 }
10013
10014 if (7 <= maxFunctionId) {
10015 fplCPUID(&info7, 7);
10016 }
10017
10018 bool hasXSave = fplIsBitSet(info1.ecx, 26) && fplIsBitSet(info1.ecx, 27);
10019 uint64_t xcr0 = hasXSave ? fplCPUXCR0() : 0;
10020
10021 const uint32_t MASK_XMM = 0x2;
10022 const uint32_t MASK_YMM = 0x4;
10023 const uint32_t MASK_MASKREG = 0x20;
10024 const uint32_t MASK_ZMM0_15 = 0x40;
10025 const uint32_t MASK_ZMM16_31 = 0x80;
10026
10027 const uint32_t MASK_SSE = MASK_XMM;
10028 const uint32_t MASK_AVX = MASK_XMM | MASK_YMM;
10029 const uint32_t MASK_AVX_512 = MASK_XMM | MASK_YMM | MASK_MASKREG | MASK_ZMM0_15 | MASK_ZMM16_31;
10030
10031 bool hasSSESupport = (xcr0 & MASK_SSE) == MASK_SSE;
10032 bool hasAVXSupport = (xcr0 & MASK_AVX) == MASK_AVX;
10033 bool hasAVX512Support = (xcr0 & MASK_AVX_512) == MASK_AVX_512;
10034
10035 outCaps->hasMMX = fplIsBitSet(info1.edx, 23);
10036
10037 if (hasSSESupport) {
10038 outCaps->hasSSE = fplIsBitSet(info1.edx, 25);
10039 outCaps->hasSSE2 = fplIsBitSet(info1.edx, 26);
10040 outCaps->hasSSE3 = fplIsBitSet(info1.ecx, 0);
10041 outCaps->hasSSSE3 = fplIsBitSet(info1.ecx, 9);
10042 outCaps->hasSSE4_1 = fplIsBitSet(info1.ecx, 19);
10043 outCaps->hasSSE4_2 = fplIsBitSet(info1.ecx, 20);
10044 }
10045
10046 if (hasAVXSupport) {
10047 outCaps->hasAVX = fplIsBitSet(info1.ecx, 28);
10048 outCaps->hasAVX2 = fplIsBitSet(info7.ebx, 5);
10049 }
10050
10051 if (hasAVX512Support) {
10052 outCaps->hasAVX512 = fplIsBitSet(info7.ebx, 16);
10053 outCaps->hasFMA3 = fplIsBitSet(info7.ecx, 12);
10054 }
10055
10056 return(true);
10057}
10058
10059fpl_common_api size_t fplCPUGetName(char *destBuffer, const size_t maxDestBufferLen) {
10060 fplCPUIDLeaf cpuInfo = fplZeroInit;
10061 fplCPUID(&cpuInfo, 0x80000000);
10062 uint32_t extendedIds = cpuInfo.eax;
10063
10064 // Get the information associated with each extended ID. Interpret CPU brand string.
10065 char cpuBrandBuffer[FPL__CPU_BRAND_BUFFER_SIZE] = fplZeroInit;
10066 uint32_t max = fplMin(extendedIds, 0x80000004);
10067 for (uint32_t i = 0x80000002; i <= max; ++i) {
10068 fplCPUID(&cpuInfo, i);
10069 uint32_t offset = (i - 0x80000002) << 4;
10070 fplMemoryCopy(cpuInfo.raw, sizeof(cpuInfo), cpuBrandBuffer + offset);
10071 }
10072
10073 size_t result = fplGetStringLength(cpuBrandBuffer);
10074 if (destBuffer != fpl_null) {
10075 // Copy result back to the dest buffer
10076 size_t requiredDestBufferLen = result + 1;
10077 FPL__CheckArgumentMin(maxDestBufferLen, requiredDestBufferLen, 0);
10078 fplCopyStringLen(cpuBrandBuffer, result, destBuffer, maxDestBufferLen);
10079 }
10080
10081 return(result);
10082}
10083#else
10084
10085fpl_common_api uint64_t fplCPURDTSC() {
10086 // Based on: https://github.com/google/benchmark/blob/v1.1.0/src/cycleclock.h
10087#if defined(FPL_ARCH_ARM64)
10088 int64_t virtual_timer_value;
10089 fplAsm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
10090 return (uint64_t)virtual_timer_value;
10091#elif defined(FPL_ARCH_ARM32) && (__ARM_ARCH >= 6)
10092 uint32_t pmccntr;
10093 uint32_t pmuseren;
10094 uint32_t pmcntenset;
10095 // Read the user mode perf monitor counter access permissions.
10096 fplAsm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
10097 if (pmuseren & 1) {
10098 // Allows reading perfmon counters for user mode code.
10099 fplAsm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
10100 if (pmcntenset & 0x80000000ul) {
10101 // Is it counting?
10102 fplAsm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
10103 // The counter is set up to count every 64th cycle
10104 return (uint64_t)pmccntr * 64ULL; // Should optimize to << 6
10105 }
10106 }
10107#endif
10108 struct timeval tv;
10109 gettimeofday(&tv, fpl_null);
10110 return (uint64_t)tv.tv_sec * 1000000ULL + (uint64_t)tv.tv_usec;
10111}
10112
10113fpl_common_api uint64_t fplCPUXCR0() {
10114 // Not supported on non-x86 platforms
10115 return(0);
10116}
10117
10118fpl_common_api void fplCPUID(fplCPUIDLeaf *outLeaf, const uint32_t functionId) {
10119 // Not supported on non-x86 platforms
10120 fplClearStruct(outLeaf);
10121}
10122
10124 // @IMPLEMENT(final): fplCPUGetCapabilities for non-x86 architectures
10125 return(false);
10126}
10127
10128fpl_common_api size_t fplCPUGetName(char *destBuffer, const size_t maxDestBufferLen) {
10129 // @IMPLEMENT(final): fplCPUGetName for non-x86 architectures
10130 return(0);
10131}
10132#endif
10133
10134//
10135// Common Atomics
10136//
10137#if !defined(FPL__COMMON_ATOMICS_DEFINED)
10138#define FPL__COMMON_ATOMICS_DEFINED
10139
10140fpl_common_api size_t fplAtomicFetchAndAddSize(volatile size_t *dest, const size_t addend) {
10141 fplAssert(dest != fpl_null);
10142#if defined(FPL_CPU_64BIT)
10143 size_t result = (size_t)fplAtomicFetchAndAddU64((volatile uint64_t *)dest, (uint64_t)addend);
10144#elif defined(FPL_CPU_32BIT)
10145 size_t result = (size_t)fplAtomicFetchAndAddU32((volatile uint32_t *)dest, (uint32_t)addend);
10146#else
10147# error "Unsupported architecture/platform!"
10148#endif // FPL_ARCH
10149 return (result);
10150}
10151
10152fpl_common_api void *fplAtomicFetchAndAddPtr(volatile void **dest, const intptr_t addend) {
10153 fplAssert(dest != fpl_null);
10154#if defined(FPL_CPU_64BIT)
10155 void *result = (void *)fplAtomicFetchAndAddS64((volatile int64_t *)dest, (int64_t)addend);
10156#elif defined(FPL_CPU_32BIT)
10157 void *result = (void *)fplAtomicFetchAndAddS32((volatile int32_t *)dest, (int32_t)addend);
10158#else
10159# error "Unsupported architecture/platform!"
10160#endif // FPL_ARCH
10161 return (result);
10162}
10163
10164fpl_common_api size_t fplAtomicAddAndFetchSize(volatile size_t *value, const size_t addend) {
10165 fplAssert(value != fpl_null);
10166#if defined(FPL_CPU_64BIT)
10167 size_t result = (size_t)fplAtomicAddAndFetchU64((volatile uint64_t *)value, (uint64_t)addend);
10168#elif defined(FPL_CPU_32BIT)
10169 size_t result = (size_t)fplAtomicAddAndFetchU32((volatile uint32_t *)value, (uint32_t)addend);
10170#else
10171# error "Unsupported architecture/platform!"
10172#endif // FPL_ARCH
10173 return (result);
10174}
10175
10176fpl_common_api void *fplAtomicAddAndFetchPtr(volatile void **value, const intptr_t addend) {
10177 fplAssert(value != fpl_null);
10178#if defined(FPL_CPU_64BIT)
10179 void *result = (void *)fplAtomicAddAndFetchU64((volatile uint64_t *)value, (uint64_t)addend);
10180#elif defined(FPL_CPU_32BIT)
10181 void *result = (void *)fplAtomicAddAndFetchU32((volatile uint32_t *)value, (uint32_t)addend);
10182#else
10183# error "Unsupported architecture/platform!"
10184#endif // FPL_ARCH
10185 return (result);
10186}
10187
10188fpl_common_api size_t fplAtomicIncrementSize(volatile size_t *value) {
10189 fplAssert(value != fpl_null);
10190#if defined(FPL_CPU_64BIT)
10191 size_t result = (size_t)fplAtomicIncrementU64((volatile uint64_t *)value);
10192#elif defined(FPL_CPU_32BIT)
10193 size_t result = (size_t)fplAtomicIncrementU32((volatile uint32_t *)value);
10194#else
10195# error "Unsupported architecture/platform!"
10196#endif // FPL_ARCH
10197 return (result);
10198}
10199
10200fpl_common_api void *fplAtomicIncrementPtr(volatile void **value) {
10201 fplAssert(value != fpl_null);
10202#if defined(FPL_CPU_64BIT)
10203 void *result = (void *)fplAtomicAddAndFetchU64((volatile uint64_t *)value, 8);
10204#elif defined(FPL_CPU_32BIT)
10205 void *result = (void *)fplAtomicAddAndFetchU32((volatile uint32_t *)value, 4);
10206#else
10207# error "Unsupported architecture/platform!"
10208#endif // FPL_ARCH
10209 return (result);
10210}
10211
10212fpl_common_api size_t fplAtomicExchangeSize(volatile size_t *target, const size_t value) {
10213 fplAssert(target != fpl_null);
10214#if defined(FPL_CPU_64BIT)
10215 size_t result = (size_t)fplAtomicExchangeU64((volatile uint64_t *)target, (uint64_t)value);
10216#elif defined(FPL_CPU_32BIT)
10217 size_t result = (size_t)fplAtomicExchangeU32((volatile uint32_t *)target, (uint32_t)value);
10218#else
10219# error "Unsupported architecture/platform!"
10220#endif // FPL_ARCH
10221 return (result);
10222}
10223fpl_common_api void *fplAtomicExchangePtr(volatile void **target, const void *value) {
10224 fplAssert(target != fpl_null);
10225#if defined(FPL_CPU_64BIT)
10226 void *result = (void *)fplAtomicExchangeU64((volatile uint64_t *)target, (uint64_t)value);
10227#elif defined(FPL_CPU_32BIT)
10228 void *result = (void *)fplAtomicExchangeU32((volatile uint32_t *)target, (uint32_t)value);
10229#else
10230# error "Unsupported architecture/platform!"
10231#endif // FPL_ARCH
10232 return (result);
10233}
10234
10235fpl_common_api size_t fplAtomicCompareAndSwapSize(volatile size_t *dest, const size_t comparand, const size_t exchange) {
10236 fplAssert(dest != fpl_null);
10237#if defined(FPL_CPU_64BIT)
10238 size_t result = (size_t)fplAtomicCompareAndSwapU64((volatile uint64_t *)dest, (uint64_t)comparand, (uint64_t)exchange);
10239#elif defined(FPL_CPU_32BIT)
10240 size_t result = (size_t)fplAtomicCompareAndSwapU32((volatile uint32_t *)dest, (uint32_t)comparand, (uint32_t)exchange);
10241#else
10242# error "Unsupported architecture/platform!"
10243#endif // FPL_ARCH
10244 return (result);
10245}
10246fpl_common_api void *fplAtomicCompareAndSwapPtr(volatile void **dest, const void *comparand, const void *exchange) {
10247 fplAssert(dest != fpl_null);
10248#if defined(FPL_CPU_64BIT)
10249 void *result = (void *)fplAtomicCompareAndSwapU64((volatile uint64_t *)dest, (uint64_t)comparand, (uint64_t)exchange);
10250#elif defined(FPL_CPU_32BIT)
10251 void *result = (void *)fplAtomicCompareAndSwapU32((volatile uint32_t *)dest, (uint32_t)comparand, (uint32_t)exchange);
10252#else
10253# error "Unsupported architecture/platform!"
10254#endif // FPL_ARCH
10255 return (result);
10256}
10257
10258fpl_common_api bool fplAtomicIsCompareAndSwapSize(volatile size_t *dest, const size_t comparand, const size_t exchange) {
10259 fplAssert(dest != fpl_null);
10260#if defined(FPL_CPU_64BIT)
10261 bool result = fplAtomicIsCompareAndSwapU64((volatile uint64_t *)dest, (uint64_t)comparand, (uint64_t)exchange);
10262#elif defined(FPL_CPU_32BIT)
10263 bool result = fplAtomicIsCompareAndSwapU32((volatile uint32_t *)dest, (uint32_t)comparand, (uint32_t)exchange);
10264#else
10265# error "Unsupported architecture/platform!"
10266#endif // FPL_ARCH
10267 return (result);
10268}
10269fpl_common_api bool fplAtomicIsCompareAndSwapPtr(volatile void **dest, const void *comparand, const void *exchange) {
10270 fplAssert(dest != fpl_null);
10271#if defined(FPL_CPU_64BIT)
10272 bool result = fplAtomicIsCompareAndSwapU64((volatile uint64_t *)dest, (uint64_t)comparand, (uint64_t)exchange);
10273#elif defined(FPL_CPU_32BIT)
10274 bool result = fplAtomicIsCompareAndSwapU32((volatile uint32_t *)dest, (uint32_t)comparand, (uint32_t)exchange);
10275#else
10276# error "Unsupported architecture/platform!"
10277#endif // FPL_ARCH
10278 return (result);
10279}
10280
10281fpl_common_api size_t fplAtomicLoadSize(volatile size_t *source) {
10282#if defined(FPL_CPU_64BIT)
10283 size_t result = (size_t)fplAtomicLoadU64((volatile uint64_t *)source);
10284#elif defined(FPL_CPU_32BIT)
10285 size_t result = (size_t)fplAtomicLoadU32((volatile uint32_t *)source);
10286#else
10287# error "Unsupported architecture/platform!"
10288#endif // FPL_ARCH
10289 return(result);
10290}
10291fpl_common_api void fplAtomicStoreSize(volatile size_t *dest, const size_t value) {
10292#if defined(FPL_CPU_64BIT)
10293 fplAtomicStoreU64((volatile uint64_t *)dest, (uint64_t)value);
10294#elif defined(FPL_CPU_32BIT)
10295 fplAtomicStoreU32((volatile uint32_t *)dest, (uint32_t)value);
10296#else
10297# error "Unsupported architecture/platform!"
10298#endif // FPL_ARCH
10299}
10300
10301fpl_common_api void *fplAtomicLoadPtr(volatile void **source) {
10302#if defined(FPL_CPU_64BIT)
10303 void *result = (void *)fplAtomicLoadU64((volatile uint64_t *)source);
10304#elif defined(FPL_CPU_32BIT)
10305 void *result = (void *)fplAtomicLoadU32((volatile uint32_t *)source);
10306#else
10307# error "Unsupported architecture/platform!"
10308#endif // FPL_ARCH
10309 return(result);
10310}
10311fpl_common_api void fplAtomicStorePtr(volatile void **dest, const void *value) {
10312#if defined(FPL_CPU_64BIT)
10313 fplAtomicStoreU64((volatile uint64_t *)dest, (uint64_t)value);
10314#elif defined(FPL_CPU_32BIT)
10315 fplAtomicStoreU32((volatile uint32_t *)dest, (uint32_t)value);
10316#else
10317# error "Unsupported architecture/platform!"
10318#endif // FPL_ARCH
10319}
10320
10321#endif // FPL__COMMON_ATOMICS_DEFINED
10322
10323//
10324// Common Threading
10325//
10327 if (thread == fpl_null) {
10329 }
10330 fplThreadState result = (fplThreadState)fplAtomicLoadU32((volatile uint32_t *)&thread->currentState);
10331 return(result);
10332}
10333
10335 const fplThreadHandle *result = &fpl__global__ThreadState.mainThread;
10336 return(result);
10337}
10338
10340 size_t result = 0;
10341 for (size_t threadIndex = 0; threadIndex < FPL_MAX_THREAD_COUNT; ++threadIndex) {
10342 fplThreadState state = (fplThreadState)fplAtomicLoadU32((volatile uint32_t *)&fpl__global__ThreadState.threads[threadIndex].currentState);
10343 if (state == fplThreadState_Stopped) {
10344 ++result;
10345 }
10346 }
10347 return(result);
10348}
10349
10351 size_t result = 0;
10352 for (size_t threadIndex = 0; threadIndex < FPL_MAX_THREAD_COUNT; ++threadIndex) {
10353 fplThreadState state = (fplThreadState)fplAtomicLoadU32((volatile uint32_t *)&fpl__global__ThreadState.threads[threadIndex].currentState);
10354 if (state != fplThreadState_Stopped) {
10355 ++result;
10356 }
10357 }
10358 return(result);
10359}
10360
10361//
10362// Common Files
10363//
10364#if !defined(FPL__COMMON_FILES_DEFINED)
10365#define FPL__COMMON_FILES_DEFINED
10366
10367fpl_common_api size_t fplFileReadBlock(const fplFileHandle *fileHandle, const size_t sizeToRead, void *targetBuffer, const size_t maxTargetBufferSize) {
10368#if defined(FPL_CPU_64BIT)
10369 return fplFileReadBlock64(fileHandle, sizeToRead, targetBuffer, maxTargetBufferSize);
10370#else
10371 return fplFileReadBlock32(fileHandle, (uint32_t)sizeToRead, targetBuffer, (uint32_t)maxTargetBufferSize);
10372#endif
10373}
10374
10375fpl_common_api size_t fplFileWriteBlock(const fplFileHandle *fileHandle, void *sourceBuffer, const size_t sourceSize) {
10376#if defined(FPL_CPU_64BIT)
10377 return fplFileWriteBlock64(fileHandle, sourceBuffer, sourceSize);
10378#else
10379 return fplFileWriteBlock32(fileHandle, sourceBuffer, (uint32_t)sourceSize);
10380#endif
10381}
10382
10383fpl_common_api size_t fplFileSetPosition(const fplFileHandle *fileHandle, const intptr_t position, const fplFilePositionMode mode) {
10384#if defined(FPL_CPU_64BIT)
10385 return fplFileSetPosition64(fileHandle, position, mode);
10386#else
10387 return fplFileSetPosition32(fileHandle, (int32_t)position, mode);
10388#endif
10389}
10390
10391fpl_common_api size_t fplFileGetPosition(const fplFileHandle *fileHandle) {
10392#if defined(FPL_CPU_64BIT)
10393 return fplFileGetPosition64(fileHandle);
10394#else
10395 return fplFileGetPosition32(fileHandle);
10396#endif
10397}
10398
10399fpl_common_api size_t fplFileGetSizeFromPath(const char *filePath) {
10400#if defined(FPL_CPU_64BIT)
10401 return fplFileGetSizeFromPath64(filePath);
10402#else
10403 return fplFileGetSizeFromPath32(filePath);
10404#endif
10405}
10406
10407fpl_common_api size_t fplFileGetSizeFromHandle(const fplFileHandle *fileHandle) {
10408#if defined(FPL_CPU_64BIT)
10409 return fplFileGetSizeFromHandle64(fileHandle);
10410#else
10411 return fplFileGetSizeFromHandle32(fileHandle);
10412#endif
10413}
10414
10415#endif // FPL__COMMON_FILES_DEFINED
10416
10417//
10418// Common Paths
10419//
10420#if !defined(FPL__COMMON_PATHS_DEFINED)
10421#define FPL__COMMON_PATHS_DEFINED
10422
10423fpl_common_api size_t fplExtractFilePath(const char *sourcePath, char *destPath, const size_t maxDestLen) {
10424 FPL__CheckArgumentNull(sourcePath, 0);
10425 size_t sourceLen = fplGetStringLength(sourcePath);
10426 size_t result = 0;
10427 if (sourceLen > 0) {
10428 int pathLen = 0;
10429 const char *chPtr = (const char *)sourcePath;
10430 while (*chPtr) {
10431 if (*chPtr == FPL_PATH_SEPARATOR) {
10432 pathLen = (int)(chPtr - sourcePath);
10433 }
10434 ++chPtr;
10435 }
10436 result = pathLen;
10437 if (destPath != fpl_null) {
10438 size_t requiredDestLen = pathLen + 1;
10439 FPL__CheckArgumentMin(maxDestLen, requiredDestLen, 0);
10440 fplCopyStringLen(sourcePath, pathLen, destPath, maxDestLen);
10441 }
10442 }
10443 return(result);
10444}
10445
10446fpl_common_api const char *fplExtractFileExtension(const char *sourcePath) {
10447 const char *result = fpl_null;
10448 if (sourcePath != fpl_null) {
10449 const char *chPtr = sourcePath;
10450 // Find last separator first
10451 const char *lastPathSep = fpl_null;
10452 while (*chPtr) {
10453 if (*chPtr == FPL_PATH_SEPARATOR) {
10454 lastPathSep = chPtr;
10455 }
10456 ++chPtr;
10457 }
10458 // Start either by the last found separator or from the very start
10459 if (lastPathSep != fpl_null) {
10460 chPtr = lastPathSep;
10461 } else {
10462 chPtr = sourcePath;
10463 }
10464 const char *lastExt = fpl_null;
10465 while (*chPtr) {
10466 if (*chPtr == FPL_FILE_EXT_SEPARATOR) {
10467 lastExt = chPtr;
10468 }
10469 ++chPtr;
10470 }
10471 if (lastExt != fpl_null) {
10472 result = lastExt;
10473 }
10474 }
10475 return(result);
10476}
10477
10478fpl_common_api const char *fplExtractFileName(const char *sourcePath) {
10479 const char *result = fpl_null;
10480 if (sourcePath) {
10481 result = sourcePath;
10482 const char *chPtr = sourcePath;
10483 while (*chPtr) {
10484 if (*chPtr == FPL_PATH_SEPARATOR) {
10485 result = chPtr + 1;
10486 }
10487 ++chPtr;
10488 }
10489 }
10490 return(result);
10491}
10492
10493fpl_common_api size_t fplChangeFileExtension(const char *filePath, const char *newFileExtension, char *destPath, const size_t maxDestLen) {
10494 FPL__CheckArgumentNull(filePath, 0);
10495 FPL__CheckArgumentNull(newFileExtension, 0);
10496
10497 size_t filePathLen = fplGetStringLength(filePath);
10498 FPL__CheckArgumentZero(filePathLen, 0);
10499
10500 size_t extLen = fplGetStringLength(newFileExtension);
10501
10502 size_t result = 0;
10503 if (filePath != fpl_null) {
10504 // Find last path
10505 const char *chPtr = (const char *)filePath;
10506 const char *lastPathSeparatorPtr = fpl_null;
10507 while (*chPtr) {
10508 if (*chPtr == FPL_PATH_SEPARATOR) {
10509 lastPathSeparatorPtr = chPtr;
10510 }
10511 ++chPtr;
10512 }
10513 // Find last ext separator
10514 if (lastPathSeparatorPtr != fpl_null) {
10515 chPtr = lastPathSeparatorPtr + 1;
10516 } else {
10517 chPtr = (const char *)filePath;
10518 }
10519 const char *lastExtSeparatorPtr = fpl_null;
10520 while (*chPtr) {
10521 if (*chPtr == FPL_FILE_EXT_SEPARATOR) {
10522 lastExtSeparatorPtr = chPtr;
10523 }
10524 ++chPtr;
10525 }
10526
10527 size_t filenameLen;
10528 if (lastExtSeparatorPtr != fpl_null) {
10529 filenameLen = (size_t)((uintptr_t)lastExtSeparatorPtr - (uintptr_t)filePath);
10530 } else {
10531 filenameLen = filePathLen;
10532 }
10533
10534 result = filenameLen + extLen;
10535
10536 // Copy parts
10537 if (destPath != fpl_null) {
10538 size_t requiredDestLen = result + 1;
10539 FPL__CheckArgumentMin(maxDestLen, requiredDestLen, 0);
10540
10541 fplCopyStringLen(filePath, filenameLen, destPath, maxDestLen);
10542 char *destExtPtr = destPath + filenameLen;
10543 fplCopyStringLen(newFileExtension, extLen, destExtPtr, maxDestLen - filenameLen);
10544 }
10545 }
10546 return(result);
10547}
10548
10549fpl_common_api size_t fplPathCombine(char *destPath, const size_t maxDestPathLen, const size_t pathCount, ...) {
10550 FPL__CheckArgumentZero(pathCount, 0);
10551
10552 size_t result = 0;
10553
10554 size_t curDestPosition = 0;
10555 char *currentDestPtr = destPath;
10556
10557 va_list vargs;
10558 va_start(vargs, pathCount);
10559 for (size_t pathIndex = 0; pathIndex < pathCount; ++pathIndex) {
10560 const char *path = va_arg(vargs, const char *);
10561 size_t pathLen = fplGetStringLength(path);
10562
10563 bool requireSeparator = pathIndex < (pathCount - 1);
10564 size_t requiredPathLen = requireSeparator ? pathLen + 1 : pathLen;
10565
10566 result += requiredPathLen;
10567
10568 if (destPath != fpl_null) {
10569 size_t requiredDestLen = result + 1;
10570 FPL__CheckArgumentMin(maxDestPathLen, requiredDestLen, 0);
10571
10572 fplCopyStringLen(path, pathLen, currentDestPtr, maxDestPathLen - curDestPosition);
10573 currentDestPtr += pathLen;
10574 if (requireSeparator) {
10575 *currentDestPtr++ = FPL_PATH_SEPARATOR;
10576 }
10577 curDestPosition += requiredPathLen;
10578 }
10579 }
10580 if (currentDestPtr != fpl_null) {
10581 *currentDestPtr = 0;
10582 }
10583 va_end(vargs);
10584 return(result);
10585}
10586#endif // FPL__COMMON_PATHS_DEFINED
10587
10588//
10589// Common Window
10590//
10591#if !defined(FPL__COMMON_WINDOW_DEFINED)
10592#define FPL__COMMON_WINDOW_DEFINED
10593fpl_common_api char *fplGetWindowTitle(char *outTitle, const size_t maxOutTitleLength) {
10594 FPL__CheckPlatform(fpl_null);
10595 fpl__PlatformAppState *appState = fpl__global__AppState;
10596 char *result = fplCopyString(appState->currentSettings.window.title, outTitle, maxOutTitleLength);
10597 return(result);
10598}
10599
10600fpl_common_api void fplSetWindowInputEvents(const bool enabled) {
10601 FPL__CheckPlatformNoRet();
10602 fpl__PlatformAppState *appState = fpl__global__AppState;
10603 appState->currentSettings.input.disabledEvents = !enabled;
10604}
10605#endif // FPL__COMMON_WINDOW_DEFINED
10606
10607//
10608// Common Logging
10609//
10610#if defined(FPL__ENABLE_LOGGING)
10612 FPL__CheckArgumentNullNoRet(params);
10613 fpl__global__LogSettings = *params;
10614 fpl__global__LogSettings.isInitialized = true;
10615}
10617 return &fpl__global__LogSettings;
10618}
10619fpl_common_api void fplSetMaxLogLevel(const fplLogLevel maxLevel) {
10620 fpl__global__LogSettings.maxLevel = maxLevel;
10621}
10623 return fpl__global__LogSettings.maxLevel;
10624}
10625#endif
10626
10627fpl_common_api const char *fplGetLastError() {
10628 const char *result = "";
10629 const fpl__ErrorState *errorState = &fpl__global__LastErrorState;
10630 if (errorState->count > 0) {
10631 size_t index = errorState->count - 1;
10632 result = fplGetErrorByIndex(index);
10633 }
10634 return (result);
10635}
10636
10637fpl_common_api const char *fplGetErrorByIndex(const size_t index) {
10638 const char *result = "";
10639 const fpl__ErrorState *errorState = &fpl__global__LastErrorState;
10640 if (index < errorState->count) {
10641 result = errorState->errors[index];
10642 } else {
10643 result = errorState->errors[errorState->count - 1];
10644 }
10645 return (result);
10646}
10647
10649 size_t result = 0;
10650 const fpl__ErrorState *errorState = &fpl__global__LastErrorState;
10651 result = errorState->count;
10652 return (result);
10653}
10654
10656 fpl__ErrorState *errorState = &fpl__global__LastErrorState;
10657 fplClearStruct(errorState);
10658}
10659
10661 FPL__CheckPlatform(fpl_null);
10662 const fpl__PlatformAppState *appState = fpl__global__AppState;
10663 return &appState->currentSettings;
10664}
10665
10667 FPL__CheckArgumentNullNoRet(video);
10668 fplClearStruct(video);
10669 video->isVSync = false;
10670 video->isAutoSize = true;
10671
10672#if defined(FPL__ENABLE_VIDEO_OPENGL)
10675#endif
10676
10677#if defined(FPL__ENABLE_VIDEO_VULKAN)
10679 video->graphics.vulkan.appVersion = fplStructInit(fplVersionInfo, "1.0.0", "1", "0", "0");
10680 video->graphics.vulkan.engineVersion = fplStructInit(fplVersionInfo, "1.0.0", "1", "0", "0");
10681 video->graphics.vulkan.apiVersion = fplStructInit(fplVersionInfo, "1.1.0", "1", "1", "0");
10683#endif
10684
10685 // @NOTE(final): Auto detect video backend
10686#if defined(FPL__ENABLE_VIDEO_OPENGL)
10688#elif defined(FPL__ENABLE_VIDEO_SOFTWARE)
10690#elif defined(FPL__ENABLE_VIDEO_VULKAN)
10692#else
10694#endif
10695}
10696
10697fpl_common_api void fplSetDefaultAudioTargetFormat(fplAudioTargetFormat *targetFormat) {
10698 FPL__CheckArgumentNullNoRet(targetFormat);
10699 fplClearStruct(targetFormat);
10700
10701#if defined(FPL__ENABLE_AUDIO)
10702 fplAudioTargetFormat emptyFormat = fplZeroInit;
10703 fplAudioDeviceFormat deviceFormat = fplZeroInit;
10704 fplConvertAudioTargetFormatToDeviceFormat(&emptyFormat, &deviceFormat);
10705
10706 targetFormat->preferExclusiveMode = deviceFormat.preferExclusiveMode;
10707 targetFormat->channels = deviceFormat.channels;
10708 targetFormat->sampleRate = deviceFormat.sampleRate;
10709 targetFormat->periods = deviceFormat.periods;
10710 targetFormat->type = deviceFormat.type;
10711 targetFormat->bufferSizeInFrames = deviceFormat.bufferSizeInFrames;
10712#endif // FPL__ENABLE_AUDIO
10713}
10714
10716 FPL__CheckArgumentNullNoRet(audio);
10717 fplClearStruct(audio);
10718 fplSetDefaultAudioTargetFormat(&audio->targetFormat);
10719
10721# if defined(FPL_PLATFORM_WINDOWS) && defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
10723# endif
10724# if defined(FPL_PLATFORM_LINUX) && defined(FPL__ENABLE_AUDIO_ALSA)
10726# endif
10727
10728 audio->startAuto = true;
10729 audio->stopAuto = true;
10730}
10731
10733 FPL__CheckArgumentNullNoRet(window);
10734 fplClearStruct(window);
10735 window->title[0] = 0;
10736 window->windowSize.width = 0;
10737 window->windowSize.height = 0;
10738 window->fullscreenSize.width = 0;
10739 window->fullscreenSize.height = 0;
10740 window->isFullscreen = false;
10741 window->isResizable = true;
10742 window->isDecorated = true;
10743 window->isFloating = false;
10744 window->isScreenSaverPrevented = false;
10745 window->isMonitorPowerPrevented = false;
10746}
10747
10749 FPL__CheckArgumentNullNoRet(console);
10750 fplClearStruct(console);
10751 console->title[0] = 0;
10752}
10753
10755 FPL__CheckArgumentNullNoRet(input);
10756 fplClearStruct(input);
10757 input->controllerDetectionFrequency = 100;
10758}
10759
10761 FPL__CheckArgumentNullNoRet(settings);
10762 fplClearStruct(settings);
10767}
10768
10770 fplSettings result;
10771 fplSetDefaultSettings(&result);
10772 return(result);
10773}
10774
10775#define FPL__PLATFORMRESULTTYPE_COUNT FPL__ENUM_COUNT(fplPlatformResultType_First, fplPlatformResultType_Last)
10776fpl_globalvar const char *fpl__global_platformResultTypeNameTable[] = {
10777 "Failed Window", // fplPlatformResultType_FailedWindow (-6)
10778 "Failed Audio", // fplPlatformResultType_FailedAudio (-5)
10779 "Failed Video", // fplPlatformResultType_FailedVideo (-4)
10780 "Failed Platform", // fplPlatformResultType_FailedPlatform (-3)
10781 "Failed Allocating Memory", // fplPlatformResultType_FailedAllocatingMemory (-2)
10782 "Already Initialized", // fplPlatformResultType_AlreadyInitialized (-1)
10783 "Not Initialized", // fplPlatformResultType_NotInitialized (0)
10784 "Success", // fplPlatformResultType_Success (1)
10785};
10786fplStaticAssert(fplArrayCount(fpl__global_platformResultTypeNameTable) == FPL__PLATFORMRESULTTYPE_COUNT);
10787
10789 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(type, fplPlatformResultType_First, fplPlatformResultType_Last);
10790 const char *result = fpl__global_platformResultTypeNameTable[index];
10791 return(result);
10792}
10793
10794#define FPL__ARCHTYPE_COUNT FPL__ENUM_COUNT(fplCPUArchType_First, fplCPUArchType_Last)
10795fpl_globalvar const char *fpl__global_ArchTypeNameTable[] = {
10796 "Unknown", // Unknown architecture
10797 "x86", // X86 architecture
10798 "x86_64", // X86 with 64-bit architecture
10799 "x64", // X64 only architecture
10800 "arm32", // ARM32 architecture
10801 "arm64", // ARM64 architecture
10802};
10803fplStaticAssert(fplArrayCount(fpl__global_ArchTypeNameTable) == FPL__ARCHTYPE_COUNT);
10804
10805fpl_common_api const char *fplCPUGetArchName(const fplCPUArchType type) {
10806 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(type, fplCPUArchType_First, fplCPUArchType_Last);
10807 const char *result = fpl__global_ArchTypeNameTable[index];
10808 return(result);
10809}
10810
10811//
10812// Debug out
10813//
10814#if defined(FPL_PLATFORM_WINDOWS)
10815fpl_platform_api void fplDebugOut(const char *text) {
10816 wchar_t buffer[FPL_MAX_BUFFER_LENGTH];
10817 fplUTF8StringToWideString(text, fplGetStringLength(text), buffer, fplArrayCount(buffer));
10818 OutputDebugStringW(buffer);
10819}
10820#else
10821fpl_platform_api void fplDebugOut(const char *text) {
10822 fplConsoleOut(text);
10823}
10824#endif
10825
10826fpl_common_api void fplDebugFormatOut(const char *format, ...) {
10827 if (format != fpl_null) {
10828 char buffer[FPL_MAX_BUFFER_LENGTH];
10829 va_list argList;
10830 va_start(argList, format);
10831 fplStringFormatArgs(buffer, fplArrayCount(buffer), format, argList);
10832 va_end(argList);
10833 fplDebugOut(buffer);
10834 }
10835}
10836#endif // FPL__COMMON_DEFINED
10837
10838// ############################################################################
10839//
10840// > WIN32_PLATFORM (Win32, Win64)
10841//
10842// ############################################################################
10843#if defined(FPL_PLATFORM_WINDOWS)
10844
10845# if defined(FPL_ARCH_X86)
10846# define FPL_MEMORY_BARRIER() \
10847 LONG barrier; \
10848 _InterlockedOr(&barrier, 0);
10849# elif defined(FPL_ARCH_X64)
10850 // @NOTE(final): No need for hardware memory fence on X64 because the hardware guarantees memory order always.
10851# define FPL_MEMORY_BARRIER()
10852# endif
10853
10854#if defined(FPL__ENABLE_WINDOW)
10855
10856fpl_internal DWORD fpl__Win32MakeWindowStyle(const fplWindowSettings *settings) {
10857 DWORD result = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
10858 if (settings->isFullscreen || !settings->isDecorated) {
10859 result |= WS_POPUP;
10860 } else {
10861 result |= WS_OVERLAPPEDWINDOW;
10862 if (!settings->isResizable) {
10863 result &= ~(WS_MAXIMIZEBOX | WS_THICKFRAME);
10864 }
10865 }
10866 return(result);
10867}
10868
10869fpl_internal DWORD fpl__Win32MakeWindowExStyle(const fplWindowSettings *settings) {
10870 DWORD result = WS_EX_APPWINDOW;
10871 if (settings->isFullscreen || settings->isFloating) {
10872 result |= WS_EX_TOPMOST;
10873 }
10874 return(result);
10875}
10876
10877fpl_internal void fpl__Win32SaveWindowState(const fpl__Win32Api *wapi, fpl__Win32LastWindowInfo *target, HWND windowHandle) {
10878 target->isMaximized = !!wapi->user.IsZoomed(windowHandle);
10879 target->isMinimized = !!wapi->user.IsIconic(windowHandle);
10880 target->style = fpl__win32_GetWindowLong(windowHandle, GWL_STYLE);
10881 target->exStyle = fpl__win32_GetWindowLong(windowHandle, GWL_EXSTYLE);
10882 wapi->user.GetWindowPlacement(windowHandle, &target->placement);
10883}
10884
10885fpl_internal void fpl__Win32RestoreWindowState(const fpl__Win32Api *wapi, const fpl__Win32LastWindowInfo *target, HWND windowHandle) {
10886 fplAssert(target->style > 0 && target->exStyle > 0);
10887 fpl__win32_SetWindowLong(windowHandle, GWL_STYLE, target->style);
10888 fpl__win32_SetWindowLong(windowHandle, GWL_EXSTYLE, target->exStyle);
10889 wapi->user.SetWindowPlacement(windowHandle, &target->placement);
10890 wapi->user.SetWindowPos(windowHandle, fpl_null, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
10891 if (target->isMaximized) {
10892 wapi->user.SendMessageW(windowHandle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
10893 } else if (target->isMinimized) {
10894 wapi->user.SendMessageW(windowHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
10895 }
10896}
10897
10898fpl_internal bool fpl__Win32LeaveFullscreen() {
10899 const fpl__PlatformAppState *platState = fpl__global__AppState;
10900 fplAssert(platState != fpl_null);
10901 const fpl__Win32AppState *win32State = &platState->win32;
10902 const fpl__Win32Api *wapi = &win32State->winApi;
10903 const fplWindowSettings *settings = &platState->currentSettings.window;
10904 const fpl__Win32WindowState *win32Window = &platState->window.win32;
10905 const fpl__Win32LastWindowInfo *fullscreenInfo = &win32Window->lastFullscreenInfo;
10906 HWND windowHandle = win32Window->windowHandle;
10907 fpl__Win32RestoreWindowState(wapi, fullscreenInfo, windowHandle);
10908 bool result;
10909 if (fullscreenInfo->wasResolutionChanged) {
10910 result = (wapi->user.ChangeDisplaySettingsW(fpl_null, CDS_RESET) == DISP_CHANGE_SUCCESSFUL);
10911 } else {
10912 result = true;
10913 }
10914 return(result);
10915}
10916
10917fpl_internal bool fpl__Win32EnterFullscreen(const int32_t xpos, const int32_t ypos, const int32_t fullscreenWidth, const int32_t fullscreenHeight, const uint32_t refreshRate, const uint32_t colorBits, const bool allowResolutionChange) {
10918 fpl__PlatformAppState *platState = fpl__global__AppState;
10919 fplAssert(platState != fpl_null);
10920 fpl__Win32AppState *win32State = &platState->win32;
10921 const fpl__Win32Api *wapi = &win32State->winApi;
10922 const fplWindowSettings *settings = &platState->currentSettings.window;
10923 fpl__Win32WindowState *win32Window = &platState->window.win32;
10924 fpl__Win32LastWindowInfo *fullscreenInfo = &win32Window->lastFullscreenInfo;
10925
10926 HWND windowHandle = win32Window->windowHandle;
10927 HDC deviceContext = win32Window->deviceContext;
10928
10929 fplAssert(fullscreenInfo->style > 0 && fullscreenInfo->exStyle > 0);
10930 fpl__win32_SetWindowLong(windowHandle, GWL_STYLE, fullscreenInfo->style & ~(WS_CAPTION | WS_THICKFRAME));
10931 fpl__win32_SetWindowLong(windowHandle, GWL_EXSTYLE, fullscreenInfo->exStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
10932
10933 MONITORINFO monitor = fplZeroInit;
10934 monitor.cbSize = sizeof(monitor);
10935 wapi->user.GetMonitorInfoW(wapi->user.MonitorFromWindow(windowHandle, MONITOR_DEFAULTTONEAREST), &monitor);
10936
10937 bool result;
10938 if (allowResolutionChange && (fullscreenWidth > 0) && (fullscreenHeight > 0)) {
10939 int32_t useFullscreenWidth = fullscreenWidth;
10940 int32_t useFullscreenHeight = fullscreenHeight;
10941
10942 DWORD useRefreshRate = refreshRate;
10943 if (!useRefreshRate) {
10944 useRefreshRate = wapi->gdi.GetDeviceCaps(deviceContext, VREFRESH);
10945 }
10946
10947 DWORD useColourBits = colorBits;
10948 if (!useColourBits) {
10949 useColourBits = wapi->gdi.GetDeviceCaps(deviceContext, BITSPIXEL);
10950 }
10951
10952 RECT windowRect;
10953 // @TODO(final/Win32): This may not be correct to assume 0, 0 as origin for the current display
10954 windowRect.left = 0;
10955 windowRect.top = 0;
10956 windowRect.right = windowRect.left + useFullscreenWidth;
10957 windowRect.bottom = windowRect.top + useFullscreenHeight;
10958
10959 WINDOWPLACEMENT placement = fplZeroInit;
10960 placement.length = sizeof(placement);
10961 placement.rcNormalPosition = windowRect;
10962 placement.showCmd = SW_SHOW;
10963 wapi->user.SetWindowPlacement(windowHandle, &placement);
10964 wapi->user.SetWindowPos(windowHandle, fpl_null, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
10965
10966 DEVMODEW fullscreenSettings = fplZeroInit;
10967 wapi->user.EnumDisplaySettingsW(fpl_null, 0, &fullscreenSettings);
10968 fullscreenSettings.dmPelsWidth = useFullscreenWidth;
10969 fullscreenSettings.dmPelsHeight = useFullscreenHeight;
10970 fullscreenSettings.dmBitsPerPel = useColourBits;
10971 fullscreenSettings.dmDisplayFrequency = useRefreshRate;
10972 fullscreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
10973 result = (wapi->user.ChangeDisplaySettingsW(&fullscreenSettings, CDS_FULLSCREEN) == DISP_CHANGE_SUCCESSFUL);
10974 fullscreenInfo->wasResolutionChanged = true;
10975 } else {
10976 RECT windowRect = fplZeroInit;
10977 if ((xpos != INT32_MAX) && (ypos != INT32_MAX) && (fullscreenWidth != 0) && (fullscreenHeight != 0)) {
10978 windowRect.left = xpos;
10979 windowRect.top = ypos;
10980 windowRect.right = xpos + fullscreenWidth;
10981 windowRect.top = ypos + fullscreenHeight;
10982 } else {
10983 windowRect = monitor.rcMonitor;
10984 }
10985 WINDOWPLACEMENT placement = fplZeroInit;
10986 placement.length = sizeof(placement);
10987 placement.rcNormalPosition = windowRect;
10988 placement.showCmd = SW_SHOWNORMAL;
10989 wapi->user.SetWindowPlacement(windowHandle, &placement);
10990 wapi->user.SetWindowPos(windowHandle, fpl_null, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
10991 result = true;
10992 fullscreenInfo->wasResolutionChanged = false;
10993 }
10994
10995 return(result);
10996}
10997
10998fpl_internal bool fpl__Win32SetWindowFullscreen(const bool value, const int32_t x, const int32_t y, const int32_t w, const int32_t h, const uint32_t refreshRate, const bool allowResolutionChange) {
10999 FPL__CheckPlatform(false);
11000 fpl__PlatformAppState *appState = fpl__global__AppState;
11001 fpl__Win32AppState *win32AppState = &appState->win32;
11002 fpl__Win32WindowState *windowState = &appState->window.win32;
11003 fplWindowSettings *windowSettings = &appState->currentSettings.window;
11004 fpl__Win32LastWindowInfo *fullscreenState = &windowState->lastFullscreenInfo;
11005 const fpl__Win32Api *wapi = &win32AppState->winApi;
11006
11007 HWND windowHandle = windowState->windowHandle;
11008
11009 // Save current window info if not already fullscreen
11010 if (!windowSettings->isFullscreen) {
11011 fpl__Win32SaveWindowState(wapi, fullscreenState, windowHandle);
11012 if (fullscreenState->isMaximized || fullscreenState->isMinimized) {
11013 wapi->user.ShowWindow(windowHandle, SW_RESTORE);
11014 }
11015 }
11016
11017 if (value) {
11018 // Enter fullscreen mode or fallback to window mode
11019 windowSettings->isFullscreen = fpl__Win32EnterFullscreen(x, y, w, h, refreshRate, 0, allowResolutionChange);
11020 if (!windowSettings->isFullscreen) {
11021 fpl__Win32LeaveFullscreen();
11022 }
11023 } else {
11024 fpl__Win32LeaveFullscreen();
11025 windowSettings->isFullscreen = false;
11026 }
11027 bool result = windowSettings->isFullscreen != 0;
11028 return(result);
11029}
11030
11031fpl_internal float fpl__Win32XInputProcessStickValue(const SHORT value, const SHORT deadZoneThreshold) {
11032 float result = 0;
11033 if (value < -deadZoneThreshold) {
11034 result = (float)((value + deadZoneThreshold) / (32768.0f - deadZoneThreshold));
11035 } else if (value > deadZoneThreshold) {
11036 result = (float)((value - deadZoneThreshold) / (32767.0f - deadZoneThreshold));
11037 }
11038 return(result);
11039}
11040
11041fpl_internal void fpl__Win32XInputGamepadToGamepadState(const XINPUT_GAMEPAD *newState, fplGamepadState *outState) {
11042 // If we got here, the controller is definitily by connected
11043 outState->isConnected = true;
11044
11045 // Analog sticks
11046 outState->leftStickX = fpl__Win32XInputProcessStickValue(newState->sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
11047 outState->leftStickY = fpl__Win32XInputProcessStickValue(newState->sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE);
11048 outState->rightStickX = fpl__Win32XInputProcessStickValue(newState->sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
11049 outState->rightStickY = fpl__Win32XInputProcessStickValue(newState->sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE);
11050
11051 // Triggers
11052 outState->leftTrigger = (float)newState->bLeftTrigger / 255.0f;
11053 outState->rightTrigger = (float)newState->bRightTrigger / 255.0f;
11054
11055 // Digital pad buttons
11056 if (newState->wButtons & XINPUT_GAMEPAD_DPAD_UP)
11057 outState->dpadUp.isDown = true;
11058 if (newState->wButtons & XINPUT_GAMEPAD_DPAD_DOWN)
11059 outState->dpadDown.isDown = true;
11060 if (newState->wButtons & XINPUT_GAMEPAD_DPAD_LEFT)
11061 outState->dpadLeft.isDown = true;
11062 if (newState->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT)
11063 outState->dpadRight.isDown = true;
11064
11065 // Action buttons
11066 if (newState->wButtons & XINPUT_GAMEPAD_A)
11067 outState->actionA.isDown = true;
11068 if (newState->wButtons & XINPUT_GAMEPAD_B)
11069 outState->actionB.isDown = true;
11070 if (newState->wButtons & XINPUT_GAMEPAD_X)
11071 outState->actionX.isDown = true;
11072 if (newState->wButtons & XINPUT_GAMEPAD_Y)
11073 outState->actionY.isDown = true;
11074
11075 // Center buttons
11076 if (newState->wButtons & XINPUT_GAMEPAD_START)
11077 outState->start.isDown = true;
11078 if (newState->wButtons & XINPUT_GAMEPAD_BACK)
11079 outState->back.isDown = true;
11080
11081 // Shoulder buttons
11082 if (newState->wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER)
11083 outState->leftShoulder.isDown = true;
11084 if (newState->wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER)
11085 outState->rightShoulder.isDown = true;
11086
11087 // Thumb buttons
11088 if (newState->wButtons & XINPUT_GAMEPAD_LEFT_THUMB)
11089 outState->leftThumb.isDown = true;
11090 if (newState->wButtons & XINPUT_GAMEPAD_RIGHT_THUMB)
11091 outState->rightThumb.isDown = true;
11092
11093 // The controller is only active, when any button or any movement happened
11094 outState->isActive = !fpl__IsZeroMemory(newState, sizeof(*newState));
11095}
11096
11097fpl_internal void fpl__Win32UpdateGameControllers(const fplSettings *settings, const fpl__Win32InitState *initState, fpl__Win32XInputState *xinputState) {
11098 fplAssert(settings != fpl_null);
11099 fplAssert(xinputState != fpl_null);
11100 if (xinputState->xinputApi.XInputGetState != fpl_null) {
11101 //
11102 // Detect new controller (Only at a fixed frequency)
11103 //
11104 if (xinputState->lastDeviceSearchTime.QuadPart == 0) {
11105 QueryPerformanceCounter(&xinputState->lastDeviceSearchTime);
11106 }
11107 LARGE_INTEGER currentDeviceSearchTime, currentDeviceSearchFreq;
11108 QueryPerformanceCounter(&currentDeviceSearchTime);
11109 QueryPerformanceFrequency(&currentDeviceSearchFreq);
11110 uint64_t deviceSearchDifferenceTimeInMs = ((currentDeviceSearchTime.QuadPart - xinputState->lastDeviceSearchTime.QuadPart) / (currentDeviceSearchFreq.QuadPart / 1000));
11111 if ((settings->input.controllerDetectionFrequency == 0) || (deviceSearchDifferenceTimeInMs > settings->input.controllerDetectionFrequency)) {
11112 xinputState->lastDeviceSearchTime = currentDeviceSearchTime;
11113 for (DWORD controllerIndex = 0; controllerIndex < XUSER_MAX_COUNT; ++controllerIndex) {
11114 XINPUT_STATE controllerState = fplZeroInit;
11115 if (xinputState->xinputApi.XInputGetState(controllerIndex, &controllerState) == ERROR_SUCCESS) {
11116 if (!xinputState->isConnected[controllerIndex]) {
11117 // Connected
11118 xinputState->isConnected[controllerIndex] = true;
11119 fplStringFormat(xinputState->deviceNames[controllerIndex], fplArrayCount(xinputState->deviceNames[controllerIndex]), "XInput-Device [%d]", controllerIndex);
11120
11121 fplEvent ev = fplZeroInit;
11124 ev.gamepad.deviceIndex = controllerIndex;
11125 ev.gamepad.deviceName = xinputState->deviceNames[controllerIndex];
11126 fpl__PushInternalEvent(&ev);
11127 }
11128 } else {
11129 if (xinputState->isConnected[controllerIndex]) {
11130 // Disconnected
11131 xinputState->isConnected[controllerIndex] = false;
11132
11133 fplEvent ev = fplZeroInit;
11136 ev.gamepad.deviceIndex = controllerIndex;
11137 ev.gamepad.deviceName = xinputState->deviceNames[controllerIndex];
11138 fpl__PushInternalEvent(&ev);
11139 }
11140 }
11141 }
11142 }
11143
11144 //
11145 // Update controller state when connected only
11146 //
11147 for (DWORD controllerIndex = 0; controllerIndex < XUSER_MAX_COUNT; ++controllerIndex) {
11148 if (xinputState->isConnected[controllerIndex]) {
11149 XINPUT_STATE controllerState = fplZeroInit;
11150 if (xinputState->xinputApi.XInputGetState(controllerIndex, &controllerState) == ERROR_SUCCESS) {
11151 // State changed
11152 fplEvent ev = fplZeroInit;
11155 ev.gamepad.deviceIndex = controllerIndex;
11156 ev.gamepad.deviceName = xinputState->deviceNames[controllerIndex];
11157 const XINPUT_GAMEPAD *newPadState = &controllerState.Gamepad;
11158 fpl__Win32XInputGamepadToGamepadState(newPadState, &ev.gamepad.state);
11160 fpl__PushInternalEvent(&ev);
11161 }
11162 }
11163 }
11164 }
11165}
11166
11167fpl_internal bool fpl__Win32IsKeyDown(const fpl__Win32Api *wapi, const int virtualKey) {
11168 bool result = (wapi->user.GetAsyncKeyState(virtualKey) & 0x8000) != 0;
11169 return(result);
11170}
11171
11172fpl_internal bool fpl__Win32IsKeyActive(const fpl__Win32Api *wapi, const int virtualKey) {
11173 bool result = (wapi->user.GetKeyState(virtualKey) & 0x0001) != 0;
11174 return(result);
11175}
11176
11177fpl_internal bool fpl__Win32IsCursorInWindow(const fpl__Win32Api *wapi, const fpl__Win32WindowState *win32Window) {
11178 POINT pos;
11179 if (!wapi->user.GetCursorPos(&pos)) {
11180 return false;
11181 }
11182 // Not this window?
11183 if (wapi->user.WindowFromPoint(pos) != win32Window->windowHandle) {
11184 return false;
11185 }
11186 // Cursor in client rect?
11187 RECT area;
11188 wapi->user.GetClientRect(win32Window->windowHandle, &area);
11189
11190 wapi->user.ClientToScreen(win32Window->windowHandle, (LPPOINT)&area.left);
11191 wapi->user.ClientToScreen(win32Window->windowHandle, (LPPOINT)&area.right);
11192 bool result = wapi->user.PtInRect(&area, pos) == TRUE;
11193
11194 return(result);
11195}
11196
11197fpl_internal void fpl__Win32LoadCursor(const fpl__Win32Api *wapi, const fpl__Win32WindowState *window) {
11198 if (window->isCursorActive) {
11199 wapi->user.SetCursor(fpl__win32_LoadCursor(fpl_null, IDC_ARROW));
11200 } else {
11201 wapi->user.SetCursor(fpl_null);
11202 }
11203}
11204
11205fpl_internal void fpl__Win32UpdateClipRect(const fpl__Win32Api *wapi, const fpl__Win32WindowState *window) {
11206 if (window != fpl_null) {
11207 RECT clipRect;
11208 wapi->user.GetClientRect(window->windowHandle, &clipRect);
11209 wapi->user.ClientToScreen(window->windowHandle, (POINT *)&clipRect.left);
11210 wapi->user.ClientToScreen(window->windowHandle, (POINT *)&clipRect.right);
11211 wapi->user.ClipCursor(&clipRect);
11212 } else {
11213 wapi->user.ClipCursor(fpl_null);
11214 }
11215}
11216
11217fpl_internal void fpl__Win32SetCursorState(const fpl__Win32Api *wapi, fpl__Win32WindowState *window, const bool state) {
11218 // @NOTE(final): We use RAWINPUT to remove the mouse device entirely when it needs to be hidden
11219 if (!state) {
11220 const RAWINPUTDEVICE rid = fplStructInit(RAWINPUTDEVICE, 0x01, 0x02, 0, window->windowHandle);
11221 if (!wapi->user.RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
11222 FPL__ERROR(FPL__MODULE_WINDOW, "Failed register raw input mouse device for window handle '%p'", window->windowHandle);
11223 }
11224 } else {
11225 const RAWINPUTDEVICE rid = fplStructInit(RAWINPUTDEVICE, 0x01, 0x02, RIDEV_REMOVE, fpl_null);
11226 if (!wapi->user.RegisterRawInputDevices(&rid, 1, sizeof(rid))) {
11227 FPL__ERROR(FPL__MODULE_WINDOW, "Failed to unregister raw input mouse device");
11228 }
11229 }
11230 if (fpl__Win32IsCursorInWindow(wapi, window)) {
11231 fpl__Win32LoadCursor(wapi, window);
11232 }
11233}
11234
11235fpl_internal void fpl__Win32ShowCursor(const fpl__Win32Api *wapi, fpl__Win32WindowState *window) {
11236 fpl__Win32SetCursorState(wapi, window, false);
11237}
11238fpl_internal void fpl__Win32HideCursor(const fpl__Win32Api *wapi, fpl__Win32WindowState *window) {
11239 fpl__Win32SetCursorState(wapi, window, true);
11240}
11241
11242fpl_internal fplKeyboardModifierFlags fpl__Win32GetKeyboardModifiers(const fpl__Win32Api *wapi) {
11244 bool lAltKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_LMENU);
11245 bool rAltKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_RMENU);
11246 bool lShiftKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_LSHIFT);
11247 bool rShiftKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_RSHIFT);
11248 bool lCtrlKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_LCONTROL);
11249 bool rCtrlKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_RCONTROL);
11250 bool lSuperKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_LWIN);
11251 bool rSuperKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_RWIN);
11252 bool capsLockActive = fpl__Win32IsKeyActive(wapi, VK_CAPITAL);
11253 bool numLockActive = fpl__Win32IsKeyActive(wapi, VK_NUMLOCK);
11254 bool scrollLockActive = fpl__Win32IsKeyActive(wapi, VK_SCROLL);
11255 if (lAltKeyIsDown) {
11256 modifiers |= fplKeyboardModifierFlags_LAlt;
11257 }
11258 if (rAltKeyIsDown) {
11259 modifiers |= fplKeyboardModifierFlags_RAlt;
11260 }
11261 if (lShiftKeyIsDown) {
11263 }
11264 if (rShiftKeyIsDown) {
11266 }
11267 if (lCtrlKeyIsDown) {
11268 modifiers |= fplKeyboardModifierFlags_LCtrl;
11269 }
11270 if (rCtrlKeyIsDown) {
11271 modifiers |= fplKeyboardModifierFlags_RCtrl;
11272 }
11273 if (lSuperKeyIsDown) {
11275 }
11276 if (rSuperKeyIsDown) {
11278 }
11279 if (capsLockActive) {
11281 }
11282 if (numLockActive) {
11284 }
11285 if (scrollLockActive) {
11287 }
11288 return(modifiers);
11289}
11290
11291fpl_internal void fpl__Win32HandleMessage(const fpl__Win32Api *wapi, fpl__PlatformAppState *appState, fpl__Win32WindowState *windowState, MSG *msg) {
11292 if (appState->currentSettings.window.callbacks.eventCallback != fpl_null) {
11293 appState->currentSettings.window.callbacks.eventCallback(fplGetPlatformType(), windowState, &msg, appState->currentSettings.window.callbacks.eventUserData);
11294 }
11295 wapi->user.TranslateMessage(msg);
11296 wapi->user.DispatchMessageW(msg);
11297}
11298
11299fpl_internal void CALLBACK fpl__Win32MessageFiberProc(struct fpl__PlatformAppState *appState) {
11300 fpl__Win32AppState *win32State = &appState->win32;
11301 fpl__Win32WindowState *windowState = &appState->window.win32;
11302 const fpl__Win32Api *wapi = &win32State->winApi;
11303 wapi->user.SetTimer(appState->window.win32.windowHandle, 1, 1, 0);
11304 for (;;) {
11305 MSG message;
11306 while (wapi->user.PeekMessageW(&message, 0, 0, 0, PM_REMOVE)) {
11307 fpl__Win32HandleMessage(wapi, appState, windowState, &message);
11308 }
11309 SwitchToFiber(appState->window.win32.mainFiber);
11310 }
11311}
11312
11313LRESULT CALLBACK fpl__Win32MessageProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
11314 fpl__PlatformAppState *appState = fpl__global__AppState;
11315 fplAssert(appState != fpl_null);
11316
11317 fpl__Win32AppState *win32State = &appState->win32;
11318 fpl__Win32WindowState *win32Window = &appState->window.win32;
11319 const fpl__Win32Api *wapi = &win32State->winApi;
11320
11321 if (!win32Window->windowHandle) {
11322 return wapi->user.DefWindowProcW(hwnd, msg, wParam, lParam);
11323 }
11324
11325 LRESULT result = 0;
11326 switch (msg) {
11327 case WM_TIMER:
11328 {
11329 if (win32Window->mainFiber != fpl_null) {
11330 SwitchToFiber(win32Window->mainFiber);
11331 }
11332 } break;
11333
11334 case WM_DESTROY:
11335 case WM_CLOSE:
11336 {
11337 appState->window.isRunning = false;
11338 } break;
11339
11340 case WM_SIZE:
11341 {
11342 DWORD newWidth = LOWORD(lParam);
11343 DWORD newHeight = HIWORD(lParam);
11344 if (wParam == SIZE_MAXIMIZED) {
11345 fpl__PushWindowSizeEvent(fplWindowEventType_Maximized, newWidth, newHeight);
11346 } else if (wParam == SIZE_MINIMIZED) {
11347 fpl__PushWindowSizeEvent(fplWindowEventType_Minimized, newWidth, newHeight);
11348 } else if (wParam == SIZE_RESTORED) {
11349 fpl__PushWindowSizeEvent(fplWindowEventType_Restored, newWidth, newHeight);
11350 }
11351
11352# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
11353 if (appState->currentSettings.video.backend == fplVideoBackendType_Software) {
11354 if (appState->initSettings.video.isAutoSize) {
11355 fplResizeVideoBackBuffer(newWidth, newHeight);
11356 }
11357 }
11358# endif
11359
11360 fpl__PushWindowSizeEvent(fplWindowEventType_Resized, newWidth, newHeight);
11361
11362 return 0;
11363 } break;
11364
11365 case WM_DROPFILES:
11366 {
11367 HDROP dropHandle = (HDROP)wParam;
11368 char fileBufferA[FPL_MAX_PATH_LENGTH];
11369 UINT fileCount;
11370 wchar_t fileBufferW[FPL_MAX_PATH_LENGTH];
11371 fileCount = wapi->shell.DragQueryFileW(dropHandle, 0xFFFFFFFF, fileBufferW, 0);
11372 if (fileCount > 0) {
11373 size_t filesTableSize = fileCount * sizeof(char **);
11374 size_t maxFileStride = FPL_MAX_PATH_LENGTH * 2 + 1;
11375 size_t filesMemorySize = filesTableSize + FPL__ARBITARY_PADDING + maxFileStride * fileCount;
11376 void *filesTableMemory = fpl__AllocateDynamicMemory(filesMemorySize, 16);
11377 char **filesTable = (char **)filesTableMemory;
11378 for (UINT fileIndex = 0; fileIndex < fileCount; ++fileIndex) {
11379 filesTable[fileIndex] = (char *)((uint8_t *)filesTableMemory + filesTableSize + FPL__ARBITARY_PADDING + fileIndex * maxFileStride);
11380 }
11381 for (UINT fileIndex = 0; fileIndex < fileCount; ++fileIndex) {
11382 char *file = filesTable[fileIndex];
11383 fileBufferW[0] = 0;
11384 UINT dragResult = wapi->shell.DragQueryFileW(dropHandle, 0, fileBufferW, fplArrayCount(fileBufferW));
11385 size_t sourceLen = lstrlenW(fileBufferW);
11386 fplWideStringToUTF8String(fileBufferW, sourceLen, file, maxFileStride);
11387 }
11388 fplMemoryBlock memory = fplZeroInit;
11389 memory.size = filesMemorySize;
11390 memory.base = filesTableMemory;
11391 fpl__PushWindowDropFilesEvent(fileBufferA, fileCount, (const char **)filesTable, &memory);
11392 }
11393 } break;
11394
11395 case WM_SYSKEYDOWN:
11396 case WM_SYSKEYUP:
11397 case WM_KEYDOWN:
11398 case WM_KEYUP:
11399 {
11400 if (!appState->currentSettings.input.disabledEvents) {
11401 uint64_t keyCode = wParam;
11402 bool isDown = (lParam & (1 << 31)) == 0;
11403 bool wasDown = (lParam & (1 << 30)) != 0;
11404 bool altKeyIsDown = fpl__Win32IsKeyDown(wapi, VK_MENU);
11406 fplKeyboardModifierFlags modifiers = fpl__Win32GetKeyboardModifiers(wapi);
11407 fpl__HandleKeyboardButtonEvent(&appState->window, GetTickCount(), keyCode, modifiers, keyState, false);
11408 }
11409 } break;
11410
11411 case WM_CHAR:
11412 case WM_SYSCHAR:
11413 case WM_UNICHAR:
11414 {
11415 if ((msg == WM_UNICHAR) && (wParam == UNICODE_NOCHAR)) {
11416 // @NOTE(final): WM_UNICHAR was sent by a third-party input method. Do not add any chars here!
11417 return TRUE;
11418 }
11419 fpl__HandleKeyboardInputEvent(&appState->window, (uint64_t)wParam, (uint32_t)wParam);
11420 return 0;
11421 } break;
11422
11423 case WM_ACTIVATE:
11424 {
11425 } break;
11426
11427 case WM_MOUSEACTIVATE:
11428 {
11429 if (HIWORD(lParam) == WM_LBUTTONDOWN) {
11430 if (LOWORD(lParam) == HTCLOSE || LOWORD(lParam) == HTMINBUTTON || LOWORD(lParam) == HTMAXBUTTON) {
11431 win32Window->isFrameInteraction = true;
11432 }
11433 }
11434 } break;
11435
11436 case WM_CAPTURECHANGED:
11437 {
11438 if (lParam == 0 && win32Window->isFrameInteraction) {
11439 if (!win32Window->isCursorActive) {
11440 fpl__Win32HideCursor(wapi, win32Window);
11441 }
11442 win32Window->isFrameInteraction = false;
11443 }
11444 } break;
11445
11446 case WM_SETFOCUS:
11447 {
11448 fplEvent newEvent = fplZeroInit;
11449 newEvent.type = fplEventType_Window;
11451 fpl__PushInternalEvent(&newEvent);
11452 if (win32Window->isFrameInteraction) {
11453 break;
11454 }
11455 if (!win32Window->isCursorActive) {
11456 fpl__Win32HideCursor(wapi, win32Window);
11457 }
11458 return 0;
11459 } break;
11460
11461 case WM_KILLFOCUS:
11462 {
11463 if (!win32Window->isCursorActive) {
11464 fpl__Win32ShowCursor(wapi, win32Window);
11465 }
11466 fplEvent newEvent = fplZeroInit;
11467 newEvent.type = fplEventType_Window;
11469 fpl__PushInternalEvent(&newEvent);
11470 return 0;
11471 } break;
11472
11473 case WM_ENTERSIZEMOVE:
11474 case WM_ENTERMENULOOP:
11475 {
11476 // Restore cursor when needed
11477 if (!win32Window->isCursorActive) {
11478 fpl__Win32ShowCursor(wapi, win32Window);
11479 }
11480 } break;
11481
11482 case WM_EXITSIZEMOVE:
11483 case WM_EXITMENULOOP:
11484 {
11485 if (!win32Window->isCursorActive) {
11486 fpl__Win32HideCursor(wapi, win32Window);
11487 }
11488 } break;
11489
11490 case WM_LBUTTONDOWN:
11491 case WM_LBUTTONUP:
11492 case WM_RBUTTONDOWN:
11493 case WM_RBUTTONUP:
11494 case WM_MBUTTONDOWN:
11495 case WM_MBUTTONUP:
11496 {
11497 if (!appState->currentSettings.input.disabledEvents) {
11498 fplButtonState buttonState;
11499 if (msg == WM_LBUTTONDOWN || msg == WM_RBUTTONDOWN || msg == WM_MBUTTONDOWN) {
11500 buttonState = fplButtonState_Press;
11501 } else {
11502 buttonState = fplButtonState_Release;
11503 }
11504 if (buttonState == fplButtonState_Press) {
11505 wapi->user.SetCapture(hwnd);
11506 } else {
11507 wapi->user.ReleaseCapture();
11508 }
11509 fplMouseButtonType mouseButton;
11510 if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONUP) {
11511 mouseButton = fplMouseButtonType_Left;
11512 } else if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONUP) {
11513 mouseButton = fplMouseButtonType_Right;
11514 } else if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONUP) {
11515 mouseButton = fplMouseButtonType_Middle;
11516 } else {
11517 mouseButton = fplMouseButtonType_None;
11518 }
11519 if (mouseButton != fplMouseButtonType_None) {
11520 int32_t mouseX = GET_X_LPARAM(lParam);
11521 int32_t mouseY = GET_Y_LPARAM(lParam);
11522 fpl__HandleMouseButtonEvent(&appState->window, mouseX, mouseY, mouseButton, buttonState);
11523 }
11524 }
11525 } break;
11526 case WM_MOUSEMOVE:
11527 {
11528 if (!appState->currentSettings.input.disabledEvents) {
11529 int32_t mouseX = GET_X_LPARAM(lParam);
11530 int32_t mouseY = GET_Y_LPARAM(lParam);
11531 fpl__HandleMouseMoveEvent(&appState->window, mouseX, mouseY);
11532 }
11533 } break;
11534 case WM_MOUSEWHEEL:
11535 {
11536 int32_t mouseX = GET_X_LPARAM(lParam);
11537 int32_t mouseY = GET_Y_LPARAM(lParam);
11538 short zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
11539 float wheelDelta = zDelta / (float)WHEEL_DELTA;
11540 fpl__HandleMouseWheelEvent(&appState->window, mouseX, mouseY, wheelDelta);
11541 } break;
11542
11543 case WM_SETCURSOR:
11544 {
11545 if (LOWORD(lParam) == HTCLIENT) {
11546 fpl__Win32LoadCursor(wapi, win32Window);
11547 return TRUE;
11548 }
11549 } break;
11550
11551 case WM_PAINT:
11552 {
11553 if (appState->currentSettings.window.callbacks.exposedCallback != fpl_null) {
11554 MSG msgData = fplZeroInit;
11555 msgData.message = msg;
11556 msgData.hwnd = hwnd;
11557 msgData.wParam = wParam;
11558 msgData.lParam = lParam;
11559 appState->currentSettings.window.callbacks.exposedCallback(fplGetPlatformType(), win32Window, &msgData, appState->currentSettings.window.callbacks.exposedUserData);
11560 } else {
11561 if (appState->currentSettings.video.backend == fplVideoBackendType_None) {
11562 PAINTSTRUCT ps;
11563 HDC hdc = wapi->user.BeginPaint(hwnd, &ps);
11564 wapi->user.EndPaint(hwnd, &ps);
11565 return(0);
11566 }
11567 }
11568 } break;
11569
11570 case WM_ERASEBKGND:
11571 {
11572 // Prevent erasing of the background always, but only if a video backend is being used
11573 // NOTE(tspaete): Do not prevent the erasing of the backend for software video output, otherwise we will get weird flickering issue at startup
11574 if (appState->currentSettings.video.backend != fplVideoBackendType_None && appState->currentSettings.video.backend != fplVideoBackendType_Software) {
11575 return 1;
11576 }
11577 } break;
11578
11579 case WM_SYSCOMMAND:
11580 {
11581 WPARAM masked = wParam & 0xFFF0;
11582 switch (masked) {
11583 case SC_SCREENSAVE:
11584 case SC_MONITORPOWER: {
11585 if (appState->currentSettings.window.isScreenSaverPrevented || appState->currentSettings.window.isMonitorPowerPrevented) {
11586 return 0;
11587 }
11588 } break;
11589 }
11590 } break;
11591
11592 default:
11593 break;
11594 }
11595 result = wapi->user.DefWindowProcW(hwnd, msg, wParam, lParam);
11596 return (result);
11597}
11598
11599fpl_internal HICON fpl__Win32LoadIconFromImageSource(const fpl__Win32Api *wapi, const HINSTANCE appInstance, const fplImageSource *imageSource) {
11600 fplAssert(imageSource != fpl_null);
11601 HICON result = 0;
11602 if (imageSource->width > 0 && imageSource->height > 0 && imageSource->data != fpl_null) {
11603 BITMAPV5HEADER bi = fplZeroInit;
11604 bi.bV5Size = sizeof(bi);
11605 bi.bV5Width = (LONG)imageSource->width;
11606 bi.bV5Height = -(LONG)imageSource->height;
11607 bi.bV5Planes = 1;
11608 bi.bV5BitCount = 32;
11609 bi.bV5Compression = BI_BITFIELDS;
11610 bi.bV5RedMask = 0x00ff0000;
11611 bi.bV5GreenMask = 0x0000ff00;
11612 bi.bV5BlueMask = 0x000000ff;
11613 bi.bV5AlphaMask = 0xff000000;
11614
11615 uint8_t *targetData = fpl_null;
11616 HDC dc = wapi->user.GetDC(fpl_null);
11617 HBITMAP colorBitmap = wapi->gdi.CreateDIBSection(dc, (BITMAPINFO *)&bi, DIB_RGB_COLORS, (void **)&targetData, fpl_null, (DWORD)0);
11618 if (colorBitmap == fpl_null) {
11619 FPL__ERROR(FPL__MODULE_WIN32, "Failed to create DIBSection from image with size %lu x %lu", imageSource->width, imageSource->height);
11620 }
11621 wapi->user.ReleaseDC(fpl_null, dc);
11622
11623 HBITMAP maskBitmap = wapi->gdi.CreateBitmap(imageSource->width, imageSource->height, 1, 1, fpl_null);
11624 if (maskBitmap == fpl_null) {
11625 FPL__ERROR(FPL__MODULE_WIN32, "Failed to create Bitmap Mask from image with size %lu x %lu", imageSource->width, imageSource->height);
11626 }
11627 if (colorBitmap != fpl_null && maskBitmap != fpl_null) {
11628 fplAssert(targetData != fpl_null);
11629 uint8_t *dst = targetData;
11630 const uint8_t *src = imageSource->data;
11631 if (imageSource->type == fplImageType_RGBA) {
11632 for (uint32_t i = 0; i < imageSource->width * imageSource->height; ++i) {
11633 dst[0] = src[2]; // R > B
11634 dst[1] = src[1]; // G > G
11635 dst[2] = src[0]; // B > R
11636 dst[3] = src[3]; // A > A
11637 src += 4;
11638 dst += 4;
11639 }
11640 ICONINFO ii = fplZeroInit;
11641 ii.fIcon = TRUE;
11642 ii.xHotspot = 0;
11643 ii.yHotspot = 0;
11644 ii.hbmMask = maskBitmap;
11645 ii.hbmColor = colorBitmap;
11646 result = wapi->user.CreateIconIndirect(&ii);
11647 } else {
11648 FPL__ERROR(FPL__MODULE_WIN32, "Image source type '%d' for icon is not supported", imageSource->type);
11649 }
11650 }
11651 if (colorBitmap != fpl_null) {
11652 wapi->gdi.DeleteObject(colorBitmap);
11653 }
11654 if (maskBitmap != fpl_null) {
11655 wapi->gdi.DeleteObject(maskBitmap);
11656 }
11657 }
11658 if (result == 0) {
11659 result = fpl__win32_LoadIcon(appInstance, IDI_APPLICATION);
11660 }
11661 return(result);
11662}
11663
11664fpl_internal bool fpl__Win32InitWindow(const fplSettings *initSettings, fplWindowSettings *currentWindowSettings, fpl__PlatformAppState *platAppState, fpl__Win32AppState *appState, fpl__Win32WindowState *windowState, const fpl__SetupWindowCallbacks *setupCallbacks) {
11665 fplAssert(appState != fpl_null);
11666 const fpl__Win32Api *wapi = &appState->winApi;
11667 const fplWindowSettings *initWindowSettings = &initSettings->window;
11668
11669 // Presetup window
11670 if (setupCallbacks->preSetup != fpl_null) {
11671 setupCallbacks->preSetup(platAppState, platAppState->initFlags, &platAppState->initSettings);
11672 }
11673
11674 // Register window class
11675 WNDCLASSEXW windowClass = fplZeroInit;
11676 windowClass.cbSize = sizeof(windowClass);
11677 windowClass.hInstance = GetModuleHandleA(fpl_null);
11678
11679 // Set window background, either as system brush or custom brush which needs to be released when changed or the platform is released
11680 if (initWindowSettings->background.value == 0) {
11681 windowState->backgroundBrush = fpl_null;
11682 windowClass.hbrBackground = wapi->user.GetSysColorBrush(COLOR_BACKGROUND);
11683 } else {
11684 COLORREF brushColor = RGB(initWindowSettings->background.r, initWindowSettings->background.g, initWindowSettings->background.b);
11685 windowState->backgroundBrush = wapi->gdi.CreateSolidBrush(brushColor);
11686 windowClass.hbrBackground = windowState->backgroundBrush;
11687 }
11688
11689 windowClass.cbSize = sizeof(windowClass);
11690 windowClass.style = CS_HREDRAW | CS_VREDRAW;
11691 windowClass.hCursor = fpl__win32_LoadCursor(windowClass.hInstance, IDC_ARROW);
11692 windowClass.hIconSm = fpl__Win32LoadIconFromImageSource(wapi, windowClass.hInstance, &initWindowSettings->icons[0]);
11693 windowClass.hIcon = fpl__Win32LoadIconFromImageSource(wapi, windowClass.hInstance, &initWindowSettings->icons[1]);
11694 windowClass.lpszClassName = FPL__WIN32_CLASSNAME;
11695 windowClass.lpfnWndProc = fpl__Win32MessageProc;
11696 windowClass.style |= CS_OWNDC;
11697 lstrcpynW(windowState->windowClass, windowClass.lpszClassName, fplArrayCount(windowState->windowClass));
11698 if (wapi->user.RegisterClassExW(&windowClass) == 0) {
11699 FPL__ERROR(FPL__MODULE_WINDOW, "Failed registering window class '%s'", windowState->windowClass);
11700 return false;
11701 }
11702
11703 // Set window title
11704 wchar_t windowTitleBuffer[FPL_MAX_NAME_LENGTH];
11705 if (fplGetStringLength(initWindowSettings->title) > 0) {
11706 fplUTF8StringToWideString(initWindowSettings->title, fplGetStringLength(initWindowSettings->title), windowTitleBuffer, fplArrayCount(windowTitleBuffer));
11707 } else {
11708 const wchar_t *defaultTitle = FPL__WIN32_UNNAMED_WINDOW;
11709 lstrcpynW(windowTitleBuffer, defaultTitle, fplArrayCount(windowTitleBuffer));
11710 }
11711 wchar_t *windowTitle = windowTitleBuffer;
11712 fplWideStringToUTF8String(windowTitle, lstrlenW(windowTitle), currentWindowSettings->title, fplArrayCount(currentWindowSettings->title));
11713
11714 // Create Fibers
11715 windowState->mainFiber = ConvertThreadToFiber(0);
11716 windowState->messageFiber = CreateFiber(0, (PFIBER_START_ROUTINE)fpl__Win32MessageFiberProc, platAppState);
11717
11718 // Prepare window style, size and position
11719 DWORD style = fpl__Win32MakeWindowStyle(&initSettings->window);
11720 DWORD exStyle = fpl__Win32MakeWindowExStyle(&initSettings->window);
11721 if (initSettings->window.isResizable) {
11722 currentWindowSettings->isResizable = true;
11723 } else {
11724 currentWindowSettings->isResizable = false;
11725 }
11726 int windowX = CW_USEDEFAULT;
11727 int windowY = CW_USEDEFAULT;
11728 int windowWidth;
11729 int windowHeight;
11730 if ((initWindowSettings->windowSize.width > 0) &&
11731 (initWindowSettings->windowSize.height > 0)) {
11732 RECT windowRect;
11733 windowRect.left = 0;
11734 windowRect.top = 0;
11735 windowRect.right = initWindowSettings->windowSize.width;
11736 windowRect.bottom = initWindowSettings->windowSize.height;
11737 wapi->user.AdjustWindowRect(&windowRect, style, false);
11738 windowWidth = windowRect.right - windowRect.left;
11739 windowHeight = windowRect.bottom - windowRect.top;
11740 } else {
11741 // @NOTE(final): Operating system decide how big the window should be.
11742 windowWidth = CW_USEDEFAULT;
11743 windowHeight = CW_USEDEFAULT;
11744 }
11745
11746 // Create window
11747 windowState->windowHandle = wapi->user.CreateWindowExW(exStyle, windowClass.lpszClassName, windowTitle, style, windowX, windowY, windowWidth, windowHeight, fpl_null, fpl_null, windowClass.hInstance, fpl_null);
11748 if (windowState->windowHandle == fpl_null) {
11749 FPL__ERROR(FPL__MODULE_WINDOW, "Failed creating window for class '%s' and position (%d x %d) with size (%d x %d)", windowState->windowClass, windowX, windowY, windowWidth, windowHeight);
11750 return false;
11751 }
11752
11753 // Accept files as drag & drop source
11754 wapi->shell.DragAcceptFiles(windowState->windowHandle, TRUE);
11755
11756 // Get actual window size and store results
11757 currentWindowSettings->windowSize.width = windowWidth;
11758 currentWindowSettings->windowSize.height = windowHeight;
11759 RECT clientRect;
11760 if (wapi->user.GetClientRect(windowState->windowHandle, &clientRect)) {
11761 currentWindowSettings->windowSize.width = clientRect.right - clientRect.left;
11762 currentWindowSettings->windowSize.height = clientRect.bottom - clientRect.top;
11763 }
11764
11765 // Get device context so we can swap the back and front buffer
11766 windowState->deviceContext = wapi->user.GetDC(windowState->windowHandle);
11767 if (windowState->deviceContext == fpl_null) {
11768 FPL__ERROR(FPL__MODULE_WINDOW, "Failed aquiring device context from window '%d'", windowState->windowHandle);
11769 return false;
11770 }
11771
11772 // Call post window setup callback
11773 if (setupCallbacks->postSetup != fpl_null) {
11774 setupCallbacks->postSetup(platAppState, platAppState->initFlags, initSettings);
11775 }
11776
11777 // Enter fullscreen if needed
11778 if (initWindowSettings->isFullscreen) {
11779 fplSetWindowFullscreenSize(true, initWindowSettings->fullscreenSize.width, initWindowSettings->fullscreenSize.height, initWindowSettings->fullscreenRefreshRate);
11780 }
11781
11782 // Show window
11783 wapi->user.ShowWindow(windowState->windowHandle, SW_SHOW);
11784 wapi->user.SetForegroundWindow(windowState->windowHandle);
11785 wapi->user.SetFocus(windowState->windowHandle);
11786
11787 // Cursor is visible at start
11788 windowState->defaultCursor = windowClass.hCursor;
11789 windowState->isCursorActive = true;
11790 platAppState->window.isRunning = true;
11791
11792 return true;
11793}
11794
11795fpl_internal void fpl__Win32ReleaseWindow(const fpl__Win32InitState *initState, const fpl__Win32AppState *appState, fpl__Win32WindowState *windowState) {
11796 const fpl__Win32Api *wapi = &appState->winApi;
11797 if (windowState->deviceContext != fpl_null) {
11798 wapi->user.ReleaseDC(windowState->windowHandle, windowState->deviceContext);
11799 windowState->deviceContext = fpl_null;
11800 }
11801 if (windowState->windowHandle != fpl_null) {
11802 wapi->user.DestroyWindow(windowState->windowHandle);
11803 windowState->windowHandle = fpl_null;
11804 wapi->user.UnregisterClassW(windowState->windowClass, initState->appInstance);
11805 }
11806 if (windowState->backgroundBrush != fpl_null) {
11807 wapi->gdi.DeleteObject(windowState->backgroundBrush);
11808 windowState->backgroundBrush = fpl_null;
11809 }
11810 if (windowState->messageFiber != fpl_null) {
11811 DeleteFiber(windowState->messageFiber);
11812 windowState->messageFiber = fpl_null;
11813 }
11814 if (windowState->mainFiber != fpl_null) {
11815 ConvertFiberToThread();
11816 windowState->mainFiber = fpl_null;
11817 }
11818}
11819
11820#endif // FPL__ENABLE_WINDOW
11821
11822fpl_internal bool fpl__Win32ThreadWaitForMultiple(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout, const bool waitForAll) {
11823 FPL__CheckArgumentNull(threads, false);
11824 FPL__CheckArgumentMax(count, FPL_MAX_THREAD_COUNT, false);
11825 fplStaticAssert(FPL_MAX_THREAD_COUNT >= MAXIMUM_WAIT_OBJECTS);
11826 const size_t actualStride = stride > 0 ? stride : sizeof(fplThreadHandle *);
11827 for (size_t index = 0; index < count; ++index) {
11828 fplThreadHandle *thread = *(fplThreadHandle **)((uint8_t *)threads + index * actualStride);
11829 if (thread == fpl_null) {
11830 FPL__ERROR(FPL__MODULE_THREADING, "Thread for index '%d' are not allowed to be null", index);
11831 return false;
11832 }
11834 if (thread->internalHandle.win32ThreadHandle == fpl_null) {
11835 FPL__ERROR(FPL__MODULE_THREADING, "Thread handle for index '%d' are not allowed to be null", index);
11836 return false;
11837 }
11838 }
11839 }
11840
11841 // @NOTE(final): WaitForMultipleObjects does not work for us here, because each thread will close its handle automatically
11842 // So we screw it and use a simple while loop and wait until either the timeout has been reached or all threads has been stopped.
11844 size_t minThreads = waitForAll ? count : 1;
11845 size_t stoppedThreads = 0;
11846 while (stoppedThreads < minThreads) {
11847 stoppedThreads = 0;
11848 for (size_t index = 0; index < count; ++index) {
11849 fplThreadHandle *thread = *(fplThreadHandle **)((uint8_t *)threads + index * actualStride);
11851 ++stoppedThreads;
11852 }
11853 }
11854 if (stoppedThreads >= minThreads) {
11855 break;
11856 }
11857 if (timeout != FPL_TIMEOUT_INFINITE) {
11858 if ((fplMillisecondsQuery() - startTime) >= timeout) {
11859 break;
11860 }
11861 }
11863 }
11864 bool result = stoppedThreads >= minThreads;
11865 return(result);
11866}
11867
11868fpl_internal bool fpl__Win32SignalWaitForMultiple(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout, const bool waitForAll) {
11869 FPL__CheckArgumentNull(signals, false);
11870 FPL__CheckArgumentMax(count, FPL_MAX_SIGNAL_COUNT, false);
11871 // @MEMORY(final): This wastes a lof memory, use temporary memory allocation here
11872 HANDLE signalHandles[FPL_MAX_SIGNAL_COUNT];
11873 const size_t actualStride = stride > 0 ? stride : sizeof(fplSignalHandle *);
11874 for (uint32_t index = 0; index < count; ++index) {
11875 fplSignalHandle *availableSignal = *(fplSignalHandle **)((uint8_t *)signals + index * actualStride);
11876 if (availableSignal == fpl_null) {
11877 FPL__ERROR(FPL__MODULE_THREADING, "Signal for index '%d' are not allowed to be null", index);
11878 return false;
11879 }
11880 if (availableSignal->internalHandle.win32EventHandle == fpl_null) {
11881 FPL__ERROR(FPL__MODULE_THREADING, "Signal handle for index '%d' are not allowed to be null", index);
11882 return false;
11883 }
11884 HANDLE handle = availableSignal->internalHandle.win32EventHandle;
11885 signalHandles[index] = handle;
11886 }
11887 DWORD t = timeout == FPL_TIMEOUT_INFINITE ? INFINITE : timeout;
11888 DWORD code = WaitForMultipleObjects((DWORD)count, signalHandles, waitForAll ? TRUE : FALSE, t);
11889 bool result = (code >= WAIT_OBJECT_0);
11890 return(result);
11891}
11892
11893fpl_internal void fpl__Win32ReleasePlatform(fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
11894 fplAssert(appState != fpl_null);
11895 fpl__Win32AppState *win32AppState = &appState->win32;
11896 fpl__Win32InitState *win32InitState = &initState->win32;
11897 if (appState->initFlags & fplInitFlags_GameController) {
11898 fpl__Win32UnloadXInputApi(&win32AppState->xinput.xinputApi);
11899 }
11900 fpl__Win32UnloadApi(&win32AppState->winApi);
11901}
11902
11903#if defined(FPL__ENABLE_WINDOW)
11904fpl_internal fplKey fpl__Win32TranslateVirtualKey(const fpl__Win32Api *wapi, const uint64_t virtualKey) {
11905 switch (virtualKey) {
11906 case VK_BACK:
11907 return fplKey_Backspace;
11908 case VK_TAB:
11909 return fplKey_Tab;
11910
11911 case VK_CLEAR:
11912 return fplKey_Clear;
11913 case VK_RETURN:
11914 return fplKey_Return;
11915
11916 case VK_SHIFT:
11917 return fplKey_Shift;
11918 case VK_CONTROL:
11919 return fplKey_Control;
11920 case VK_MENU:
11921 return fplKey_Alt;
11922 case VK_PAUSE:
11923 return fplKey_Pause;
11924 case VK_CAPITAL:
11925 return fplKey_CapsLock;
11926
11927 case VK_ESCAPE:
11928 return fplKey_Escape;
11929 case VK_SPACE:
11930 return fplKey_Space;
11931 case VK_PRIOR:
11932 return fplKey_PageUp;
11933 case VK_NEXT:
11934 return fplKey_PageDown;
11935 case VK_END:
11936 return fplKey_End;
11937 case VK_HOME:
11938 return fplKey_Home;
11939 case VK_LEFT:
11940 return fplKey_Left;
11941 case VK_UP:
11942 return fplKey_Up;
11943 case VK_RIGHT:
11944 return fplKey_Right;
11945 case VK_DOWN:
11946 return fplKey_Down;
11947 case VK_SELECT:
11948 return fplKey_Select;
11949 case VK_PRINT:
11950 return fplKey_Print;
11951 case VK_EXECUTE:
11952 return fplKey_Execute;
11953 case VK_SNAPSHOT:
11954 return fplKey_Snapshot;
11955 case VK_INSERT:
11956 return fplKey_Insert;
11957 case VK_DELETE:
11958 return fplKey_Delete;
11959 case VK_HELP:
11960 return fplKey_Help;
11961
11962 case 0x30:
11963 return fplKey_0;
11964 case 0x31:
11965 return fplKey_1;
11966 case 0x32:
11967 return fplKey_2;
11968 case 0x33:
11969 return fplKey_3;
11970 case 0x34:
11971 return fplKey_4;
11972 case 0x35:
11973 return fplKey_5;
11974 case 0x36:
11975 return fplKey_6;
11976 case 0x37:
11977 return fplKey_7;
11978 case 0x38:
11979 return fplKey_8;
11980 case 0x39:
11981 return fplKey_9;
11982
11983 case 0x41:
11984 return fplKey_A;
11985 case 0x42:
11986 return fplKey_B;
11987 case 0x43:
11988 return fplKey_C;
11989 case 0x44:
11990 return fplKey_D;
11991 case 0x45:
11992 return fplKey_E;
11993 case 0x46:
11994 return fplKey_F;
11995 case 0x47:
11996 return fplKey_G;
11997 case 0x48:
11998 return fplKey_H;
11999 case 0x49:
12000 return fplKey_I;
12001 case 0x4A:
12002 return fplKey_J;
12003 case 0x4B:
12004 return fplKey_K;
12005 case 0x4C:
12006 return fplKey_L;
12007 case 0x4D:
12008 return fplKey_M;
12009 case 0x4E:
12010 return fplKey_N;
12011 case 0x4F:
12012 return fplKey_O;
12013 case 0x50:
12014 return fplKey_P;
12015 case 0x51:
12016 return fplKey_Q;
12017 case 0x52:
12018 return fplKey_R;
12019 case 0x53:
12020 return fplKey_S;
12021 case 0x54:
12022 return fplKey_T;
12023 case 0x55:
12024 return fplKey_U;
12025 case 0x56:
12026 return fplKey_V;
12027 case 0x57:
12028 return fplKey_W;
12029 case 0x58:
12030 return fplKey_X;
12031 case 0x59:
12032 return fplKey_Y;
12033 case 0x5A:
12034 return fplKey_Z;
12035
12036 case VK_LWIN:
12037 return fplKey_LeftSuper;
12038 case VK_RWIN:
12039 return fplKey_RightSuper;
12040 case VK_APPS:
12041 return fplKey_Apps;
12042
12043 case VK_SLEEP:
12044 return fplKey_Sleep;
12045 case VK_NUMPAD0:
12046 return fplKey_NumPad0;
12047 case VK_NUMPAD1:
12048 return fplKey_NumPad1;
12049 case VK_NUMPAD2:
12050 return fplKey_NumPad2;
12051 case VK_NUMPAD3:
12052 return fplKey_NumPad3;
12053 case VK_NUMPAD4:
12054 return fplKey_NumPad4;
12055 case VK_NUMPAD5:
12056 return fplKey_NumPad5;
12057 case VK_NUMPAD6:
12058 return fplKey_NumPad6;
12059 case VK_NUMPAD7:
12060 return fplKey_NumPad7;
12061 case VK_NUMPAD8:
12062 return fplKey_NumPad8;
12063 case VK_NUMPAD9:
12064 return fplKey_NumPad9;
12065 case VK_MULTIPLY:
12066 return fplKey_Multiply;
12067 case VK_ADD:
12068 return fplKey_Add;
12069 case VK_SEPARATOR:
12070 return fplKey_Separator;
12071 case VK_SUBTRACT:
12072 return fplKey_Substract;
12073 case VK_DECIMAL:
12074 return fplKey_Decimal;
12075 case VK_DIVIDE:
12076 return fplKey_Divide;
12077 case VK_F1:
12078 return fplKey_F1;
12079 case VK_F2:
12080 return fplKey_F2;
12081 case VK_F3:
12082 return fplKey_F3;
12083 case VK_F4:
12084 return fplKey_F4;
12085 case VK_F5:
12086 return fplKey_F5;
12087 case VK_F6:
12088 return fplKey_F6;
12089 case VK_F7:
12090 return fplKey_F7;
12091 case VK_F8:
12092 return fplKey_F8;
12093 case VK_F9:
12094 return fplKey_F9;
12095 case VK_F10:
12096 return fplKey_F10;
12097 case VK_F11:
12098 return fplKey_F11;
12099 case VK_F12:
12100 return fplKey_F12;
12101 case VK_F13:
12102 return fplKey_F13;
12103 case VK_F14:
12104 return fplKey_F14;
12105 case VK_F15:
12106 return fplKey_F15;
12107 case VK_F16:
12108 return fplKey_F16;
12109 case VK_F17:
12110 return fplKey_F17;
12111 case VK_F18:
12112 return fplKey_F18;
12113 case VK_F19:
12114 return fplKey_F19;
12115 case VK_F20:
12116 return fplKey_F20;
12117 case VK_F21:
12118 return fplKey_F21;
12119 case VK_F22:
12120 return fplKey_F22;
12121 case VK_F23:
12122 return fplKey_F23;
12123 case VK_F24:
12124 return fplKey_F24;
12125
12126 case VK_LSHIFT:
12127 return fplKey_LeftShift;
12128 case VK_RSHIFT:
12129 return fplKey_RightShift;
12130 case VK_LCONTROL:
12131 return fplKey_LeftControl;
12132 case VK_RCONTROL:
12133 return fplKey_RightControl;
12134 case VK_LMENU:
12135 return fplKey_LeftAlt;
12136 case VK_RMENU:
12137 return fplKey_RightAlt;
12138
12139 case VK_VOLUME_MUTE:
12140 return fplKey_VolumeMute;
12141 case VK_VOLUME_DOWN:
12142 return fplKey_VolumeDown;
12143 case VK_VOLUME_UP:
12144 return fplKey_VolumeUp;
12145 case VK_MEDIA_NEXT_TRACK:
12146 return fplKey_MediaNextTrack;
12147 case VK_MEDIA_PREV_TRACK:
12148 return fplKey_MediaPrevTrack;
12149 case VK_MEDIA_STOP:
12150 return fplKey_MediaStop;
12151 case VK_MEDIA_PLAY_PAUSE:
12152 return fplKey_MediaPlayPause;
12153
12154 case VK_OEM_MINUS:
12155 return fplKey_OemMinus;
12156 case VK_OEM_PLUS:
12157 return fplKey_OemPlus;
12158 case VK_OEM_COMMA:
12159 return fplKey_OemComma;
12160 case VK_OEM_PERIOD:
12161 return fplKey_OemPeriod;
12162
12163 case VK_OEM_1:
12164 return fplKey_Oem1;
12165 case VK_OEM_2:
12166 return fplKey_Oem2;
12167 case VK_OEM_3:
12168 return fplKey_Oem3;
12169 case VK_OEM_4:
12170 return fplKey_Oem4;
12171 case VK_OEM_5:
12172 return fplKey_Oem5;
12173 case VK_OEM_6:
12174 return fplKey_Oem6;
12175 case VK_OEM_7:
12176 return fplKey_Oem7;
12177 case VK_OEM_8:
12178 return fplKey_Oem8;
12179
12180 default:
12181 return fplKey_None;
12182 }
12183}
12184#endif
12185
12186fpl_internal bool fpl__Win32InitPlatform(const fplInitFlags initFlags, const fplSettings *initSettings, fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
12187 fplAssert(initState != fpl_null);
12188 fplAssert(appState != fpl_null);
12189
12190 fpl__Win32InitState *win32InitState = &initState->win32;
12191 fpl__Win32AppState *win32AppState = &appState->win32;
12192
12193 // @NOTE(final): Expect kernel32.lib to be linked always, so VirtualAlloc, LoadLibrary, CreateThread, etc. will always work.
12194
12195 // Get application instance handle
12196 win32InitState->appInstance = GetModuleHandleA(fpl_null);
12197
12198 // Query performance frequency and store it once, it will never change during runtime
12199 QueryPerformanceFrequency(&win32InitState->qpf);
12200
12201 // Get main thread infos
12202 HANDLE mainThreadHandle = GetCurrentThread();
12203 DWORD mainThreadHandleId = GetCurrentThreadId();
12204 fplThreadHandle *mainThread = &fpl__global__ThreadState.mainThread;
12205 fplClearStruct(mainThread);
12206 mainThread->id = mainThreadHandleId;
12207 mainThread->internalHandle.win32ThreadHandle = mainThreadHandle;
12209
12210 // Load windows api library
12211 if (!fpl__Win32LoadApi(&win32AppState->winApi)) {
12212 // @NOTE(final): Assume that errors are pushed on already.
12213 fpl__Win32ReleasePlatform(initState, appState);
12214 return false;
12215 }
12216
12217 // Load XInput
12218 if (initFlags & fplInitFlags_GameController) {
12219 fpl__Win32LoadXInputApi(&win32AppState->xinput.xinputApi);
12220 }
12221
12222 // Show/Hide console
12223 bool showConsole = (initFlags & fplInitFlags_Console);
12224 HWND consoleWindow = GetConsoleWindow();
12225 if (!showConsole) {
12226 if (consoleWindow != fpl_null) {
12227 win32AppState->winApi.user.ShowWindow(consoleWindow, SW_HIDE);
12228 } else {
12229 FreeConsole();
12230 }
12231 } else if (consoleWindow != fpl_null) {
12232 const fplConsoleSettings *initConsoleSettings = &initSettings->console;
12233 fplConsoleSettings *currentConsoleSettings = &appState->currentSettings.console;
12234
12235 // Setup a console title
12236 wchar_t consoleTitleBuffer[FPL_MAX_NAME_LENGTH];
12237 if (fplGetStringLength(initConsoleSettings->title) > 0) {
12238 fplUTF8StringToWideString(initConsoleSettings->title, fplGetStringLength(initConsoleSettings->title), consoleTitleBuffer, fplArrayCount(consoleTitleBuffer));
12239 } else {
12240 const wchar_t *defaultTitle = FPL__WIN32_UNNAMED_CONSOLE;
12241 lstrcpynW(consoleTitleBuffer, defaultTitle, fplArrayCount(consoleTitleBuffer));
12242 }
12243 wchar_t *windowTitle = consoleTitleBuffer;
12244 fplWideStringToUTF8String(windowTitle, lstrlenW(windowTitle), currentConsoleSettings->title, fplArrayCount(currentConsoleSettings->title));
12245 SetConsoleTitleW(windowTitle);
12246
12247 win32AppState->winApi.user.ShowWindow(consoleWindow, SW_SHOW);
12248 }
12249
12250 // Init keymap
12251# if defined(FPL__ENABLE_WINDOW)
12252 fplClearStruct(appState->window.keyMap);
12253 for (int i = 0; i < 256; ++i) {
12254 int vk = win32AppState->winApi.user.MapVirtualKeyW(MAPVK_VSC_TO_VK, i);
12255 if (vk == 0) {
12256 vk = i;
12257 }
12258 appState->window.keyMap[i] = fpl__Win32TranslateVirtualKey(&win32AppState->winApi, vk);
12259 }
12260# endif
12261
12262 // Hint for windows to know, that the application is in use always
12263# if defined(FPL__ENABLE_WINDOW)
12264 if (initSettings->window.isMonitorPowerPrevented || initSettings->window.isScreenSaverPrevented) {
12265 SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
12266 } else {
12267 SetThreadExecutionState(ES_CONTINUOUS);
12268 }
12269# endif
12270
12271 return (true);
12272}
12273
12274//
12275// Win32 Atomics
12276//
12278 FPL_MEMORY_BARRIER();
12279 _ReadBarrier();
12280}
12282 FPL_MEMORY_BARRIER();
12283 _WriteBarrier();
12284}
12286 FPL_MEMORY_BARRIER();
12287 _ReadWriteBarrier();
12288}
12289
12290fpl_platform_api uint32_t fplAtomicExchangeU32(volatile uint32_t *target, const uint32_t value) {
12291 fplAssert(target != fpl_null);
12292 uint32_t result = InterlockedExchange((volatile LONG *)target, value);
12293 return (result);
12294}
12295fpl_platform_api int32_t fplAtomicExchangeS32(volatile int32_t *target, const int32_t value) {
12296 fplAssert(target != fpl_null);
12297 int32_t result = InterlockedExchange((volatile LONG *)target, value);
12298 return (result);
12299}
12300fpl_platform_api uint64_t fplAtomicExchangeU64(volatile uint64_t *target, const uint64_t value) {
12301 fplAssert(target != fpl_null);
12302 uint64_t result = InterlockedExchange64((volatile LONG64 *)target, value);
12303 return (result);
12304}
12305fpl_platform_api int64_t fplAtomicExchangeS64(volatile int64_t *target, const int64_t value) {
12306 fplAssert(target != fpl_null);
12307 int64_t result = InterlockedExchange64((volatile LONG64 *)target, value);
12308 return (result);
12309}
12310
12311fpl_platform_api uint32_t fplAtomicFetchAndAddU32(volatile uint32_t *value, const uint32_t addend) {
12312 fplAssert(value != fpl_null);
12313 uint32_t result = InterlockedExchangeAdd((volatile LONG *)value, addend);
12314 return (result);
12315}
12316fpl_platform_api int32_t fplAtomicFetchAndAddS32(volatile int32_t *value, const int32_t addend) {
12317 fplAssert(value != fpl_null);
12318 int32_t result = InterlockedExchangeAdd((volatile LONG *)value, addend);
12319 return (result);
12320}
12321fpl_platform_api uint64_t fplAtomicFetchAndAddU64(volatile uint64_t *value, const uint64_t addend) {
12322 fplAssert(value != fpl_null);
12323 uint64_t result = InterlockedExchangeAdd64((volatile LONG64 *)value, addend);
12324 return (result);
12325}
12326fpl_platform_api int64_t fplAtomicFetchAndAddS64(volatile int64_t *value, const int64_t addend) {
12327 fplAssert(value != fpl_null);
12328 int64_t result = InterlockedExchangeAdd64((volatile LONG64 *)value, addend);
12329 return(result);
12330}
12331
12332fpl_platform_api uint32_t fplAtomicAddAndFetchU32(volatile uint32_t *value, const uint32_t addend) {
12333 fplAssert(value != fpl_null);
12334 uint32_t result = InterlockedAdd((volatile LONG *)value, addend);
12335 return (result);
12336}
12337fpl_platform_api int32_t fplAtomicAddAndFetchS32(volatile int32_t *value, const int32_t addend) {
12338 fplAssert(value != fpl_null);
12339 int32_t result = InterlockedAdd((volatile LONG *)value, addend);
12340 return (result);
12341}
12342fpl_platform_api uint64_t fplAtomicAddAndFetchU64(volatile uint64_t *value, const uint64_t addend) {
12343 fplAssert(value != fpl_null);
12344 uint64_t result = InterlockedAdd64((volatile LONG64 *)value, addend);
12345 return (result);
12346}
12347fpl_platform_api int64_t fplAtomicAddAndFetchS64(volatile int64_t *value, const int64_t addend) {
12348 fplAssert(value != fpl_null);
12349 int64_t result = InterlockedAdd64((volatile LONG64 *)value, addend);
12350 return(result);
12351}
12352
12353fpl_platform_api uint32_t fplAtomicIncrementU32(volatile uint32_t *value) {
12354 fplAssert(value != fpl_null);
12355 uint32_t result = InterlockedIncrement((volatile LONG *)value);
12356 return (result);
12357}
12358fpl_platform_api int32_t fplAtomicIncrementS32(volatile int32_t *value) {
12359 fplAssert(value != fpl_null);
12360 int32_t result = InterlockedIncrement((volatile LONG *)value);
12361 return (result);
12362}
12363fpl_platform_api uint64_t fplAtomicIncrementU64(volatile uint64_t *value) {
12364 fplAssert(value != fpl_null);
12365 uint64_t result = InterlockedIncrement64((volatile LONG64 *)value);
12366 return (result);
12367}
12368fpl_platform_api int64_t fplAtomicIncrementS64(volatile int64_t *value) {
12369 fplAssert(value != fpl_null);
12370 int64_t result = InterlockedIncrement64((volatile LONG64 *)value);
12371 return(result);
12372}
12373
12374fpl_platform_api uint32_t fplAtomicCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange) {
12375 fplAssert(dest != fpl_null);
12376 uint32_t result = InterlockedCompareExchange((volatile LONG *)dest, exchange, comparand);
12377 return (result);
12378}
12379fpl_platform_api int32_t fplAtomicCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange) {
12380 fplAssert(dest != fpl_null);
12381 int32_t result = InterlockedCompareExchange((volatile LONG *)dest, exchange, comparand);
12382 return (result);
12383}
12384fpl_platform_api uint64_t fplAtomicCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange) {
12385 fplAssert(dest != fpl_null);
12386 uint64_t result = InterlockedCompareExchange64((volatile LONG64 *)dest, exchange, comparand);
12387 return (result);
12388}
12389fpl_platform_api int64_t fplAtomicCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange) {
12390 fplAssert(dest != fpl_null);
12391 int64_t result = InterlockedCompareExchange64((volatile LONG64 *)dest, exchange, comparand);
12392 return (result);
12393}
12394
12395fpl_platform_api bool fplAtomicIsCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange) {
12396 fplAssert(dest != fpl_null);
12397 uint32_t value = InterlockedCompareExchange((volatile LONG *)dest, exchange, comparand);
12398 bool result = (value == comparand);
12399 return (result);
12400}
12401fpl_platform_api bool fplAtomicIsCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange) {
12402 fplAssert(dest != fpl_null);
12403 int32_t value = InterlockedCompareExchange((volatile LONG *)dest, exchange, comparand);
12404 bool result = (value == comparand);
12405 return (result);
12406}
12407fpl_platform_api bool fplAtomicIsCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange) {
12408 fplAssert(dest != fpl_null);
12409 uint64_t value = InterlockedCompareExchange64((volatile LONG64 *)dest, exchange, comparand);
12410 bool result = (value == comparand);
12411 return (result);
12412}
12413fpl_platform_api bool fplAtomicIsCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange) {
12414 fplAssert(dest != fpl_null);
12415 int64_t value = InterlockedCompareExchange64((volatile LONG64 *)dest, exchange, comparand);
12416 bool result = (value == comparand);
12417 return (result);
12418}
12419
12420fpl_platform_api uint32_t fplAtomicLoadU32(volatile uint32_t *source) {
12421 uint32_t result = InterlockedCompareExchange((volatile LONG *)source, 0, 0);
12422 return(result);
12423}
12424fpl_platform_api uint64_t fplAtomicLoadU64(volatile uint64_t *source) {
12425 uint64_t result = InterlockedCompareExchange64((volatile LONG64 *)source, 0, 0);
12426 return(result);
12427}
12428fpl_platform_api int32_t fplAtomicLoadS32(volatile int32_t *source) {
12429 int32_t result = InterlockedCompareExchange((volatile LONG *)source, 0, 0);
12430 return(result);
12431}
12432fpl_platform_api int64_t fplAtomicLoadS64(volatile int64_t *source) {
12433 int64_t result = InterlockedCompareExchange64((volatile LONG64 *)source, 0, 0);
12434 return(result);
12435}
12436
12437fpl_platform_api void fplAtomicStoreU32(volatile uint32_t *dest, const uint32_t value) {
12438 InterlockedExchange((volatile LONG *)dest, value);
12439}
12440fpl_platform_api void fplAtomicStoreU64(volatile uint64_t *dest, const uint64_t value) {
12441 InterlockedExchange64((volatile LONG64 *)dest, value);
12442}
12443fpl_platform_api void fplAtomicStoreS32(volatile int32_t *dest, const int32_t value) {
12444 InterlockedExchange((volatile LONG *)dest, value);
12445}
12446fpl_platform_api void fplAtomicStoreS64(volatile int64_t *dest, const int64_t value) {
12447 InterlockedExchange64((volatile LONG64 *)dest, value);
12448}
12449
12450//
12451// Win32 OS
12452//
12453fpl_internal const char *fpl__Win32GetVersionName(DWORD major, DWORD minor) {
12454 const char *result;
12455 if (major == 5 && minor == 0) {
12456 result = "Windows 2000";
12457 } else if (major == 5 && minor == 1) {
12458 result = "Windows XP";
12459 } else if (major == 5 && minor == 2) {
12460 result = "Windows XP";
12461 } else if (major == 6 && minor == 0) {
12462 result = "Windows Vista";
12463 } else if (major == 6 && minor == 1) {
12464 result = "Windows 7";
12465 } else if (major == 6 && minor == 2) {
12466 result = "Windows 8";
12467 } else if (major == 6 && minor == 3) {
12468 result = "Windows 8.1";
12469 } else if (major == 10) {
12470 result = "Windows 10";
12471 } else {
12472 result = "Windows";
12473 }
12474 return(result);
12475}
12476
12477#define FPL__FUNC_NTDLL_RtlGetVersion(name) DWORD WINAPI name(PRTL_OSVERSIONINFOW lpVersionInformation)
12478typedef FPL__FUNC_NTDLL_RtlGetVersion(fpl__func_ntdll_RtlGetVersionProc);
12479#define FPL__FUNC_KERNEL32_GetVersion(name) DWORD WINAPI name()
12480typedef FPL__FUNC_KERNEL32_GetVersion(fpl__func_kernel32_GetVersion);
12481#define FPL__FUNC_KERNEL32_GetVersionExW(name) BOOL WINAPI name(LPOSVERSIONINFOEXW lpVersionInfo)
12482typedef FPL__FUNC_KERNEL32_GetVersionExW(fpl__func_kernel32_GetVersionExW);
12484 FPL__CheckArgumentNull(outInfos, false);
12485
12486 fplClearStruct(outInfos);
12487
12488 // @NOTE(final): Prefer RtlGetVersion always, because MS might decide to totally remove GetVersion() and GetVersionEx()
12489 HMODULE ntdllModule = GetModuleHandleA("ntdll");
12490 fpl__func_ntdll_RtlGetVersionProc *rtlGetVersionProc = (fpl__func_ntdll_RtlGetVersionProc *)GetProcAddress(ntdllModule, "RtlGetVersion");
12491 if (rtlGetVersionProc != fpl_null) {
12492 RTL_OSVERSIONINFOW info = fplZeroInit;
12493 info.dwOSVersionInfoSize = sizeof(info);
12494 if (rtlGetVersionProc(&info) == 0) {
12495 fplS32ToString((int32_t)info.dwMajorVersion, outInfos->osVersion.major, fplArrayCount(outInfos->osVersion.major));
12496 fplS32ToString((int32_t)info.dwMinorVersion, outInfos->osVersion.minor, fplArrayCount(outInfos->osVersion.minor));
12497 fplS32ToString(0, outInfos->osVersion.fix, fplArrayCount(outInfos->osVersion.fix));
12498 fplS32ToString((int32_t)info.dwBuildNumber, outInfos->osVersion.build, fplArrayCount(outInfos->osVersion.build));
12499 fplStringFormat(outInfos->osVersion.fullName, fplArrayCount(outInfos->osVersion.fullName), "%u.%u.%u.%u", info.dwMajorVersion, info.dwMinorVersion, 0, info.dwBuildNumber);
12500 const char *versionName = fpl__Win32GetVersionName(info.dwMajorVersion, info.dwMinorVersion);
12501 fplCopyString(versionName, outInfos->osName, fplArrayCount(outInfos->osName));
12502 return(true);
12503 }
12504 }
12505
12506 // @NOTE(final): GetVersion() and GetVersionExA() is deprecated as of windows 8.1, so we load it manually always
12507 HMODULE kernelLib = LoadLibraryA("kernel32.dll");
12508 if (kernelLib == fpl_null) {
12509 FPL__ERROR(FPL__MODULE_WIN32, "Kernel32 library could not be loaded");
12510 return false;
12511 }
12512 fpl__func_kernel32_GetVersion *getVersionProc = (fpl__func_kernel32_GetVersion *)GetProcAddress(kernelLib, "GetVersion");
12513 fpl__func_kernel32_GetVersionExW *getVersionExProc = (fpl__func_kernel32_GetVersionExW *)GetProcAddress(kernelLib, "GetVersionExW");
12514 FreeLibrary(kernelLib);
12515
12516 if (getVersionExProc != fpl_null) {
12517 OSVERSIONINFOEXW infoEx = fplZeroInit;
12518 infoEx.dwOSVersionInfoSize = sizeof(infoEx);
12519 if (getVersionExProc(&infoEx) == TRUE) {
12520 fplS32ToString((int32_t)infoEx.dwMajorVersion, outInfos->osVersion.major, fplArrayCount(outInfos->osVersion.major));
12521 fplS32ToString((int32_t)infoEx.dwMinorVersion, outInfos->osVersion.minor, fplArrayCount(outInfos->osVersion.minor));
12522 fplS32ToString(0, outInfos->osVersion.fix, fplArrayCount(outInfos->osVersion.fix));
12523 fplS32ToString((int32_t)infoEx.dwBuildNumber, outInfos->osVersion.build, fplArrayCount(outInfos->osVersion.build));
12524 fplStringFormat(outInfos->osVersion.fullName, fplArrayCount(outInfos->osVersion.fullName), "%u.%u.%u.%u", infoEx.dwMajorVersion, infoEx.dwMinorVersion, 0, infoEx.dwBuildNumber);
12525 const char *versionName = fpl__Win32GetVersionName(infoEx.dwMajorVersion, infoEx.dwMinorVersion);
12526 fplCopyString(versionName, outInfos->osName, fplArrayCount(outInfos->osName));
12527 return(true);
12528 }
12529 }
12530
12531 if (getVersionProc != fpl_null) {
12532 DWORD dwVersion = getVersionProc();
12533 if (dwVersion > 0) {
12534 DWORD major = (DWORD)(LOBYTE(LOWORD(dwVersion)));
12535 DWORD minor = (DWORD)(HIBYTE(LOWORD(dwVersion)));
12536 DWORD build = 0;
12537 if (dwVersion < 0x80000000) {
12538 build = (DWORD)((DWORD)(HIWORD(dwVersion)));
12539 }
12540 fplS32ToString((int32_t)major, outInfos->osVersion.major, fplArrayCount(outInfos->osVersion.major));
12541 fplS32ToString((int32_t)minor, outInfos->osVersion.minor, fplArrayCount(outInfos->osVersion.minor));
12542 fplS32ToString(0, outInfos->osVersion.fix, fplArrayCount(outInfos->osVersion.fix));
12543 fplS32ToString((int32_t)build, outInfos->osVersion.build, fplArrayCount(outInfos->osVersion.build));
12544 fplStringFormat(outInfos->osVersion.fullName, fplArrayCount(outInfos->osVersion.fullName), "%u.%u.%u.%u", major, minor, 0, build);
12545 const char *versionName = fpl__Win32GetVersionName(major, minor);
12546 fplCopyString(versionName, outInfos->osName, fplArrayCount(outInfos->osName));
12547 return(true);
12548 }
12549 }
12550
12551 return(false);
12552}
12553
12554#define FPL__FUNC_ADV32_GetUserNameW(name) BOOL WINAPI name(LPWSTR lpBuffer, LPDWORD pcbBuffer)
12555typedef FPL__FUNC_ADV32_GetUserNameW(fpl__func_adv32_GetUserNameW);
12556fpl_platform_api size_t fplSessionGetUsername(char *nameBuffer, const size_t maxNameBufferLen) {
12557 const char *libName = "advapi32.dll";
12558 HMODULE adv32Lib = LoadLibraryA(libName);
12559 if (adv32Lib == fpl_null) {
12560 FPL__ERROR(FPL__MODULE_WIN32, "Failed loading library '%s'", libName);
12561 return false;
12562 }
12563 fpl__func_adv32_GetUserNameW *getUserNameProc = (fpl__func_adv32_GetUserNameW *)GetProcAddress(adv32Lib, "GetUserNameW");
12564 size_t result = 0;
12565 if (getUserNameProc != fpl_null) {
12566 wchar_t wideBuffer[FPL_MAX_BUFFER_LENGTH];
12567 DWORD size = (DWORD)fplArrayCount(wideBuffer);
12568 if (getUserNameProc(wideBuffer, &size) == TRUE) {
12569 result = fplWideStringToUTF8String(wideBuffer, size, nameBuffer, maxNameBufferLen);
12570 }
12571 }
12572 FreeLibrary(adv32Lib);
12573 return(result);
12574}
12575
12576//
12577// Win32 Hardware
12578//
12580 SYSTEM_INFO sysInfo = fplZeroInit;
12581 GetSystemInfo(&sysInfo);
12582 // @NOTE(final): For now this returns the number of logical processors, which is the actual core count in most cases.
12583 size_t result = sysInfo.dwNumberOfProcessors;
12584 return(result);
12585}
12586
12587#define FPL__WIN32_PROCESSOR_ARCHITECTURE_ARM64 12
12589 fplCPUArchType result;
12590 SYSTEM_INFO sysInfo = fplZeroInit;
12591 BOOL isWow64;
12592 if (IsWow64Process(GetCurrentProcess(), &isWow64)) {
12593 if (isWow64)
12594 GetNativeSystemInfo(&sysInfo);
12595 else
12596 GetSystemInfo(&sysInfo);
12597 } else {
12598 GetSystemInfo(&sysInfo);
12599 }
12600 switch (sysInfo.wProcessorArchitecture) {
12601 case PROCESSOR_ARCHITECTURE_AMD64:
12602 result = fplCPUArchType_x86_64;
12603 break;
12604 case PROCESSOR_ARCHITECTURE_IA64:
12605 result = fplCPUArchType_x64;
12606 break;
12607 case PROCESSOR_ARCHITECTURE_ARM:
12608 result = fplCPUArchType_Arm32;
12609 break;
12610 case FPL__WIN32_PROCESSOR_ARCHITECTURE_ARM64:
12611 result = fplCPUArchType_Arm64;
12612 break;
12613 case PROCESSOR_ARCHITECTURE_UNKNOWN:
12614 result = fplCPUArchType_Unknown;
12615 break;
12616 case PROCESSOR_ARCHITECTURE_INTEL:
12617 default:
12618 result = fplCPUArchType_x86;
12619 break;
12620 }
12621 return(result);
12622}
12623
12624#define FPL__FUNC_WIN32_KERNEL32_GetPhysicallyInstalledSystemMemory(name) BOOL WINAPI name(PULONGLONG TotalMemoryInKilobytes)
12625typedef FPL__FUNC_WIN32_KERNEL32_GetPhysicallyInstalledSystemMemory(fpl__win32_kernel_func_GetPhysicallyInstalledSystemMemory);
12627 FPL__CheckArgumentNull(outInfos, false);
12628 bool result = false;
12629
12630 HMODULE kernel32lib = LoadLibraryA("kernel32.dll");
12631 if (kernel32lib == fpl_null) {
12632 return false;
12633 }
12634 fpl__win32_kernel_func_GetPhysicallyInstalledSystemMemory *getPhysicallyInstalledSystemMemory = (fpl__win32_kernel_func_GetPhysicallyInstalledSystemMemory *)GetProcAddress(kernel32lib, "GetPhysicallyInstalledSystemMemory");
12635 FreeLibrary(kernel32lib);
12636
12637 ULONGLONG installedMemorySize = 0;
12638 if (getPhysicallyInstalledSystemMemory != fpl_null) {
12639 getPhysicallyInstalledSystemMemory(&installedMemorySize);
12640 }
12641
12642 SYSTEM_INFO systemInfo = fplZeroInit;
12643 GetSystemInfo(&systemInfo);
12644
12645 MEMORYSTATUSEX statex = fplZeroInit;
12646 statex.dwLength = sizeof(statex);
12647
12648 if (GlobalMemoryStatusEx(&statex)) {
12649 fplClearStruct(outInfos);
12650 outInfos->installedPhysicalSize = installedMemorySize * 1024ull;
12651 outInfos->totalPhysicalSize = statex.ullTotalPhys;
12652 outInfos->freePhysicalSize = statex.ullAvailPhys;
12653 outInfos->totalCacheSize = statex.ullTotalVirtual;
12654 outInfos->freeCacheSize = statex.ullAvailVirtual;
12655 outInfos->pageSize = systemInfo.dwPageSize;
12656 if (outInfos->pageSize > 0) {
12657 outInfos->totalPageCount = statex.ullTotalPageFile / outInfos->pageSize;
12658 outInfos->freePageCount = statex.ullAvailPageFile / outInfos->pageSize;
12659 }
12660 result = true;
12661 }
12662 return(result);
12663}
12664
12665//
12666// Win32 Threading
12667//
12668fpl_internal DWORD WINAPI fpl__Win32ThreadProc(void *data) {
12669 fplThreadHandle *thread = (fplThreadHandle *)data;
12670 fplAssert(thread != fpl_null);
12671 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Running);
12672
12673 fplThreadParameters parameters = thread->parameters;
12674
12675
12676
12677 if (parameters.runFunc != fpl_null) {
12678 parameters.runFunc(thread, parameters.userData);
12679 }
12680
12681 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopping);
12682 HANDLE handle = thread->internalHandle.win32ThreadHandle;
12683 if (handle != fpl_null) {
12684 CloseHandle(handle);
12685 }
12686 thread->isValid = false;
12687 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopped);
12688 ExitThread(0);
12689}
12690
12692 DWORD threadId = GetCurrentThreadId();
12693 uint32_t result = (uint32_t)threadId;
12694 return(result);
12695}
12696
12697fpl_internal bool fpl__Win32SetThreadPriority(HANDLE threadHandle, const fplThreadPriority newPriority) {
12698 int win32Priority = 0;
12699 switch (newPriority) {
12701 win32Priority = THREAD_PRIORITY_IDLE;
12702 break;
12704 win32Priority = THREAD_PRIORITY_LOWEST;
12705 break;
12707 win32Priority = THREAD_PRIORITY_NORMAL;
12708 break;
12710 win32Priority = THREAD_PRIORITY_HIGHEST;
12711 break;
12713 win32Priority = THREAD_PRIORITY_TIME_CRITICAL;
12714 break;
12715 default:
12716 FPL__ERROR("Threading", "The thread priority %d is not supported", newPriority);
12717 return(false);
12718 }
12719 bool result = SetThreadPriority(threadHandle, win32Priority) == TRUE;
12720 return(result);
12721}
12722
12724 FPL__CheckArgumentNull(parameters, fpl_null);
12725 FPL__CheckArgumentNull(parameters->runFunc, fpl_null);
12726 fplThreadHandle *result = fpl_null;
12727 fplThreadHandle *thread = fpl__GetFreeThread();
12728 if (thread != fpl_null) {
12729 DWORD creationFlags = 0;
12730 DWORD threadId = 0;
12731 SIZE_T stackSize = parameters->stackSize;
12732 thread->parameters = *parameters;
12734 HANDLE handle = CreateThread(fpl_null, stackSize, fpl__Win32ThreadProc, thread, creationFlags, &threadId);
12735 if (handle != fpl_null) {
12736 fpl__Win32SetThreadPriority(handle, thread->parameters.priority);
12737 thread->isValid = true;
12738 thread->id = threadId;
12739 thread->internalHandle.win32ThreadHandle = handle;
12740 result = thread;
12741 } else {
12742 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating thread, error code: %d", GetLastError());
12743 }
12744 } else {
12745 FPL__ERROR(FPL__MODULE_THREADING, "All %d threads are in use, you cannot create until you free one", FPL_MAX_THREAD_COUNT);
12746 }
12747 return(result);
12748}
12749
12751 FPL__CheckArgumentNull(runFunc, fpl_null);
12752 fplThreadParameters parameters = fplZeroInit;
12754 parameters.runFunc = runFunc;
12755 parameters.userData = data;
12756 fplThreadHandle *result = fplThreadCreateWithParameters(&parameters);
12757 return(result);
12758}
12759
12760fpl_internal fplThreadPriority fpl__Win32MapNativeThreadPriority(const int win32ThreadPriority) {
12761 switch (win32ThreadPriority) {
12762 case THREAD_PRIORITY_IDLE:
12764 case THREAD_PRIORITY_LOWEST:
12765 case THREAD_PRIORITY_BELOW_NORMAL:
12766 return fplThreadPriority_Low;
12767 case THREAD_PRIORITY_NORMAL:
12769 case THREAD_PRIORITY_ABOVE_NORMAL:
12770 case THREAD_PRIORITY_HIGHEST:
12772 case THREAD_PRIORITY_TIME_CRITICAL:
12774 default:
12776 }
12777}
12778
12780 FPL__CheckArgumentNull(thread, fplThreadPriority_Unknown);
12782 if (thread->isValid && thread->internalHandle.win32ThreadHandle != fpl_null) {
12783 HANDLE threadHandle = thread->internalHandle.win32ThreadHandle;
12784 int win32ThreadPriority = GetThreadPriority(threadHandle);
12785 result = fpl__Win32MapNativeThreadPriority(win32ThreadPriority);
12786 }
12787 return(result);
12788}
12789
12791 FPL__CheckArgumentNull(thread, false);
12792 bool result = false;
12793 if (thread->isValid && thread->internalHandle.win32ThreadHandle != fpl_null) {
12794 HANDLE threadHandle = thread->internalHandle.win32ThreadHandle;
12795 result = fpl__Win32SetThreadPriority(threadHandle, newPriority);
12796 }
12797 return(result);
12798}
12799
12800fpl_platform_api void fplThreadSleep(const uint32_t milliseconds) {
12801 Sleep((DWORD)milliseconds);
12802}
12803
12805 YieldProcessor();
12806 return(true);
12807}
12808
12810 FPL__CheckArgumentNull(thread, false);
12811 fplThreadState state = fplGetThreadState(thread);
12812 if (thread->isValid && (state != fplThreadState_Stopped && state != fplThreadState_Stopping)) {
12813 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopping);
12814 HANDLE handle = thread->internalHandle.win32ThreadHandle;
12815 if (handle != fpl_null) {
12816 TerminateThread(handle, 0);
12817 CloseHandle(handle);
12818 }
12819 thread->isValid = false;
12820 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopped);
12821 return true;
12822 } else {
12823 return false;
12824 }
12825}
12826
12828 FPL__CheckArgumentNull(thread, false);
12829 bool result;
12831 if (thread->internalHandle.win32ThreadHandle == fpl_null) {
12832 FPL__ERROR(FPL__MODULE_THREADING, "Win32 thread handle are not allowed to be null");
12833 return false;
12834 }
12835 HANDLE handle = thread->internalHandle.win32ThreadHandle;
12836 DWORD t = timeout == FPL_TIMEOUT_INFINITE ? INFINITE : timeout;
12837 result = (WaitForSingleObject(handle, t) == WAIT_OBJECT_0);
12838 } else {
12839 result = true;
12840 }
12841 return(result);
12842}
12843
12844fpl_platform_api bool fplThreadWaitForAll(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
12845 bool result = fpl__Win32ThreadWaitForMultiple(threads, count, stride, timeout, true);
12846 return(result);
12847}
12848
12849fpl_platform_api bool fplThreadWaitForAny(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
12850 bool result = fpl__Win32ThreadWaitForMultiple(threads, count, stride, timeout, false);
12851 return(result);
12852}
12853
12855 FPL__CheckArgumentNull(mutex, false);
12856 if (mutex->isValid) {
12857 FPL__ERROR(FPL__MODULE_THREADING, "Mutex '%p' is already initialized", mutex);
12858 return false;
12859 }
12860 fplClearStruct(mutex);
12861 mutex->isValid = true;
12862 fplAssert(sizeof(mutex->internalHandle.win32CriticalSection) >= sizeof(CRITICAL_SECTION));
12863 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
12864 InitializeCriticalSection(critSection);
12865 return true;
12866}
12867
12869 FPL__CheckArgumentNullNoRet(mutex);
12870 if (mutex->isValid) {
12871 fplAssert(sizeof(mutex->internalHandle.win32CriticalSection) >= sizeof(CRITICAL_SECTION));
12872 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
12873 DeleteCriticalSection(critSection);
12874 fplClearStruct(mutex);
12875 }
12876}
12877
12879 FPL__CheckArgumentNull(mutex, false);
12880 if (!mutex->isValid) {
12881 FPL__ERROR(FPL__MODULE_THREADING, "Mutex parameter must be valid");
12882 return false;
12883 }
12884 fplAssert(sizeof(mutex->internalHandle.win32CriticalSection) >= sizeof(CRITICAL_SECTION));
12885 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
12886 EnterCriticalSection(critSection);
12887 return true;
12888}
12889
12891 FPL__CheckArgumentNull(mutex, false);
12892 if (!mutex->isValid) {
12893 FPL__ERROR(FPL__MODULE_THREADING, "Mutex parameter must be valid");
12894 return false;
12895 }
12896 fplAssert(sizeof(mutex->internalHandle.win32CriticalSection) >= sizeof(CRITICAL_SECTION));
12897 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
12898 bool result = TryEnterCriticalSection(critSection) == TRUE;
12899 return(result);
12900}
12901
12903 FPL__CheckArgumentNull(mutex, false);
12904 if (!mutex->isValid) {
12905 FPL__ERROR(FPL__MODULE_THREADING, "Mutex parameter must be valid");
12906 return false;
12907 }
12908 fplAssert(sizeof(mutex->internalHandle.win32CriticalSection) >= sizeof(CRITICAL_SECTION));
12909 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
12910 LeaveCriticalSection(critSection);
12911 return true;
12912}
12913
12914fpl_platform_api bool fplSignalInit(fplSignalHandle *signal, const fplSignalValue initialValue) {
12915 FPL__CheckArgumentNull(signal, false);
12916 if (signal->isValid) {
12917 FPL__ERROR(FPL__MODULE_THREADING, "Signal '%p' is already initialized", signal);
12918 return false;
12919 }
12920 HANDLE handle = CreateEventA(fpl_null, FALSE, (initialValue == fplSignalValue_Set) ? TRUE : FALSE, fpl_null);
12921 if (handle == fpl_null) {
12922 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating signal (Win32 event): %d", GetLastError());
12923 return false;
12924 }
12925 fplClearStruct(signal);
12926 signal->isValid = true;
12927 signal->internalHandle.win32EventHandle = handle;
12928 return(true);
12929}
12930
12932 FPL__CheckArgumentNullNoRet(signal);
12933 if (signal->internalHandle.win32EventHandle != fpl_null) {
12934 HANDLE handle = signal->internalHandle.win32EventHandle;
12935 CloseHandle(handle);
12936 fplClearStruct(signal);
12937 }
12938}
12939
12941 FPL__CheckArgumentNull(signal, false);
12942 if (signal->internalHandle.win32EventHandle == fpl_null) {
12943 FPL__ERROR(FPL__MODULE_THREADING, "Signal handle are not allowed to be null");
12944 return false;
12945 }
12946 HANDLE handle = signal->internalHandle.win32EventHandle;
12947 DWORD t = timeout == FPL_TIMEOUT_INFINITE ? INFINITE : timeout;
12948 bool result = (WaitForSingleObject(handle, t) == WAIT_OBJECT_0);
12949 return(result);
12950}
12951
12952fpl_platform_api bool fplSignalWaitForAll(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
12953 bool result = fpl__Win32SignalWaitForMultiple(signals, count, stride, timeout, true);
12954 return(result);
12955}
12956
12957fpl_platform_api bool fplSignalWaitForAny(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
12958 bool result = fpl__Win32SignalWaitForMultiple(signals, count, stride, timeout, false);
12959 return(result);
12960}
12961
12963 FPL__CheckArgumentNull(signal, false);
12964 if (signal->internalHandle.win32EventHandle == fpl_null) {
12965 FPL__ERROR(FPL__MODULE_THREADING, "Signal handle are not allowed to be null");
12966 return false;
12967 }
12968 HANDLE handle = signal->internalHandle.win32EventHandle;
12969 bool result = SetEvent(handle) == TRUE;
12970 return(result);
12971}
12972
12974 FPL__CheckArgumentNull(signal, false);
12975 if (signal->internalHandle.win32EventHandle == fpl_null) {
12976 FPL__ERROR(FPL__MODULE_THREADING, "Signal handle are not allowed to be null");
12977 return false;
12978 }
12979 HANDLE handle = signal->internalHandle.win32EventHandle;
12980 bool result = ResetEvent(handle) == TRUE;
12981 return(result);
12982}
12983
12985 FPL__CheckArgumentNull(condition, false);
12986 fplClearStruct(condition);
12987 fplAssert(sizeof(condition->internalHandle.win32Condition) == sizeof(CONDITION_VARIABLE));
12988 CONDITION_VARIABLE *condVar = (CONDITION_VARIABLE *)&condition->internalHandle.win32Condition;
12989 InitializeConditionVariable(condVar);
12990 condition->isValid = true;
12991 return true;
12992}
12993
12995 FPL__CheckArgumentNullNoRet(condition);
12996 if (condition->isValid) {
12997 fplClearStruct(condition);
12998 }
12999}
13000
13002 FPL__CheckArgumentNull(condition, false);
13003 FPL__CheckArgumentNull(mutex, false);
13004 if (!condition->isValid) {
13005 FPL__ERROR(FPL__MODULE_THREADING, "Condition '%p' is not valid", condition);
13006 return false;
13007 }
13008 if (!mutex->isValid) {
13009 FPL__ERROR(FPL__MODULE_THREADING, "Mutex '%p' is not valid", mutex);
13010 return false;
13011 }
13012 DWORD t = timeout == FPL_TIMEOUT_INFINITE ? INFINITE : timeout;
13013 CRITICAL_SECTION *critSection = (CRITICAL_SECTION *)&mutex->internalHandle.win32CriticalSection;
13014 CONDITION_VARIABLE *condVar = (CONDITION_VARIABLE *)&condition->internalHandle.win32Condition;
13015 bool result = SleepConditionVariableCS(condVar, critSection, t) != 0;
13016 return(result);
13017}
13018
13020 FPL__CheckArgumentNull(condition, false);
13021 if (!condition->isValid) {
13022 FPL__ERROR(FPL__MODULE_THREADING, "Condition '%p' is not valid", condition);
13023 return false;
13024 }
13025 CONDITION_VARIABLE *critSection = (CONDITION_VARIABLE *)&condition->internalHandle.win32Condition;
13026 WakeConditionVariable(critSection);
13027 return true;
13028}
13029
13031 FPL__CheckArgumentNull(condition, false);
13032 if (!condition->isValid) {
13033 FPL__ERROR(FPL__MODULE_THREADING, "Condition '%p' is not valid", condition);
13034 return false;
13035 }
13036 CONDITION_VARIABLE *critSection = (CONDITION_VARIABLE *)&condition->internalHandle.win32Condition;
13037 WakeAllConditionVariable(critSection);
13038 return true;
13039}
13040
13041fpl_platform_api bool fplSemaphoreInit(fplSemaphoreHandle *semaphore, const uint32_t initialValue) {
13042 FPL__CheckArgumentNull(semaphore, false);
13043 FPL__CheckArgumentMax(initialValue, INT32_MAX, false);
13044 if (semaphore->isValid) {
13045 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is already initialized", semaphore);
13046 return false;
13047 }
13048 HANDLE handle = CreateSemaphoreA(fpl_null, (LONG)initialValue, INT32_MAX, fpl_null);
13049 if (handle == fpl_null) {
13050 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating semaphore");
13051 return false;
13052 }
13053 fplClearStruct(semaphore);
13054 semaphore->isValid = true;
13055 semaphore->internalHandle.win32.handle = handle;
13056 semaphore->internalHandle.win32.value = (int32_t)initialValue;
13057 return true;
13058}
13059
13061 FPL__CheckArgumentNullNoRet(semaphore);
13062 if (semaphore->isValid) {
13063 CloseHandle(semaphore->internalHandle.win32.handle);
13064 fplClearStruct(semaphore);
13065 }
13066}
13067
13069 FPL__CheckArgumentNull(semaphore, false);
13070 if (!semaphore->isValid) {
13071 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
13072 return false;
13073 }
13074 DWORD t = timeout == FPL_TIMEOUT_INFINITE ? INFINITE : timeout;
13075 bool result = false;
13076 if (WaitForSingleObject(semaphore->internalHandle.win32.handle, timeout) == WAIT_OBJECT_0) {
13077 fplAtomicFetchAndAddS32(&semaphore->internalHandle.win32.value, -1);
13078 result = true;
13079 }
13080 return(result);
13081}
13082
13084 FPL__CheckArgumentNull(semaphore, false);
13085 if (!semaphore->isValid) {
13086 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
13087 return false;
13088 }
13089 bool result = false;
13090 if (WaitForSingleObject(semaphore->internalHandle.win32.handle, 0) == WAIT_OBJECT_0) {
13091 fplAtomicFetchAndAddS32(&semaphore->internalHandle.win32.value, -1);
13092 result = true;
13093 }
13094 return(result);
13095}
13096
13098 FPL__CheckArgumentNull(semaphore, false);
13099 if (!semaphore->isValid) {
13100 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
13101 return false;
13102 }
13103 int32_t result = fplAtomicLoadS32(&semaphore->internalHandle.win32.value);
13104 return(result);
13105}
13106
13108 FPL__CheckArgumentNull(semaphore, false);
13109 if (!semaphore->isValid) {
13110 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
13111 return false;
13112 }
13113 bool result = true;
13114 int32_t prevValue = fplAtomicFetchAndAddS32(&semaphore->internalHandle.win32.value, 1);
13115 if (ReleaseSemaphore(semaphore->internalHandle.win32.handle, 1, fpl_null) == FALSE) {
13116 // Restore value when it fails
13117 FPL__ERROR(FPL__MODULE_THREADING, "Failed releasing the semaphore '%p'", semaphore);
13118 fplAtomicStoreS32(&semaphore->internalHandle.win32.value, prevValue);
13119 result = false;
13120 }
13121 return(result);
13122}
13123
13124//
13125// Win32 Console
13126//
13127fpl_platform_api void fplConsoleOut(const char *text) {
13128 DWORD charsToWrite = (DWORD)fplGetStringLength(text);
13129 DWORD writtenChars = 0;
13130 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
13131 wchar_t wideBuffer[FPL_MAX_BUFFER_LENGTH];
13132 fplUTF8StringToWideString(text, charsToWrite, wideBuffer, fplArrayCount(wideBuffer));
13133 WriteConsoleW(handle, wideBuffer, charsToWrite, &writtenChars, fpl_null);
13134}
13135
13136fpl_platform_api void fplConsoleError(const char *text) {
13137 DWORD charsToWrite = (DWORD)fplGetStringLength(text);
13138 DWORD writtenChars = 0;
13139 HANDLE handle = GetStdHandle(STD_ERROR_HANDLE);
13140 wchar_t wideBuffer[FPL_MAX_BUFFER_LENGTH];
13141 fplUTF8StringToWideString(text, charsToWrite, wideBuffer, fplArrayCount(wideBuffer));
13142 WriteConsoleW(handle, wideBuffer, charsToWrite, &writtenChars, fpl_null);
13143}
13144
13146 HANDLE handle = GetStdHandle(STD_INPUT_HANDLE);
13147 DWORD savedMode;
13148 GetConsoleMode(handle, &savedMode);
13149 SetConsoleMode(handle, ENABLE_PROCESSED_INPUT);
13150 char result = 0;
13151 if (WaitForSingleObject(handle, INFINITE) == WAIT_OBJECT_0) {
13152 DWORD charsRead = 0;
13153 char inputBuffer[2] = fplZeroInit;
13154 if (ReadFile(handle, inputBuffer, 1, &charsRead, fpl_null) != 0) {
13155 result = inputBuffer[0];
13156 }
13157 }
13158 SetConsoleMode(handle, savedMode);
13159 return (result);
13160}
13161
13162//
13163// Win32 Memory
13164//
13165fpl_platform_api void *fplMemoryAllocate(const size_t size) {
13166 FPL__CheckArgumentZero(size, fpl_null);
13167 void *result = VirtualAlloc(fpl_null, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
13168 if (result == fpl_null) {
13169 FPL__ERROR(FPL__MODULE_MEMORY, "Failed allocating memory of %xu bytes", size);
13170 }
13171 return(result);
13172}
13173
13174fpl_platform_api void fplMemoryFree(void *ptr) {
13175 FPL__CheckArgumentNullNoRet(ptr);
13176 VirtualFree(ptr, 0, MEM_RELEASE);
13177}
13178
13179//
13180// Win32 Files
13181//
13182fpl_internal const uint64_t FPL__WIN32_TICKS_PER_SEC = 10000000ULL;
13183fpl_internal const uint64_t FPL__WIN32_UNIX_EPOCH_DIFFERENCE = 11644473600ULL;
13184
13185fpl_internal fplFileTimeStamp fpl__Win32ConvertFileTimeToUnixTimestamp(const FILETIME *fileTime) {
13186 // Ticks are defined in 100 ns = 10000000 secs
13187 // Windows ticks starts at 1601-01-01T00:00:00Z
13188 // Unix secs starts at 1970-01-01T00:00:00Z
13189 fplFileTimeStamp result = 0;
13190 if (fileTime != fpl_null && (fileTime->dwLowDateTime > 0 || fileTime->dwHighDateTime > 0)) {
13191 // Convert to SYSTEMTIME and remove milliseconds
13192 SYSTEMTIME sysTime;
13193 FileTimeToSystemTime(fileTime, &sysTime);
13194 sysTime.wMilliseconds = 0; // Really important, because unix-timestamps does not support milliseconds
13195
13196 // Reconvert to FILETIME to account for removed milliseconds
13197 FILETIME withoutMSecs;
13198 SystemTimeToFileTime(&sysTime, &withoutMSecs);
13199
13200 // Convert to large integer so we can access U64 directly
13201 ULARGE_INTEGER ticks;
13202 ticks.LowPart = withoutMSecs.dwLowDateTime;
13203 ticks.HighPart = withoutMSecs.dwHighDateTime;
13204
13205 // Final conversion from ticks to unix-timestamp
13206 result = (ticks.QuadPart / FPL__WIN32_TICKS_PER_SEC) - FPL__WIN32_UNIX_EPOCH_DIFFERENCE;
13207 }
13208 return(result);
13209}
13210
13211fpl_internal FILETIME fpl__Win32ConvertUnixTimestampToFileTime(const fplFileTimeStamp unixTimeStamp) {
13212 // Ticks are defined in 100 ns = 10000000 secs
13213 // 100 ns = milliseconds * 10000
13214 // Windows ticks starts at 1601-01-01T00:00:00Z
13215 // Unix secs starts at 1970-01-01T00:00:00Z
13216 if (unixTimeStamp > 0) {
13217 uint64_t ticks = (unixTimeStamp + FPL__WIN32_UNIX_EPOCH_DIFFERENCE) * FPL__WIN32_TICKS_PER_SEC;
13218 FILETIME result;
13219 result.dwLowDateTime = (DWORD)ticks;
13220 result.dwHighDateTime = ticks >> 32;
13221 return(result);
13222 }
13223 FILETIME empty = fplZeroInit;
13224 return(empty);
13225}
13226
13227fpl_platform_api bool fplFileOpenBinary(const char *filePath, fplFileHandle *outHandle) {
13228 FPL__CheckArgumentNull(outHandle, false);
13229 if (filePath != fpl_null) {
13230 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
13231 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
13232 HANDLE win32FileHandle = CreateFileW(filePathWide, GENERIC_READ, FILE_SHARE_READ, fpl_null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, fpl_null);
13233 if (win32FileHandle != INVALID_HANDLE_VALUE) {
13234 fplClearStruct(outHandle);
13235 outHandle->isValid = true;
13236 outHandle->internalHandle.win32FileHandle = (void *)win32FileHandle;
13237 return true;
13238 }
13239 }
13240 return false;
13241}
13242
13243fpl_platform_api bool fplFileCreateBinary(const char *filePath, fplFileHandle *outHandle) {
13244 FPL__CheckArgumentNull(outHandle, false);
13245 if (filePath != fpl_null) {
13246 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
13247 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
13248 HANDLE win32FileHandle = CreateFileW(filePathWide, GENERIC_WRITE, FILE_SHARE_WRITE, fpl_null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, fpl_null);
13249 if (win32FileHandle != INVALID_HANDLE_VALUE) {
13250 fplClearStruct(outHandle);
13251 outHandle->isValid = true;
13252 outHandle->internalHandle.win32FileHandle = (void *)win32FileHandle;
13253 return true;
13254 }
13255 }
13256 return false;
13257}
13258
13259fpl_platform_api uint32_t fplFileReadBlock32(const fplFileHandle *fileHandle, const uint32_t sizeToRead, void *targetBuffer, const uint32_t maxTargetBufferSize) {
13260 FPL__CheckArgumentNull(fileHandle, 0);
13261 FPL__CheckArgumentZero(sizeToRead, 0);
13262 FPL__CheckArgumentNull(targetBuffer, 0);
13263 if (fileHandle->internalHandle.win32FileHandle == fpl_null) {
13264 FPL__ERROR(FPL__MODULE_FILES, "Filehandle is not opened for reading");
13265 return 0;
13266 }
13267 uint32_t result = 0;
13268 HANDLE win32FileHandle = (HANDLE)fileHandle->internalHandle.win32FileHandle;
13269 DWORD bytesRead = 0;
13270 if (ReadFile(win32FileHandle, targetBuffer, (DWORD)sizeToRead, &bytesRead, fpl_null) == TRUE) {
13271 result = bytesRead;
13272 }
13273 return(result);
13274}
13275
13276fpl_platform_api uint64_t fplFileReadBlock64(const fplFileHandle *fileHandle, const uint64_t sizeToRead, void *targetBuffer, const uint64_t maxTargetBufferSize) {
13277 FPL__CheckArgumentNull(fileHandle, 0);
13278 FPL__CheckArgumentZero(sizeToRead, 0);
13279 FPL__CheckArgumentNull(targetBuffer, 0);
13280 if (fileHandle->internalHandle.win32FileHandle == fpl_null) {
13281 FPL__ERROR(FPL__MODULE_FILES, "Filehandle is not opened for reading");
13282 return 0;
13283 }
13284 // @NOTE(final): There is no ReadFile64 function in win32, so we have to read it in chunks
13285 uint64_t result = 0;
13286 HANDLE win32FileHandle = (HANDLE)fileHandle->internalHandle.win32FileHandle;
13287 uint64_t remainingSize = sizeToRead;
13288 uint64_t bufferPos = 0;
13289 const uint64_t MaxDWORD = (uint64_t)(DWORD)-1;
13290 while (remainingSize > 0) {
13291 DWORD bytesRead = 0;
13292 uint8_t *target = (uint8_t *)targetBuffer + bufferPos;
13293 uint64_t size = fplMin(remainingSize, MaxDWORD);
13294 fplAssert(size <= MaxDWORD);
13295 if (ReadFile(win32FileHandle, target, (DWORD)size, &bytesRead, fpl_null) == TRUE) {
13296 result = bytesRead;
13297 } else {
13298 break;
13299 }
13300 remainingSize -= bytesRead;
13301 bufferPos += bytesRead;
13302 }
13303 return(result);
13304}
13305
13306fpl_platform_api uint32_t fplFileWriteBlock32(const fplFileHandle *fileHandle, void *sourceBuffer, const uint32_t sourceSize) {
13307 FPL__CheckArgumentNull(fileHandle, 0);
13308 FPL__CheckArgumentZero(sourceSize, 0);
13309 FPL__CheckArgumentNull(sourceBuffer, 0);
13310 if (fileHandle->internalHandle.win32FileHandle == fpl_null) {
13311 FPL__ERROR(FPL__MODULE_FILES, "Filehandle is not opened for writing");
13312 return 0;
13313 }
13314 HANDLE win32FileHandle = (HANDLE)fileHandle->internalHandle.win32FileHandle;
13315 uint32_t result = 0;
13316 DWORD bytesWritten = 0;
13317 if (WriteFile(win32FileHandle, sourceBuffer, (DWORD)sourceSize, &bytesWritten, fpl_null) == TRUE) {
13318 result = bytesWritten;
13319 }
13320 return(result);
13321}
13322
13323fpl_platform_api uint64_t fplFileWriteBlock64(const fplFileHandle *fileHandle, void *sourceBuffer, const uint64_t sourceSize) {
13324 FPL__CheckArgumentNull(fileHandle, 0);
13325 FPL__CheckArgumentZero(sourceSize, 0);
13326 FPL__CheckArgumentNull(sourceBuffer, 0);
13327 if (fileHandle->internalHandle.win32FileHandle == fpl_null) {
13328 FPL__ERROR(FPL__MODULE_FILES, "Filehandle is not opened for writing");
13329 return 0;
13330 }
13331 HANDLE win32FileHandle = (HANDLE)fileHandle->internalHandle.win32FileHandle;
13332 uint64_t result = 0;
13333 uint64_t bufferPos = 0;
13334 uint64_t remainingSize = sourceSize;
13335 const uint64_t MaxDWORD = (uint64_t)(DWORD)-1;
13336 while (remainingSize > 0) {
13337 uint8_t *source = (uint8_t *)sourceBuffer + bufferPos;
13338 uint64_t size = fplMin(remainingSize, MaxDWORD);
13339 fplAssert(size <= MaxDWORD);
13340 DWORD bytesWritten = 0;
13341 if (WriteFile(win32FileHandle, source, (DWORD)size, &bytesWritten, fpl_null) == TRUE) {
13342 result = bytesWritten;
13343 } else {
13344 break;
13345 }
13346 remainingSize -= bytesWritten;
13347 bufferPos += bytesWritten;
13348 }
13349 return(result);
13350}
13351
13352fpl_platform_api uint32_t fplFileSetPosition32(const fplFileHandle *fileHandle, const int32_t position, const fplFilePositionMode mode) {
13353 FPL__CheckArgumentNull(fileHandle, 0);
13354 uint32_t result = 0;
13355 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
13356 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
13357 DWORD moveMethod = FILE_BEGIN;
13358 if (mode == fplFilePositionMode_Current) {
13359 moveMethod = FILE_CURRENT;
13360 } else if (mode == fplFilePositionMode_End) {
13361 moveMethod = FILE_END;
13362 }
13363 DWORD r = 0;
13364 r = SetFilePointer(win32FileHandle, (LONG)position, fpl_null, moveMethod);
13365 result = (uint32_t)r;
13366 }
13367 return(result);
13368}
13369
13370fpl_platform_api uint64_t fplFileSetPosition64(const fplFileHandle *fileHandle, const int64_t position, const fplFilePositionMode mode) {
13371 FPL__CheckArgumentNull(fileHandle, 0);
13372 uint64_t result = 0;
13373 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
13374 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
13375 DWORD moveMethod = FILE_BEGIN;
13376 if (mode == fplFilePositionMode_Current) {
13377 moveMethod = FILE_CURRENT;
13378 } else if (mode == fplFilePositionMode_End) {
13379 moveMethod = FILE_END;
13380 }
13381 LARGE_INTEGER r = fplZeroInit;
13382 LARGE_INTEGER li;
13383 li.QuadPart = position;
13384 if (SetFilePointerEx(win32FileHandle, li, &r, moveMethod) == TRUE) {
13385 result = (uint64_t)r.QuadPart;
13386 }
13387 }
13388 return(result);
13389}
13390
13391fpl_platform_api uint32_t fplFileGetPosition32(const fplFileHandle *fileHandle) {
13392 FPL__CheckArgumentNull(fileHandle, 0);
13393 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
13394 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
13395 DWORD filePosition = SetFilePointer(win32FileHandle, 0L, fpl_null, FILE_CURRENT);
13396 if (filePosition != INVALID_SET_FILE_POINTER) {
13397 return filePosition;
13398 }
13399 }
13400 return 0;
13401}
13402
13403fpl_platform_api uint64_t fplFileGetPosition64(const fplFileHandle *fileHandle) {
13404 FPL__CheckArgumentNull(fileHandle, 0);
13405 uint64_t result = 0;
13406 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
13407 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
13408 LARGE_INTEGER r = fplZeroInit;
13409 LARGE_INTEGER li;
13410 li.QuadPart = 0;
13411 if (SetFilePointerEx(win32FileHandle, li, &r, FILE_CURRENT) == TRUE) {
13412 result = (uint64_t)r.QuadPart;
13413 }
13414 }
13415 return 0;
13416}
13417
13419 FPL__CheckArgumentNull(fileHandle, false);
13420 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
13421 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
13422 bool result = FlushFileBuffers(win32FileHandle) == TRUE;
13423 return(result);
13424 }
13425 return(false);
13426}
13427
13429 if ((fileHandle != fpl_null) && (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE)) {
13430 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
13431 CloseHandle(win32FileHandle);
13432 fplClearStruct(fileHandle);
13433 }
13434}
13435
13436fpl_platform_api uint32_t fplFileGetSizeFromPath32(const char *filePath) {
13437 uint32_t result = 0;
13438 if (filePath != fpl_null) {
13439 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
13440 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
13441 HANDLE win32FileHandle = CreateFileW(filePathWide, GENERIC_READ, FILE_SHARE_READ, fpl_null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, fpl_null);
13442 if (win32FileHandle != INVALID_HANDLE_VALUE) {
13443 DWORD fileSize = GetFileSize(win32FileHandle, fpl_null);
13444 result = (uint32_t)fileSize;
13445 CloseHandle(win32FileHandle);
13446 }
13447 }
13448 return(result);
13449}
13450
13451fpl_platform_api uint64_t fplFileGetSizeFromPath64(const char *filePath) {
13452 uint64_t result = 0;
13453 if (filePath != fpl_null) {
13454 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
13455 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
13456 HANDLE win32FileHandle = CreateFileW(filePathWide, GENERIC_READ, FILE_SHARE_READ, fpl_null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, fpl_null);
13457 if (win32FileHandle != INVALID_HANDLE_VALUE) {
13458 LARGE_INTEGER li = fplZeroInit;
13459 if (GetFileSizeEx(win32FileHandle, &li) == TRUE) {
13460 result = (uint64_t)li.QuadPart;
13461 }
13462 CloseHandle(win32FileHandle);
13463 }
13464 }
13465 return(result);
13466}
13467
13468fpl_platform_api uint32_t fplFileGetSizeFromHandle32(const fplFileHandle *fileHandle) {
13469 FPL__CheckArgumentNull(fileHandle, 0);
13470 uint32_t result = 0;
13471 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
13472 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
13473 DWORD fileSize = GetFileSize(win32FileHandle, fpl_null);
13474 result = (uint32_t)fileSize;
13475 }
13476 return(result);
13477}
13478
13479fpl_platform_api uint64_t fplFileGetSizeFromHandle64(const fplFileHandle *fileHandle) {
13480 FPL__CheckArgumentNull(fileHandle, 0);
13481 uint64_t result = 0;
13482 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
13483 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
13484 LARGE_INTEGER li = fplZeroInit;
13485 if (GetFileSizeEx(win32FileHandle, &li) == TRUE) {
13486 result = (uint64_t)li.QuadPart;
13487 }
13488 }
13489 return(result);
13490}
13491
13492fpl_platform_api bool fplFileGetTimestampsFromPath(const char *filePath, fplFileTimeStamps *outStamps) {
13493 FPL__CheckArgumentNull(outStamps, false);
13494 if (filePath != fpl_null) {
13495 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
13496 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
13497 HANDLE win32FileHandle = CreateFileW(filePathWide, GENERIC_READ, FILE_SHARE_READ, fpl_null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, fpl_null);
13498 bool result = false;
13499 if (win32FileHandle != INVALID_HANDLE_VALUE) {
13500 FILETIME times[3];
13501 if (GetFileTime(win32FileHandle, &times[0], &times[1], &times[2]) == TRUE) {
13502 fplClearStruct(outStamps);
13503 outStamps->creationTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[0]);
13504 outStamps->lastAccessTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[1]);
13505 outStamps->lastModifyTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[2]);
13506 result = true;
13507 }
13508 CloseHandle(win32FileHandle);
13509 }
13510 return(result);
13511 }
13512 return(false);
13513}
13514
13516 FPL__CheckArgumentNull(fileHandle, 0);
13517 FPL__CheckArgumentNull(outStamps, 0);
13518 if (fileHandle->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
13519 HANDLE win32FileHandle = (void *)fileHandle->internalHandle.win32FileHandle;
13520 FILETIME times[3];
13521 if (GetFileTime(win32FileHandle, &times[0], &times[1], &times[2]) == TRUE) {
13522 fplClearStruct(outStamps);
13523 outStamps->creationTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[0]);
13524 outStamps->lastAccessTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[1]);
13525 outStamps->lastModifyTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&times[2]);
13526 return(true);
13527 }
13528 }
13529 return(false);
13530}
13531
13532fpl_platform_api bool fplFileSetTimestamps(const char *filePath, const fplFileTimeStamps *timeStamps) {
13533 FPL__CheckArgumentNull(timeStamps, false);
13534 if (filePath != fpl_null) {
13535 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
13536 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
13537 HANDLE win32FileHandle = CreateFileW(filePathWide, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE | FILE_SHARE_READ, fpl_null, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, fpl_null);
13538 bool result = false;
13539 if (win32FileHandle != INVALID_HANDLE_VALUE) {
13540 FILETIME times[3];
13541 times[0] = fpl__Win32ConvertUnixTimestampToFileTime(timeStamps->creationTime);
13542 times[1] = fpl__Win32ConvertUnixTimestampToFileTime(timeStamps->lastAccessTime);
13543 times[2] = fpl__Win32ConvertUnixTimestampToFileTime(timeStamps->lastModifyTime);
13544 if (SetFileTime(win32FileHandle, &times[0], NULL, NULL) == TRUE) {
13545 return(true);
13546 }
13547 CloseHandle(win32FileHandle);
13548 }
13549 return(result);
13550 }
13551 return(false);
13552}
13553
13554fpl_platform_api bool fplFileExists(const char *filePath) {
13555 bool result = false;
13556 if (filePath != fpl_null) {
13557 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
13558 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
13559 WIN32_FIND_DATAW findData;
13560 HANDLE searchHandle = FindFirstFileW(filePathWide, &findData);
13561 if (searchHandle != INVALID_HANDLE_VALUE) {
13562 result = !(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
13563 FindClose(searchHandle);
13564 }
13565 }
13566 return(result);
13567}
13568
13569fpl_platform_api bool fplFileCopy(const char *sourceFilePath, const char *targetFilePath, const bool overwrite) {
13570 FPL__CheckArgumentNull(sourceFilePath, false);
13571 FPL__CheckArgumentNull(targetFilePath, false);
13572 wchar_t sourceFilePathWide[FPL_MAX_PATH_LENGTH];
13573 wchar_t targetFilePathWide[FPL_MAX_PATH_LENGTH];
13574 fplUTF8StringToWideString(sourceFilePath, fplGetStringLength(sourceFilePath), sourceFilePathWide, fplArrayCount(sourceFilePathWide));
13575 fplUTF8StringToWideString(sourceFilePath, fplGetStringLength(sourceFilePath), targetFilePathWide, fplArrayCount(targetFilePathWide));
13576 bool result = (CopyFileW(sourceFilePathWide, targetFilePathWide, !overwrite) == TRUE);
13577 return(result);
13578}
13579
13580fpl_platform_api bool fplFileMove(const char *sourceFilePath, const char *targetFilePath) {
13581 FPL__CheckArgumentNull(sourceFilePath, false);
13582 FPL__CheckArgumentNull(targetFilePath, false);
13583 wchar_t sourceFilePathWide[FPL_MAX_PATH_LENGTH];
13584 wchar_t targetFilePathWide[FPL_MAX_PATH_LENGTH];
13585 fplUTF8StringToWideString(sourceFilePath, fplGetStringLength(sourceFilePath), sourceFilePathWide, fplArrayCount(sourceFilePathWide));
13586 fplUTF8StringToWideString(sourceFilePath, fplGetStringLength(sourceFilePath), targetFilePathWide, fplArrayCount(targetFilePathWide));
13587 bool result = (MoveFileW(sourceFilePathWide, targetFilePathWide) == TRUE);
13588 return(result);
13589}
13590
13591fpl_platform_api bool fplFileDelete(const char *filePath) {
13592 FPL__CheckArgumentNull(filePath, false);
13593 wchar_t filePathWide[FPL_MAX_PATH_LENGTH];
13594 fplUTF8StringToWideString(filePath, fplGetStringLength(filePath), filePathWide, fplArrayCount(filePathWide));
13595 bool result = (DeleteFileW(filePathWide) == TRUE);
13596 return(result);
13597}
13598
13599fpl_platform_api bool fplDirectoryExists(const char *path) {
13600 bool result = false;
13601 if (path != fpl_null) {
13602 wchar_t pathWide[FPL_MAX_PATH_LENGTH];
13603 fplUTF8StringToWideString(path, fplGetStringLength(path), pathWide, fplArrayCount(pathWide));
13604 WIN32_FIND_DATAW findData;
13605 HANDLE searchHandle = FindFirstFileW(pathWide, &findData);
13606 if (searchHandle != INVALID_HANDLE_VALUE) {
13607 result = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) > 0;
13608 FindClose(searchHandle);
13609 }
13610 }
13611 return(result);
13612}
13613
13614fpl_platform_api bool fplDirectoriesCreate(const char *path) {
13615 FPL__CheckArgumentNull(path, false);
13616 wchar_t pathWide[FPL_MAX_PATH_LENGTH];
13617 fplUTF8StringToWideString(path, fplGetStringLength(path), pathWide, fplArrayCount(pathWide));
13618 bool result = CreateDirectoryW(pathWide, fpl_null) > 0;
13619 return(result);
13620}
13621fpl_platform_api bool fplDirectoryRemove(const char *path) {
13622 FPL__CheckArgumentNull(path, false);
13623 wchar_t pathWide[FPL_MAX_PATH_LENGTH];
13624 fplUTF8StringToWideString(path, fplGetStringLength(path), pathWide, fplArrayCount(pathWide));
13625 bool result = RemoveDirectoryW(pathWide) > 0;
13626 return(result);
13627}
13628fpl_internal void fpl__Win32FillFileEntry(const char *rootPath, const WIN32_FIND_DATAW *findData, fplFileEntry *entry) {
13629 fplAssert(findData != fpl_null);
13630 fplAssert(entry != fpl_null);
13631 fplWideStringToUTF8String(findData->cFileName, lstrlenW(findData->cFileName), entry->name, fplArrayCount(entry->name));
13633 if (findData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
13635 } else if (
13636 (findData->dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
13637 (findData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ||
13638 (findData->dwFileAttributes & FILE_ATTRIBUTE_READONLY) ||
13639 (findData->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) ||
13640 (findData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) {
13641 entry->type = fplFileEntryType_File;
13642 }
13643
13644 // @TODO(final/Win32): Win32 Read ACL for full permission detection!
13646 entry->permissions.umask = 0;
13647 if (findData->dwFileAttributes & FILE_ATTRIBUTE_NORMAL) {
13649 } else {
13650 if (findData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) {
13652 }
13653 if (findData->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) {
13655 }
13656 if (findData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {
13658 }
13662 if ((findData->dwFileAttributes & FILE_ATTRIBUTE_READONLY) || (findData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)) {
13663 entry->permissions.user &= ~fplFilePermissionFlags_CanWrite;
13664 }
13665 }
13666 if (entry->type == fplFileEntryType_File) {
13667 ULARGE_INTEGER ul;
13668 ul.LowPart = findData->nFileSizeLow;
13669 ul.HighPart = findData->nFileSizeHigh;
13670 entry->size = (size_t)ul.QuadPart;
13671 } else {
13672 entry->size = 0;
13673 }
13674 entry->timeStamps.creationTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&findData->ftCreationTime);
13675 entry->timeStamps.lastAccessTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&findData->ftLastAccessTime);
13676 entry->timeStamps.lastModifyTime = fpl__Win32ConvertFileTimeToUnixTimestamp(&findData->ftLastWriteTime);
13677}
13678fpl_platform_api bool fplDirectoryListBegin(const char *path, const char *filter, fplFileEntry *entry) {
13679 FPL__CheckArgumentNull(path, false);
13680 FPL__CheckArgumentNull(entry, false);
13681 if (fplGetStringLength(filter) == 0) {
13682 filter = "*";
13683 }
13684 char pathAndFilter[MAX_PATH + 1] = fplZeroInit;
13685 fplCopyString(path, pathAndFilter, fplArrayCount(pathAndFilter));
13686 fplEnforcePathSeparatorLen(pathAndFilter, fplArrayCount(pathAndFilter));
13687 fplStringAppend(filter, pathAndFilter, fplArrayCount(pathAndFilter));
13688 wchar_t pathAndFilterWide[MAX_PATH + 1];
13689 fplUTF8StringToWideString(pathAndFilter, fplGetStringLength(pathAndFilter), pathAndFilterWide, fplArrayCount(pathAndFilterWide));
13690 WIN32_FIND_DATAW findData;
13691 HANDLE searchHandle = FindFirstFileW(pathAndFilterWide, &findData);
13692 bool result = false;
13693 if (searchHandle != INVALID_HANDLE_VALUE) {
13694 fplClearStruct(entry);
13695 entry->internalHandle.win32FileHandle = searchHandle;
13696 entry->internalRoot.rootPath = path;
13697 entry->internalRoot.filter = filter;
13698 bool foundFirst = true;
13699 while (foundFirst) {
13700 if (lstrcmpW(findData.cFileName, L".") == 0 || lstrcmpW(findData.cFileName, L"..") == 0) {
13701 foundFirst = FindNextFileW(searchHandle, &findData) == TRUE;
13702 } else {
13703 result = true;
13704 fpl__Win32FillFileEntry(path, &findData, entry);
13705 break;
13706 }
13707 }
13708 }
13709 return(result);
13710}
13712 FPL__CheckArgumentNull(entry, false);
13713 bool result = false;
13714 if (entry->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
13715 HANDLE searchHandle = entry->internalHandle.win32FileHandle;
13716 WIN32_FIND_DATAW findData;
13717 bool foundNext;
13718 do {
13719 foundNext = FindNextFileW(searchHandle, &findData) == TRUE;
13720 if (foundNext) {
13721 if (lstrcmpW(findData.cFileName, L".") == 0 || lstrcmpW(findData.cFileName, L"..") == 0) {
13722 continue;
13723 }
13724 fpl__Win32FillFileEntry(entry->internalRoot.rootPath, &findData, entry);
13725 result = true;
13726 break;
13727 }
13728 } while (foundNext);
13729 }
13730 return(result);
13731}
13733 FPL__CheckArgumentNullNoRet(entry);
13734 if (entry->internalHandle.win32FileHandle != INVALID_HANDLE_VALUE) {
13735 HANDLE searchHandle = entry->internalHandle.win32FileHandle;
13736 FindClose(searchHandle);
13737 fplClearStruct(entry);
13738 }
13739}
13740
13741//
13742// Win32 Path/Directories
13743//
13744fpl_platform_api size_t fplGetExecutableFilePath(char *destPath, const size_t maxDestLen) {
13745 wchar_t modulePath[MAX_PATH];
13746 GetModuleFileNameW(fpl_null, modulePath, MAX_PATH);
13747 size_t modulePathLen = lstrlenW(modulePath);
13748 size_t result = fplWideStringToUTF8String(modulePath, modulePathLen, destPath, maxDestLen);
13749 return(result);
13750}
13751
13752fpl_platform_api size_t fplGetHomePath(char *destPath, const size_t maxDestLen) {
13753 FPL__CheckPlatform(0);
13754 const fpl__Win32Api *wapi = &fpl__global__AppState->win32.winApi;
13755 wchar_t homePath[MAX_PATH];
13756 wapi->shell.SHGetFolderPathW(fpl_null, CSIDL_PROFILE, fpl_null, 0, homePath);
13757 size_t homePathLen = lstrlenW(homePath);
13758 size_t result = fplWideStringToUTF8String(homePath, homePathLen, destPath, maxDestLen);
13759 return(result);
13760}
13761
13762//
13763// Win32 Timings
13764//
13766 const fpl__Win32InitState *initState = &fpl__global__InitState.win32;
13767 fplTimestamp result = fplZeroInit;
13768 if (initState->qpf.QuadPart > 0) {
13769 LARGE_INTEGER time;
13770 QueryPerformanceCounter(&time);
13771 result.win32.qpc.QuadPart = time.QuadPart;
13772 } else {
13773 result.win32.ticks = GetTickCount64();
13774 }
13775 return(result);
13776}
13777
13779 const fpl__Win32InitState *initState = &fpl__global__InitState.win32;
13780 fplSeconds result;
13781 LARGE_INTEGER freq = initState->qpf;
13782 if (freq.QuadPart > 0) {
13783 uint64_t delta = finish.win32.qpc.QuadPart - start.win32.qpc.QuadPart;
13784 result = (fplSeconds)(delta / (double)freq.QuadPart);
13785 } else {
13786 uint64_t delta = finish.win32.ticks - start.win32.ticks;
13787 result = (fplSeconds)(delta / 1000.0);
13788 }
13789 return(result);
13790}
13791
13793 fplMilliseconds result = (fplMilliseconds)GetTickCount64();
13794 return(result);
13795}
13796
13797//
13798// Win32 Strings
13799//
13800fpl_platform_api size_t fplWideStringToUTF8String(const wchar_t *wideSource, const size_t wideSourceLen, char *utf8Dest, const size_t maxUtf8DestLen) {
13801 FPL__CheckArgumentNull(wideSource, 0);
13802 FPL__CheckArgumentZero(wideSourceLen, 0);
13803 size_t result = WideCharToMultiByte(CP_UTF8, 0, wideSource, (int)wideSourceLen, fpl_null, 0, fpl_null, fpl_null);
13804 if (utf8Dest != fpl_null) {
13805 size_t minRequiredLen = result + 1;
13806 FPL__CheckArgumentMin(maxUtf8DestLen, minRequiredLen, 0);
13807 WideCharToMultiByte(CP_UTF8, 0, wideSource, (int)wideSourceLen, utf8Dest, (int)maxUtf8DestLen, fpl_null, fpl_null);
13808 utf8Dest[result] = 0;
13809 }
13810 return(result);
13811}
13812fpl_platform_api size_t fplUTF8StringToWideString(const char *utf8Source, const size_t utf8SourceLen, wchar_t *wideDest, const size_t maxWideDestLen) {
13813 FPL__CheckArgumentNull(utf8Source, 0);
13814 FPL__CheckArgumentZero(utf8SourceLen, 0);
13815 size_t result = MultiByteToWideChar(CP_UTF8, 0, utf8Source, (int)utf8SourceLen, fpl_null, 0);
13816 if (wideDest != fpl_null) {
13817 size_t minRequiredLen = result + 1;
13818 FPL__CheckArgumentMin(maxWideDestLen, minRequiredLen, 0);
13819 MultiByteToWideChar(CP_UTF8, 0, utf8Source, (int)utf8SourceLen, wideDest, (int)maxWideDestLen);
13820 wideDest[result] = 0;
13821 }
13822 return(result);
13823}
13824
13825//
13826// Win32 Library
13827//
13828fpl_platform_api bool fplDynamicLibraryLoad(const char *libraryFilePath, fplDynamicLibraryHandle *outHandle) {
13829 bool result = false;
13830 if (libraryFilePath != fpl_null && outHandle != fpl_null) {
13831 wchar_t libraryFilePathWide[FPL_MAX_PATH_LENGTH];
13832 fplUTF8StringToWideString(libraryFilePath, fplGetStringLength(libraryFilePath), libraryFilePathWide, fplArrayCount(libraryFilePathWide));
13833 HMODULE libModule = LoadLibraryW(libraryFilePathWide);
13834 if (libModule != fpl_null) {
13835 fplClearStruct(outHandle);
13836 outHandle->internalHandle.win32LibraryHandle = libModule;
13837 outHandle->isValid = true;
13838 result = true;
13839 }
13840 }
13841 return(result);
13842}
13843fpl_platform_api void *fplGetDynamicLibraryProc(const fplDynamicLibraryHandle *handle, const char *name) {
13844 if ((handle != fpl_null) && (handle->internalHandle.win32LibraryHandle != fpl_null) && (name != fpl_null)) {
13845 HMODULE libModule = (HMODULE)handle->internalHandle.win32LibraryHandle;
13846 return (void *)GetProcAddress(libModule, name);
13847 }
13848 return fpl_null;
13849}
13851 if ((handle != fpl_null) && (handle->internalHandle.win32LibraryHandle != fpl_null)) {
13852 HMODULE libModule = (HMODULE)handle->internalHandle.win32LibraryHandle;
13853 FreeLibrary(libModule);
13854 fplClearStruct(handle);
13855 }
13856}
13857
13858#if defined(FPL__ENABLE_WINDOW)
13859//
13860// Win32 Window
13861//
13863 FPL__CheckArgumentNull(outSize, false);
13864 FPL__CheckPlatform(false);
13865 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
13866 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
13867 const fpl__Win32Api *wapi = &appState->winApi;
13868 bool result = false;
13869 RECT windowRect;
13870 if (wapi->user.GetClientRect(windowState->windowHandle, &windowRect)) {
13871 outSize->width = windowRect.right - windowRect.left;
13872 outSize->height = windowRect.bottom - windowRect.top;
13873 result = true;
13874 }
13875 return(result);
13876}
13877
13878fpl_platform_api void fplSetWindowSize(const uint32_t width, const uint32_t height) {
13879 FPL__CheckPlatformNoRet();
13880 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
13881 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
13882 const fpl__Win32Api *wapi = &appState->winApi;
13883 RECT clientRect, windowRect;
13884 if (wapi->user.GetClientRect(windowState->windowHandle, &clientRect) &&
13885 wapi->user.GetWindowRect(windowState->windowHandle, &windowRect)) {
13886 int borderWidth = (windowRect.right - windowRect.left) - (clientRect.right - clientRect.left);
13887 int borderHeight = (windowRect.bottom - windowRect.top) - (clientRect.bottom - clientRect.top);
13888 int newWidth = width + borderWidth;
13889 int newHeight = height + borderHeight;
13890 wapi->user.SetWindowPos(windowState->windowHandle, fpl_null, 0, 0, newWidth, newHeight, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
13891 }
13892}
13893
13895 FPL__CheckPlatform(false);
13896 const fpl__PlatformAppState *appState = fpl__global__AppState;
13897 bool result = appState->currentSettings.window.isResizable != 0;
13898 return(result);
13899}
13900
13901fpl_platform_api void fplSetWindowResizeable(const bool value) {
13902 FPL__CheckPlatformNoRet();
13903 fpl__PlatformAppState *appState = fpl__global__AppState;
13904 const fpl__Win32WindowState *windowState = &appState->window.win32;
13905 if (!appState->currentSettings.window.isFullscreen && appState->currentSettings.window.isDecorated) {
13906 DWORD style = fpl__win32_GetWindowLong(windowState->windowHandle, GWL_STYLE);
13907 DWORD exStyle = fpl__win32_GetWindowLong(windowState->windowHandle, GWL_EXSTYLE);
13908 if (value) {
13909 style |= (WS_MAXIMIZEBOX | WS_THICKFRAME);
13910 } else {
13911 style &= ~(WS_MAXIMIZEBOX | WS_THICKFRAME);
13912 }
13913 fpl__win32_SetWindowLong(windowState->windowHandle, GWL_STYLE, style);
13914 appState->currentSettings.window.isResizable = value;
13915 }
13916}
13917
13919 FPL__CheckPlatform(false);
13920 const fpl__PlatformAppState *appState = fpl__global__AppState;
13921 bool result = appState->currentSettings.window.isDecorated != 0;
13922 return(result);
13923}
13924
13925fpl_platform_api void fplSetWindowDecorated(const bool value) {
13926 FPL__CheckPlatformNoRet();
13927 fpl__PlatformAppState *appState = fpl__global__AppState;
13928 const fpl__Win32WindowState *windowState = &appState->window.win32;
13929 const fpl__Win32Api *wapi = &appState->win32.winApi;
13930 if (!appState->currentSettings.window.isFullscreen) {
13931 HWND windowHandle = windowState->windowHandle;
13932 DWORD style = fpl__win32_GetWindowLong(windowHandle, GWL_STYLE);
13933 DWORD exStyle = fpl__win32_GetWindowLong(windowHandle, GWL_EXSTYLE);
13934 if (value) {
13935 style &= ~WS_POPUP;
13936 style |= WS_OVERLAPPEDWINDOW;
13937 if (!appState->currentSettings.window.isResizable) {
13938 style &= ~(WS_MAXIMIZEBOX | WS_THICKFRAME);
13939 }
13940 } else {
13941 style &= ~WS_OVERLAPPEDWINDOW;
13942 style |= WS_POPUP;
13943 }
13944 fpl__win32_SetWindowLong(windowHandle, GWL_STYLE, style);
13945 wapi->user.SetWindowPos(windowHandle, fpl_null, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
13946 appState->currentSettings.window.isDecorated = value;
13947 }
13948}
13949
13951 FPL__CheckPlatform(false);
13952 const fpl__PlatformAppState *appState = fpl__global__AppState;
13953 bool result = appState->currentSettings.window.isFloating != 0;
13954 return(result);
13955}
13956
13957fpl_platform_api void fplSetWindowFloating(const bool value) {
13958 FPL__CheckPlatformNoRet();
13959 fpl__PlatformAppState *appState = fpl__global__AppState;
13960 const fpl__Win32WindowState *windowState = &appState->window.win32;
13961 const fpl__Win32Api *wapi = &appState->win32.winApi;
13962 if (!appState->currentSettings.window.isFullscreen) {
13963 if (value) {
13964 wapi->user.SetWindowPos(windowState->windowHandle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
13965 } else {
13966 wapi->user.SetWindowPos(windowState->windowHandle, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
13967 }
13968 appState->currentSettings.window.isFloating = value;
13969 }
13970}
13971
13973 FPL__CheckPlatform(false);
13974 fpl__PlatformAppState *appState = fpl__global__AppState;
13975 bool result = appState->currentSettings.window.isFullscreen != 0;
13976 return(result);
13977}
13978
13979
13980fpl_platform_api bool fplSetWindowFullscreenSize(const bool value, const uint32_t fullscreenWidth, const uint32_t fullscreenHeight, const uint32_t refreshRate) {
13981 bool result = fpl__Win32SetWindowFullscreen(value, INT32_MAX, INT32_MAX, fullscreenWidth, fullscreenHeight, refreshRate, true);
13982 return(result);
13983}
13984
13985fpl_platform_api bool fplSetWindowFullscreenRect(const bool value, const int32_t x, const int32_t y, const int32_t width, const int32_t height) {
13986 bool result = fpl__Win32SetWindowFullscreen(value, x, y, width, height, 0, false);
13987 return(result);
13988}
13989
13991 bool result = fpl__Win32SetWindowFullscreen(true, INT32_MAX, INT32_MAX, 0, 0, 0, false);
13992 return(result);
13993}
13994
13996 bool result = fpl__Win32SetWindowFullscreen(false, 0, 0, 0, 0, 0, false);
13997 return(result);
13998}
13999
14001 FPL__CheckArgumentNull(outPos, false);
14002 FPL__CheckPlatform(false);
14003 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14004 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14005 const fpl__Win32Api *wapi = &appState->winApi;
14006 bool result = false;
14007 WINDOWPLACEMENT placement = fplZeroInit;
14008 placement.length = sizeof(WINDOWPLACEMENT);
14009 if (wapi->user.GetWindowPlacement(windowState->windowHandle, &placement) == TRUE) {
14010 switch (placement.showCmd) {
14011 case SW_MAXIMIZE:
14012 {
14013 outPos->left = placement.ptMaxPosition.x;
14014 outPos->top = placement.ptMaxPosition.y;
14015 } break;
14016 case SW_MINIMIZE:
14017 {
14018 outPos->left = placement.ptMinPosition.x;
14019 outPos->top = placement.ptMinPosition.y;
14020 } break;
14021 default:
14022 {
14023 outPos->left = placement.rcNormalPosition.left;
14024 outPos->top = placement.rcNormalPosition.top;
14025 } break;
14026 }
14027 result = true;
14028 }
14029 return(result);
14030}
14031
14032fpl_platform_api void fplSetWindowTitle(const char *title) {
14033 FPL__CheckPlatformNoRet();
14034 fpl__PlatformAppState *appState = fpl__global__AppState;
14035 const fpl__Win32AppState *win32AppState = &fpl__global__AppState->win32;
14036 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14037 const fpl__Win32Api *wapi = &win32AppState->winApi;
14038 HWND handle = windowState->windowHandle;
14039 fplCopyString(title, appState->currentSettings.window.title, fplArrayCount(appState->currentSettings.window.title));
14040 wchar_t titleWide[FPL_MAX_BUFFER_LENGTH];
14041 fplUTF8StringToWideString(title, fplGetStringLength(title), titleWide, fplArrayCount(titleWide));
14042 wapi->user.SetWindowTextW(handle, titleWide);
14043}
14044
14045fpl_platform_api void fplSetWindowPosition(const int32_t left, const int32_t top) {
14046 FPL__CheckPlatformNoRet();
14047 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14048 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14049 const fpl__Win32Api *wapi = &appState->winApi;
14050 WINDOWPLACEMENT placement = fplZeroInit;
14051 placement.length = sizeof(WINDOWPLACEMENT);
14052 RECT windowRect;
14053 if (wapi->user.GetWindowPlacement(windowState->windowHandle, &placement) &&
14054 wapi->user.GetWindowRect(windowState->windowHandle, &windowRect)) {
14055 switch (placement.showCmd) {
14056 case SW_NORMAL:
14057 case SW_SHOW:
14058 {
14059 placement.rcNormalPosition.left = left;
14060 placement.rcNormalPosition.top = top;
14061 placement.rcNormalPosition.right = placement.rcNormalPosition.left + (windowRect.right - windowRect.left);
14062 placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (windowRect.bottom - windowRect.top);
14063 wapi->user.SetWindowPlacement(windowState->windowHandle, &placement);
14064 } break;
14065 }
14066 }
14067}
14068
14070 FPL__CheckPlatform(fplWindowState_Unknown);
14071 const fpl__PlatformAppState *appState = fpl__global__AppState;
14072 const fpl__Win32AppState *win32AppState = &appState->win32;
14073 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14074 const fpl__Win32Api *wapi = &win32AppState->winApi;
14075 HWND windowHandle = windowState->windowHandle;
14076 fplWindowState result;
14077 if (appState->currentSettings.window.isFullscreen) {
14079 } else {
14080 bool isMaximized = !!wapi->user.IsZoomed(windowHandle);
14081 bool isMinimized = !!wapi->user.IsIconic(windowHandle);
14082 if (isMinimized) {
14083 result = fplWindowState_Iconify;
14084 } else if (isMaximized) {
14085 result = fplWindowState_Maximize;
14086 } else {
14087 result = fplWindowState_Normal;
14088 }
14089 }
14090 return(result);
14091}
14092
14094 FPL__CheckPlatform(false);
14095 const fpl__PlatformAppState *appState = fpl__global__AppState;
14096 const fpl__Win32AppState *win32AppState = &appState->win32;
14097 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14098 const fpl__Win32Api *wapi = &win32AppState->winApi;
14099 HWND windowHandle = windowState->windowHandle;
14100 bool result = false;
14101 switch (newState) {
14103 {
14104 wapi->user.SendMessageW(windowHandle, WM_SYSCOMMAND, SC_MINIMIZE, 0);
14105 result = true;
14106 } break;
14107
14109 {
14110 if (!appState->currentSettings.window.isFullscreen && appState->currentSettings.window.isResizable) {
14111 wapi->user.SendMessageW(windowHandle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
14112 result = true;
14113 }
14114 } break;
14115
14117 {
14118 wapi->user.SendMessageW(windowHandle, WM_SYSCOMMAND, SC_RESTORE, 0);
14119 result = true;
14120 } break;
14121
14122 default:
14123 break;
14124 }
14125 return(true);
14126}
14127
14128fpl_platform_api void fplSetWindowCursorEnabled(const bool value) {
14129 FPL__CheckPlatformNoRet();
14130 fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14131 windowState->isCursorActive = value;
14132}
14133
14134fpl_internal bool fpl__Win32ProcessNextEvent(const fpl__Win32Api *wapi, fpl__PlatformAppState *appState, fpl__Win32WindowState *windowState) {
14135 bool result = false;
14136 if (windowState->windowHandle != 0) {
14137 MSG msg;
14138 if (wapi->user.PeekMessageW(&msg, windowState->windowHandle, 0, 0, PM_REMOVE) == TRUE) {
14139 fpl__Win32HandleMessage(wapi, appState, windowState, &msg);
14140 result = true;
14141 }
14142 }
14143 return(result);
14144}
14145
14147 FPL__CheckPlatform(false);
14148 fpl__PlatformAppState *appState = fpl__global__AppState;
14149 fpl__Win32AppState *win32AppState = &appState->win32;
14150 fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14151 const fpl__Win32InitState *win32InitState = &fpl__global__InitState.win32;
14152 const fpl__Win32Api *wapi = &win32AppState->winApi;
14153
14154 // Poll next event from the internal queue first
14155 if (fpl__PollInternalEvent(ev)) {
14156 return(true);
14157 }
14158
14159 // Create new event from the OS message queue
14160 if (!fpl__Win32ProcessNextEvent(wapi, appState, windowState)) {
14161 return(false);
14162 }
14163
14164 // Poll the first event from the internal queue
14165 if (fpl__PollInternalEvent(ev)) {
14166 return(true);
14167 }
14168
14169 // No events left
14170 return(false);
14171}
14172
14174 FPL__CheckPlatformNoRet();
14175 fpl__PlatformAppState *appState = fpl__global__AppState;
14176 fpl__Win32AppState *win32AppState = &appState->win32;
14177 fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14178 const fpl__Win32InitState *win32InitState = &fpl__global__InitState.win32;
14179 const fpl__Win32Api *wapi = &win32AppState->winApi;
14180 if (windowState->windowHandle != 0) {
14181 if (windowState->mainFiber != fpl_null && windowState->messageFiber != fpl_null) {
14182 SwitchToFiber(windowState->messageFiber);
14183 } else {
14184 MSG msg;
14185 while (wapi->user.PeekMessageW(&msg, windowState->windowHandle, 0, 0, PM_REMOVE)) {
14186 fpl__Win32HandleMessage(wapi, appState, windowState, &msg);
14187 }
14188 }
14189 }
14190 fpl__ClearInternalEvents();
14191}
14192
14194 FPL__CheckPlatform(false);
14195 fpl__PlatformAppState *appState = fpl__global__AppState;
14196 fpl__Win32AppState *win32AppState = &appState->win32;
14197 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14198 const fpl__Win32InitState *win32InitState = &fpl__global__InitState.win32;
14199 const fpl__Win32Api *wapi = &win32AppState->winApi;
14200 fpl__ClearInternalEvents();
14201 if ((!appState->currentSettings.input.disabledEvents) && (appState->initFlags & fplInitFlags_GameController)) {
14202 fpl__Win32UpdateGameControllers(&appState->currentSettings, win32InitState, &win32AppState->xinput);
14203 }
14204 bool result = appState->window.isRunning != 0;
14205 return(result);
14206}
14207
14209 FPL__CheckPlatform(false);
14210 bool result = fpl__global__AppState->window.isRunning != 0;
14211 return(result);
14212}
14213
14215 FPL__CheckPlatformNoRet();
14216 fpl__PlatformAppState *appState = fpl__global__AppState;
14217 const fpl__Win32AppState *win32AppState = &appState->win32;
14218 if (appState->window.isRunning) {
14219 appState->window.isRunning = false;
14220 const fpl__Win32Api *wapi = &win32AppState->winApi;
14221 wapi->user.PostQuitMessage(0);
14222 }
14223}
14224
14225fpl_platform_api bool fplGetClipboardText(char *dest, const uint32_t maxDestLen) {
14226 FPL__CheckPlatform(false);
14227 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14228 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14229 const fpl__Win32Api *wapi = &appState->winApi;
14230 bool result = false;
14231 if (wapi->user.OpenClipboard(windowState->windowHandle)) {
14232 if (wapi->user.IsClipboardFormatAvailable(CF_UNICODETEXT)) {
14233 HGLOBAL dataHandle = wapi->user.GetClipboardData(CF_UNICODETEXT);
14234 if (dataHandle != fpl_null) {
14235 const wchar_t *stringValue = (const wchar_t *)GlobalLock(dataHandle);
14236 fplWideStringToUTF8String(stringValue, lstrlenW(stringValue), dest, maxDestLen);
14237 GlobalUnlock(dataHandle);
14238 result = true;
14239 }
14240 }
14241 wapi->user.CloseClipboard();
14242 }
14243 return(result);
14244}
14245
14246fpl_platform_api bool fplSetClipboardText(const char *text) {
14247 FPL__CheckPlatform(false);
14248 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14249 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14250 const fpl__Win32Api *wapi = &appState->winApi;
14251 bool result = false;
14252 if (wapi->user.OpenClipboard(windowState->windowHandle)) {
14253 const size_t textLen = fplGetStringLength(text);
14254 const size_t bufferLen = textLen + 1;
14255 HGLOBAL handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)bufferLen * sizeof(wchar_t));
14256 if (handle != fpl_null) {
14257 wchar_t *target = (wchar_t *)GlobalLock(handle);
14258 fplUTF8StringToWideString(text, textLen, target, bufferLen);
14259 GlobalUnlock(handle);
14260 wapi->user.EmptyClipboard();
14261 wapi->user.SetClipboardData(CF_UNICODETEXT, handle);
14262 result = true;
14263 }
14264 wapi->user.CloseClipboard();
14265 }
14266 return(result);
14267}
14268
14270 FPL__CheckArgumentNull(outState, false);
14271 FPL__CheckPlatform(false);
14272 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14273 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14274 const fpl__Win32Api *wapi = &appState->winApi;
14275 fplClearStruct(outState);
14276 outState->modifiers = fpl__Win32GetKeyboardModifiers(wapi);
14277 for (uint32_t keyCode = 0; keyCode < 256; ++keyCode) {
14278 int k = wapi->user.MapVirtualKeyW(MAPVK_VSC_TO_VK, keyCode);
14279 if (k == 0) {
14280 k = (int)keyCode;
14281 }
14282 bool down = fpl__Win32IsKeyDown(wapi, k);
14283 fplKey key = fpl__GetMappedKey(&fpl__global__AppState->window, keyCode);
14284 outState->keyStatesRaw[keyCode] = down;
14285 outState->buttonStatesMapped[(int)key] = down ? fplButtonState_Press : fplButtonState_Release;
14286 }
14287 return(true);
14288}
14289
14291 FPL__CheckArgumentNull(outStates, false);
14292 FPL__CheckPlatform(false);
14293 fpl__PlatformAppState *platformAppState = fpl__global__AppState;
14294 if (platformAppState->initFlags & fplInitFlags_GameController) {
14295 fpl__Win32AppState *appState = &platformAppState->win32;
14296 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14297 const fpl__Win32Api *wapi = &appState->winApi;
14298 fpl__Win32XInputState *xinputState = &appState->xinput;
14299 fplAssert(xinputState != fpl_null);
14300 if (xinputState->xinputApi.XInputGetState != fpl_null) {
14301 // @TODO(final): fpl__Win32UpdateGameControllers() uses duplicate code
14302 QueryPerformanceCounter(&xinputState->lastDeviceSearchTime);
14303
14304 fplClearStruct(outStates);
14305 for (DWORD controllerIndex = 0; controllerIndex < XUSER_MAX_COUNT; ++controllerIndex) {
14306 XINPUT_STATE controllerState = fplZeroInit;
14307 if (xinputState->xinputApi.XInputGetState(controllerIndex, &controllerState) == ERROR_SUCCESS) {
14308 if (!xinputState->isConnected[controllerIndex]) {
14309 xinputState->isConnected[controllerIndex] = true;
14310 fplStringFormat(xinputState->deviceNames[controllerIndex], fplArrayCount(xinputState->deviceNames[controllerIndex]), "XInput-Device [%d]", controllerIndex);
14311 }
14312 const XINPUT_GAMEPAD *newPadState = &controllerState.Gamepad;
14313 fplGamepadState *targetPadState = &outStates->deviceStates[controllerIndex];
14314 fpl__Win32XInputGamepadToGamepadState(newPadState, targetPadState);
14315 targetPadState->deviceName = xinputState->deviceNames[controllerIndex];
14316 } else {
14317 if (xinputState->isConnected[controllerIndex]) {
14318 xinputState->isConnected[controllerIndex] = false;
14319 }
14320 }
14321 }
14322 return(true);
14323 }
14324 }
14325 return(false);
14326}
14327
14328fpl_platform_api bool fplQueryCursorPosition(int32_t *outX, int32_t *outY) {
14329 FPL__CheckArgumentNull(outX, false);
14330 FPL__CheckArgumentNull(outY, false);
14331 FPL__CheckPlatform(false);
14332 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14333 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14334 const fpl__Win32Api *wapi = &appState->winApi;
14335 POINT p;
14336 if (wapi->user.GetCursorPos(&p) == TRUE) {
14337#if 0
14338 HMONITOR monitor = wapi->user.MonitorFromPoint(p, MONITOR_DEFAULTTONEAREST);
14339 if (monitor != fpl_null) {
14340 MONITORINFOEXW info = fplZeroInit;
14341 info.cbSize = sizeof(info);
14342 if (wapi->user.GetMonitorInfoW(monitor, (LPMONITORINFO)&info) != 0) {
14343 *outX = p.x - info.rcMonitor.left;
14344 *outY = p.y - info.rcMonitor.top;
14345 return(true);
14346 }
14347 }
14348#else
14349 *outX = p.x;
14350 *outY = p.y;
14351 return(true);
14352#endif
14353 }
14354 return(false);
14355}
14356
14358 FPL__CheckArgumentNull(outState, false);
14359 FPL__CheckPlatform(false);
14360 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14361 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14362 const fpl__Win32Api *wapi = &appState->winApi;
14363 POINT p;
14364 if ((wapi->user.GetCursorPos(&p) == TRUE) && (wapi->user.ScreenToClient(windowState->windowHandle, &p))) {
14365 fplClearStruct(outState);
14366 outState->x = p.x;
14367 outState->y = p.y;
14368
14369 bool leftDown = fpl__Win32IsKeyDown(wapi, VK_LBUTTON);
14370 bool rightDown = fpl__Win32IsKeyDown(wapi, VK_RBUTTON);
14371 bool middleDown = fpl__Win32IsKeyDown(wapi, VK_MBUTTON);
14375
14376 return(true);
14377 }
14378 return(false);
14379}
14380
14381fpl_internal BOOL WINAPI fpl__Win32MonitorCountEnumProc(HMONITOR monitorHandle, HDC hdc, LPRECT rect, LPARAM userData) {
14382 size_t *count = (size_t *)(uintptr_t)userData;
14383 *count = *count + 1;
14384 return(TRUE);
14385}
14386
14388 FPL__CheckPlatform(0);
14389 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14390 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14391 const fpl__Win32Api *wapi = &appState->winApi;
14392 size_t result = 0;
14393 LPARAM param = (LPARAM)&result;
14394 wapi->user.EnumDisplayMonitors(fpl_null, fpl_null, fpl__Win32MonitorCountEnumProc, param);
14395 return(result);
14396}
14397fpl_internal void fpl__Win32FillDisplayInfo(const MONITORINFOEXW *info, fplDisplayInfo *outInfo) {
14398 fplAssert(info != fpl_null);
14399 fplAssert(outInfo != fpl_null);
14400 size_t idLen = lstrlenW(info->szDevice);
14401 fplWideStringToUTF8String(info->szDevice, idLen, outInfo->id, fplArrayCount(outInfo->id));
14402 outInfo->virtualPosition.left = info->rcMonitor.left;
14403 outInfo->virtualPosition.top = info->rcMonitor.top;
14404 outInfo->virtualSize.width = info->rcMonitor.right - info->rcMonitor.left;
14405 outInfo->virtualSize.height = info->rcMonitor.bottom - info->rcMonitor.top;
14406 outInfo->physicalSize.width = (info->rcMonitor.right > info->rcMonitor.left) ? (info->rcMonitor.right - info->rcMonitor.left) : (info->rcMonitor.left - info->rcMonitor.right);
14407 outInfo->physicalSize.height = (info->rcMonitor.bottom > info->rcMonitor.top) ? (info->rcMonitor.bottom - info->rcMonitor.top) : (info->rcMonitor.top - info->rcMonitor.bottom);
14408 outInfo->isPrimary = (info->dwFlags & MONITORINFOF_PRIMARY) ? 1 : 0;
14409}
14410
14411typedef struct fpl__Win32DisplayEnumState {
14412 fplDisplayInfo *baseInfo;
14413 const fpl__Win32Api *wapi;
14414 size_t count;
14415 size_t maxCount;
14416} fpl__Win32DisplayEnumState;
14417
14418fpl_internal BOOL WINAPI fpl__Win32MonitorInfoEnumProc(HMONITOR monitorHandle, HDC hdc, LPRECT rect, LPARAM userData) {
14419 fpl__Win32DisplayEnumState *enumState = (fpl__Win32DisplayEnumState *)(uintptr_t)userData;
14420 const fpl__Win32Api *wapi = enumState->wapi;
14421 if (enumState->count < enumState->maxCount) {
14422 fplDisplayInfo *targetInfo = enumState->baseInfo + enumState->count;
14423 fplClearStruct(targetInfo);
14424 MONITORINFOEXW info = fplZeroInit;
14425 info.cbSize = sizeof(info);
14426 if (wapi->user.GetMonitorInfoW(monitorHandle, (LPMONITORINFO)&info) != 0) {
14427 fpl__Win32FillDisplayInfo(&info, targetInfo);
14428 }
14429 ++enumState->count;
14430 BOOL result = enumState->count < enumState->maxCount;
14431 return(result);
14432 } else {
14433 return(FALSE);
14434 }
14435}
14436
14437fpl_platform_api size_t fplGetDisplays(fplDisplayInfo *displays, const size_t maxDisplayCount) {
14438 FPL__CheckArgumentNull(displays, 0);
14439 FPL__CheckArgumentZero(maxDisplayCount, 0);
14440 FPL__CheckPlatform(0);
14441 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14442 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14443 const fpl__Win32Api *wapi = &appState->winApi;
14444 fpl__Win32DisplayEnumState enumState = fplZeroInit;
14445 enumState.baseInfo = displays;
14446 enumState.maxCount = maxDisplayCount;
14447 enumState.wapi = wapi;
14448 LPARAM param = (LPARAM)&enumState;
14449 wapi->user.EnumDisplayMonitors(fpl_null, fpl_null, fpl__Win32MonitorInfoEnumProc, param);
14450 return(enumState.count);
14451}
14452
14453fpl_internal BOOL WINAPI fpl__Win32PrimaryMonitorEnumProc(HMONITOR monitorHandle, HDC hdc, LPRECT rect, LPARAM userData) {
14454 fpl__Win32DisplayEnumState *enumState = (fpl__Win32DisplayEnumState *)(uintptr_t)userData;
14455 const fpl__Win32Api *wapi = enumState->wapi;
14456 MONITORINFOEXW info = fplZeroInit;
14457 info.cbSize = sizeof(info);
14458 if (wapi->user.GetMonitorInfoW(monitorHandle, (LPMONITORINFO)&info) != 0) {
14459 if (info.dwFlags & MONITORINFOF_PRIMARY) {
14460 fplClearStruct(enumState->baseInfo);
14461 fpl__Win32FillDisplayInfo(&info, enumState->baseInfo);
14462 enumState->count = 1;
14463 return(FALSE);
14464 }
14465 }
14466 return(TRUE);
14467}
14468
14470 FPL__CheckArgumentNull(display, false);
14471 FPL__CheckPlatform(false);
14472 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14473 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14474 const fpl__Win32Api *wapi = &appState->winApi;
14475 fpl__Win32DisplayEnumState enumState = fplZeroInit;
14476 enumState.baseInfo = display;
14477 enumState.maxCount = 1;
14478 enumState.wapi = wapi;
14479 LPARAM param = (LPARAM)&enumState;
14480 wapi->user.EnumDisplayMonitors(fpl_null, fpl_null, fpl__Win32PrimaryMonitorEnumProc, param);
14481 bool result = (enumState.count == 1);
14482 return(result);
14483}
14484
14486 FPL__CheckArgumentNull(outDisplay, false);
14487 FPL__CheckPlatform(false);
14488 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14489 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14490 const fpl__Win32Api *wapi = &appState->winApi;
14491 HMONITOR foundMonitor = wapi->user.MonitorFromWindow(windowState->windowHandle, MONITOR_DEFAULTTONULL);
14492 bool result = false;
14493 if (foundMonitor != fpl_null) {
14494 MONITORINFOEXW info = fplZeroInit;
14495 info.cbSize = sizeof(info);
14496 if (wapi->user.GetMonitorInfoW(foundMonitor, (LPMONITORINFO)&info) != 0) {
14497 fplClearStruct(outDisplay);
14498 fpl__Win32FillDisplayInfo(&info, outDisplay);
14499 result = true;
14500 }
14501 }
14502 return(result);
14503}
14504
14505fpl_platform_api bool fplGetDisplayFromPosition(const int32_t x, const int32_t y, fplDisplayInfo *outDisplay) {
14506 FPL__CheckArgumentNull(outDisplay, false);
14507 FPL__CheckPlatform(false);
14508 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14509 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14510 const fpl__Win32Api *wapi = &appState->winApi;
14511 POINT pt;
14512 pt.x = x;
14513 pt.y = y;
14514 bool result = false;
14515 HMONITOR foundMonitor = wapi->user.MonitorFromPoint(pt, MONITOR_DEFAULTTONULL);
14516 if (foundMonitor != fpl_null) {
14517 MONITORINFOEXW info = fplZeroInit;
14518 info.cbSize = sizeof(info);
14519 if (wapi->user.GetMonitorInfoW(foundMonitor, (LPMONITORINFO)&info) != 0) {
14520 fplClearStruct(outDisplay);
14521 fpl__Win32FillDisplayInfo(&info, outDisplay);
14522 result = true;
14523 }
14524 }
14525 return(result);
14526}
14527
14528fpl_platform_api size_t fplGetDisplayModes(const char *id, fplDisplayMode *modes, const size_t maxDisplayModeCount) {
14529 FPL__CheckArgumentNull(id, 0);
14530 FPL__CheckPlatform(0);
14531 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14532 const fpl__Win32WindowState *windowState = &fpl__global__AppState->window.win32;
14533 const fpl__Win32Api *wapi = &appState->winApi;
14534 wchar_t deviceName[CCHDEVICENAME + 1];
14535 fplUTF8StringToWideString(id, fplGetStringLength(id), deviceName, fplArrayCount(deviceName));
14536 size_t result = 0;
14537 DEVMODEW devMode;
14538 while (wapi->user.EnumDisplaySettingsW(deviceName, (DWORD)result, &devMode)) {
14539 if (modes != fpl_null) {
14540 if (result == maxDisplayModeCount) {
14541 break;
14542 }
14543 fplDisplayMode *outMode = modes + result;
14544 fplClearStruct(outMode);
14545 outMode->width = devMode.dmPelsWidth;
14546 outMode->height = devMode.dmPelsHeight;
14547 outMode->colorBits = devMode.dmBitsPerPel;
14548 outMode->refreshRate = devMode.dmDisplayFrequency;
14549 }
14550 ++result;
14551 }
14552 return(result);
14553}
14554
14555#endif // FPL__ENABLE_WINDOW
14556
14557fpl_internal LCTYPE fpl__Win32GetLocaleLCIDFromFormat(const fplLocaleFormat format) {
14558 switch (format) {
14560 return LOCALE_SNAME;
14561 default:
14562 return LOCALE_SABBREVLANGNAME;
14563 }
14564}
14565
14566fpl_platform_api size_t fplGetSystemLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
14567 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
14568 LCTYPE lcType = fpl__Win32GetLocaleLCIDFromFormat(targetFormat);
14569 wchar_t bufferWide[FPL_MAX_BUFFER_LENGTH];
14570 int r = GetLocaleInfoW(LOCALE_SYSTEM_DEFAULT, lcType, bufferWide, fplArrayCount(bufferWide));
14571 size_t result = fplWideStringToUTF8String(bufferWide, lstrlenW(bufferWide), buffer, maxBufferLen);
14572 return(result);
14573}
14574
14575fpl_platform_api size_t fplGetUserLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
14576 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
14577 LCTYPE lcType = fpl__Win32GetLocaleLCIDFromFormat(targetFormat);
14578 wchar_t bufferWide[FPL_MAX_BUFFER_LENGTH];
14579 int r = GetLocaleInfoW(LOCALE_USER_DEFAULT, lcType, bufferWide, fplArrayCount(bufferWide));
14580 size_t result = fplWideStringToUTF8String(bufferWide, lstrlenW(bufferWide), buffer, maxBufferLen);
14581 return(result);
14582}
14583
14584fpl_platform_api size_t fplGetInputLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
14585 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
14586 FPL__CheckPlatform(false);
14587 const fpl__Win32AppState *appState = &fpl__global__AppState->win32;
14588 const fpl__Win32Api *wapi = &appState->winApi;
14589 HKL kbLayout = wapi->user.GetKeyboardLayout(GetCurrentThreadId());
14590 LCID langId = (DWORD)(intptr_t)kbLayout & 0xFFFF;
14591 LCTYPE lcType = fpl__Win32GetLocaleLCIDFromFormat(targetFormat);
14592 wchar_t bufferWide[FPL_MAX_BUFFER_LENGTH];
14593 int r = GetLocaleInfoW(langId, lcType, bufferWide, fplArrayCount(bufferWide));
14594 size_t result = fplWideStringToUTF8String(bufferWide, lstrlenW(bufferWide), buffer, maxBufferLen);
14595 return(result);
14596}
14597#endif // FPL_PLATFORM_WINDOWS
14598
14599// ############################################################################
14600//
14601// > POSIX_SUBPLATFORM (Linux, Unix)
14602//
14603// ############################################################################
14604#if defined(FPL_SUBPLATFORM_POSIX)
14605fpl_internal void fpl__PosixReleaseSubplatform(fpl__PosixAppState *appState) {
14606 fpl__PThreadUnloadApi(&appState->pthreadApi);
14607}
14608
14609fpl_internal bool fpl__PosixInitSubplatform(const fplInitFlags initFlags, const fplSettings *initSettings, fpl__PosixInitState *initState, fpl__PosixAppState *appState) {
14610 if (!fpl__PThreadLoadApi(&appState->pthreadApi)) {
14611 FPL__ERROR(FPL__MODULE_POSIX, "Failed initializing PThread API");
14612 return false;
14613 }
14614 return true;
14615}
14616
14617fpl_internal void fpl__InitWaitTimeSpec(const uint32_t milliseconds, struct timespec *outSpec) {
14618 time_t secs = milliseconds / 1000;
14619 uint64_t nanoSecs = (milliseconds - (secs * 1000)) * 1000000;
14620 if (nanoSecs >= 1000000000) {
14621 time_t addonSecs = (time_t)(nanoSecs / 1000000000);
14622 nanoSecs -= (addonSecs * 1000000000);
14623 secs += addonSecs;
14624 }
14625 clock_gettime(CLOCK_REALTIME, outSpec);
14626 outSpec->tv_sec += secs;
14627 outSpec->tv_nsec += nanoSecs;
14628}
14629
14630void *fpl__PosixThreadProc(void *data) {
14631 fplAssert(fpl__global__AppState != fpl_null);
14632 const fpl__PThreadApi *pthreadApi = &fpl__global__AppState->posix.pthreadApi;
14633 fplThreadHandle *thread = (fplThreadHandle *)data;
14634 fplAssert(thread != fpl_null);
14635 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Running);
14636
14637 fplThreadParameters parameters = thread->parameters;
14638 if (parameters.runFunc != fpl_null) {
14639 parameters.runFunc(thread, parameters.userData);
14640 }
14641
14642 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopping);
14643 thread->isValid = false;
14644 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopped);
14645
14646 pthreadApi->pthread_exit(data);
14647 return 0;
14648}
14649
14650fpl_internal bool fpl__PosixMutexLock(const fpl__PThreadApi *pthreadApi, pthread_mutex_t *handle) {
14651 int lockRes;
14652 do {
14653 lockRes = pthreadApi->pthread_mutex_lock(handle);
14654 } while (lockRes == EAGAIN);
14655 bool result = (lockRes == 0);
14656 return(result);
14657}
14658
14659fpl_internal bool fpl__PosixMutexTryLock(const fpl__PThreadApi *pthreadApi, pthread_mutex_t *handle) {
14660 int lockRes;
14661 do {
14662 lockRes = pthreadApi->pthread_mutex_trylock(handle);
14663 } while (lockRes == EAGAIN);
14664 bool result = (lockRes == 0);
14665 return(result);
14666}
14667
14668fpl_internal bool fpl__PosixMutexUnlock(const fpl__PThreadApi *pthreadApi, pthread_mutex_t *handle) {
14669 int unlockRes;
14670 do {
14671 unlockRes = pthreadApi->pthread_mutex_unlock(handle);
14672 } while (unlockRes == EAGAIN);
14673 bool result = (unlockRes == 0);
14674 return(result);
14675}
14676
14677fpl_internal int fpl__PosixMutexCreate(const fpl__PThreadApi *pthreadApi, pthread_mutex_t *handle) {
14678 int mutexRes;
14679 do {
14680 mutexRes = pthreadApi->pthread_mutex_init(handle, fpl_null);
14681 } while (mutexRes == EAGAIN);
14682 return(mutexRes);
14683}
14684
14685fpl_internal bool fpl__PosixThreadWaitForMultiple(fplThreadHandle **threads, const uint32_t minCount, const uint32_t maxCount, const size_t stride, const fplTimeoutValue timeout) {
14686 FPL__CheckArgumentNull(threads, false);
14687 FPL__CheckArgumentMax(maxCount, FPL_MAX_THREAD_COUNT, false);
14688 const size_t actualStride = stride > 0 ? stride : sizeof(fplThreadHandle *);
14689 for (uint32_t index = 0; index < maxCount; ++index) {
14690 fplThreadHandle *thread = *(fplThreadHandle **)((uint8_t *)threads + index * actualStride);
14691 if (thread == fpl_null) {
14692 FPL__ERROR(FPL__MODULE_THREADING, "Thread for index '%d' are not allowed to be null", index);
14693 return false;
14694 }
14695 }
14696
14697 uint32_t completeCount = 0;
14698 bool isRunning[FPL_MAX_THREAD_COUNT];
14699 for (uint32_t index = 0; index < maxCount; ++index) {
14700 fplThreadHandle *thread = *(fplThreadHandle **)((uint8_t *)threads + index * actualStride);
14701 isRunning[index] = fplGetThreadState(thread) != fplThreadState_Stopped;
14702 if (!isRunning[index]) {
14703 ++completeCount;
14704 }
14705 }
14706
14708 bool result = false;
14709 while (completeCount < minCount) {
14710 for (uint32_t index = 0; index < maxCount; ++index) {
14711 fplThreadHandle *thread = *(fplThreadHandle **)((uint8_t *)threads + index * actualStride);
14712 if (isRunning[index]) {
14713 fplThreadState state = fplGetThreadState(thread);
14714 if (state == fplThreadState_Stopped) {
14715 isRunning[index] = false;
14716 ++completeCount;
14717 if (completeCount >= minCount) {
14718 result = true;
14719 break;
14720 }
14721 }
14722 }
14723 fplThreadSleep(10);
14724 }
14725 if ((timeout != FPL_TIMEOUT_INFINITE) && (fplMillisecondsQuery() - startTime) >= timeout) {
14726 result = false;
14727 break;
14728 }
14729 }
14730 return(result);
14731}
14732
14733//
14734// POSIX Atomics
14735//
14736#if defined(FPL_COMPILER_GCC) || defined(FPL_COMPILER_CLANG) || defined(__GNUC__)
14737// @NOTE(final): See: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html#g_t_005f_005fsync-Builtins
14738// @NOTE(final): There is only one barrier in POSIX (read and write)
14740 __sync_synchronize();
14741}
14743 __sync_synchronize();
14744}
14746 __sync_synchronize();
14747}
14748
14749fpl_platform_api uint32_t fplAtomicExchangeU32(volatile uint32_t *target, const uint32_t value) {
14750 __sync_synchronize();
14751 uint32_t result = __sync_lock_test_and_set(target, value);
14752 return(result);
14753}
14754fpl_platform_api uint64_t fplAtomicExchangeU64(volatile uint64_t *target, const uint64_t value) {
14755 __sync_synchronize();
14756 uint64_t result = __sync_lock_test_and_set(target, value);
14757 return(result);
14758}
14759fpl_platform_api int32_t fplAtomicExchangeS32(volatile int32_t *target, const int32_t value) {
14760 __sync_synchronize();
14761 int32_t result = __sync_lock_test_and_set(target, value);
14762 return(result);
14763}
14764fpl_platform_api int64_t fplAtomicExchangeS64(volatile int64_t *target, const int64_t value) {
14765 __sync_synchronize();
14766 int64_t result = __sync_lock_test_and_set(target, value);
14767 return(result);
14768}
14769
14770fpl_platform_api uint32_t fplAtomicFetchAndAddU32(volatile uint32_t *value, const uint32_t addend) {
14771 fplAssert(value != fpl_null);
14772 uint32_t result = __sync_fetch_and_add(value, addend);
14773 return (result);
14774}
14775fpl_platform_api uint64_t fplAtomicFetchAndAddU64(volatile uint64_t *value, const uint64_t addend) {
14776 fplAssert(value != fpl_null);
14777 uint64_t result = __sync_fetch_and_add(value, addend);
14778 return (result);
14779}
14780fpl_platform_api int32_t fplAtomicFetchAndAddS32(volatile int32_t *value, const int32_t addend) {
14781 fplAssert(value != fpl_null);
14782 int32_t result = __sync_fetch_and_add(value, addend);
14783 return (result);
14784}
14785fpl_platform_api int64_t fplAtomicFetchAndAddS64(volatile int64_t *value, const int64_t addend) {
14786 fplAssert(value != fpl_null);
14787 int64_t result = __sync_fetch_and_add(value, addend);
14788 return (result);
14789}
14790
14791fpl_platform_api uint32_t fplAtomicAddAndFetchU32(volatile uint32_t *value, const uint32_t addend) {
14792 fplAssert(value != fpl_null);
14793 uint32_t result = __sync_add_and_fetch(value, addend);
14794 return (result);
14795}
14796fpl_platform_api int32_t fplAtomicAddAndFetchS32(volatile int32_t *value, const int32_t addend) {
14797 fplAssert(value != fpl_null);
14798 int32_t result = __sync_add_and_fetch(value, addend);
14799 return (result);
14800}
14801fpl_platform_api uint64_t fplAtomicAddAndFetchU64(volatile uint64_t *value, const uint64_t addend) {
14802 fplAssert(value != fpl_null);
14803 uint64_t result = __sync_add_and_fetch(value, addend);
14804 return (result);
14805}
14806fpl_platform_api int64_t fplAtomicAddAndFetchS64(volatile int64_t *value, const int64_t addend) {
14807 fplAssert(value != fpl_null);
14808 int64_t result = __sync_add_and_fetch(value, addend);
14809 return (result);
14810}
14811
14812fpl_platform_api uint32_t fplAtomicIncrementU32(volatile uint32_t *value) {
14813 fplAssert(value != fpl_null);
14814 uint32_t result = __sync_add_and_fetch(value, 1);
14815 return (result);
14816}
14817fpl_platform_api uint64_t fplAtomicIncrementU64(volatile uint64_t *value) {
14818 fplAssert(value != fpl_null);
14819 uint64_t result = __sync_add_and_fetch(value, 1);
14820 return (result);
14821}
14822fpl_platform_api int32_t fplAtomicIncrementS32(volatile int32_t *value) {
14823 fplAssert(value != fpl_null);
14824 int32_t result = __sync_add_and_fetch(value, 1);
14825 return (result);
14826}
14827fpl_platform_api int64_t fplAtomicIncrementS64(volatile int64_t *value) {
14828 fplAssert(value != fpl_null);
14829 int64_t result = __sync_add_and_fetch(value, 1);
14830 return (result);
14831}
14832
14833fpl_platform_api uint32_t fplAtomicCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange) {
14834 fplAssert(dest != fpl_null);
14835 uint32_t result = __sync_val_compare_and_swap(dest, comparand, exchange);
14836 return (result);
14837}
14838fpl_platform_api uint64_t fplAtomicCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange) {
14839 fplAssert(dest != fpl_null);
14840 uint64_t result = __sync_val_compare_and_swap(dest, comparand, exchange);
14841 return (result);
14842}
14843fpl_platform_api int32_t fplAtomicCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange) {
14844 fplAssert(dest != fpl_null);
14845 int32_t result = __sync_val_compare_and_swap(dest, comparand, exchange);
14846 return (result);
14847}
14848fpl_platform_api int64_t fplAtomicCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange) {
14849 fplAssert(dest != fpl_null);
14850 int64_t result = __sync_val_compare_and_swap(dest, comparand, exchange);
14851 return (result);
14852}
14853
14854fpl_platform_api bool fplAtomicIsCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange) {
14855 fplAssert(dest != fpl_null);
14856 bool result = __sync_bool_compare_and_swap(dest, comparand, exchange);
14857 return (result);
14858}
14859fpl_platform_api bool fplAtomicIsCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange) {
14860 fplAssert(dest != fpl_null);
14861 bool result = __sync_bool_compare_and_swap(dest, comparand, exchange);
14862 return (result);
14863}
14864fpl_platform_api bool fplAtomicIsCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange) {
14865 fplAssert(dest != fpl_null);
14866 bool result = __sync_bool_compare_and_swap(dest, comparand, exchange);
14867 return (result);
14868}
14869fpl_platform_api bool fplAtomicIsCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange) {
14870 fplAssert(dest != fpl_null);
14871 bool result = __sync_bool_compare_and_swap(dest, comparand, exchange);
14872 return (result);
14873}
14874
14875fpl_platform_api uint32_t fplAtomicLoadU32(volatile uint32_t *source) {
14876 uint32_t result = __sync_add_and_fetch(source, 0);
14877 return(result);
14878}
14879fpl_platform_api uint64_t fplAtomicLoadU64(volatile uint64_t *source) {
14880 uint64_t result = __sync_add_and_fetch(source, 0);
14881 return(result);
14882}
14883fpl_platform_api int32_t fplAtomicLoadS32(volatile int32_t *source) {
14884 int32_t result = __sync_add_and_fetch(source, 0);
14885 return(result);
14886}
14887fpl_platform_api int64_t fplAtomicLoadS64(volatile int64_t *source) {
14888 int64_t result = __sync_add_and_fetch(source, 0);
14889 return(result);
14890}
14891
14892fpl_platform_api void fplAtomicStoreU32(volatile uint32_t *dest, const uint32_t value) {
14893 __sync_synchronize();
14894 __sync_lock_test_and_set(dest, value);
14895}
14896fpl_platform_api void fplAtomicStoreU64(volatile uint64_t *dest, const uint64_t value) {
14897 __sync_synchronize();
14898 __sync_lock_test_and_set(dest, value);
14899}
14900fpl_platform_api void fplAtomicStoreS32(volatile int32_t *dest, const int32_t value) {
14901 __sync_synchronize();
14902 __sync_lock_test_and_set(dest, value);
14903}
14904fpl_platform_api void fplAtomicStoreS64(volatile int64_t *dest, const int64_t value) {
14905 __sync_synchronize();
14906 __sync_lock_test_and_set(dest, value);
14907}
14908#else
14909# error "This POSIX compiler/platform is not supported!"
14910#endif
14911
14912//
14913// POSIX Timings
14914//
14916 fplTimestamp result = fplZeroInit;
14917 struct timespec t;
14918 clock_gettime(CLOCK_MONOTONIC, &t);
14919 result.posix.seconds = (uint64_t)t.tv_sec;
14920 result.posix.nanoSeconds = (int64_t)t.tv_nsec;
14921 return(result);
14922}
14923
14925 uint64_t deltaSeconds = finish.posix.seconds - start.posix.seconds;
14926 int64_t deltaNanos = finish.posix.nanoSeconds - start.posix.nanoSeconds;
14927 if (deltaNanos < 0) {
14928 --deltaSeconds;
14929 deltaNanos += 1000000000L;
14930 }
14931 fplSeconds result = (fplSeconds)deltaSeconds + ((fplSeconds)deltaNanos * 1e-9);
14932 return(result);
14933}
14934
14936 struct timeval tv;
14937 gettimeofday(&tv, fpl_null);
14938 fplMilliseconds result = (fplMilliseconds)(tv.tv_sec * 1000 + ((uint64_t)tv.tv_usec / 1000));
14939 return(result);
14940}
14941
14942//
14943// POSIX Threading
14944//
14946 FPL__CheckArgumentNull(thread, false);
14947 FPL__CheckPlatform(false);
14948 const fpl__PlatformAppState *appState = fpl__global__AppState;
14949 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
14950 if (thread->isValid && (fplGetThreadState(thread) != fplThreadState_Stopped)) {
14951 pthread_t threadHandle = thread->internalHandle.posixThread;
14952 if (pthreadApi->pthread_kill(threadHandle, 0) == 0) {
14953 pthreadApi->pthread_join(threadHandle, fpl_null);
14954 }
14955 thread->isValid = false;
14956 fplAtomicStoreU32((volatile uint32_t *)&thread->currentState, (uint32_t)fplThreadState_Stopped);
14957 return true;
14958 } else {
14959 return false;
14960 }
14961}
14962
14964 FPL__CheckPlatform(0);
14965 const fpl__PlatformAppState *appState = fpl__global__AppState;
14966 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
14967 pthread_t currentThread = pthreadApi->pthread_self();
14968 uint32_t result = (uint32_t)currentThread;
14969 return(result);
14970}
14971
14973 FPL__CheckArgumentNull(parameters, fpl_null);
14974 FPL__CheckPlatform(fpl_null);
14975 const fpl__PlatformAppState *appState = fpl__global__AppState;
14976 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
14977 fplThreadHandle *result = fpl_null;
14978 fplThreadHandle *thread = fpl__GetFreeThread();
14979 if (thread != fpl_null) {
14981 thread->parameters = *parameters;
14982 thread->isValid = false;
14983 thread->isStopping = false;
14984
14985 fplThreadPriority initialPriority = parameters->priority;
14986
14987 // Setup attributes
14988 pthread_attr_t *attrPtr = fpl_null;
14989 pthread_attr_t attr;
14990 if (pthreadApi->pthread_attr_init(&attr) == 0) {
14991 // Scheduler policy
14992 int scheduler = -1;
14993 if (initialPriority == fplThreadPriority_Idle) {
14994#if defined(SCHED_IDLE)
14995 if (pthreadApi->pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) {
14996 scheduler = SCHED_IDLE;
14997 }
14998#endif
14999 } else if (initialPriority >= fplThreadPriority_High) {
15000#if defined(SCHED_FIFO)
15001 if ((scheduler == -1) && (pthreadApi->pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0)) {
15002 scheduler = SCHED_FIFO;
15003 }
15004#endif
15005#if defined(SCHED_RR)
15006 if ((scheduler == -1) && (pthreadApi->pthread_attr_setschedpolicy(&attr, SCHED_RR) == 0)) {
15007 scheduler = SCHED_RR;
15008 }
15009#endif
15010 } else {
15011 // @TODO(final): Is sched_getscheduler supported for all POSIX standards?
15012 scheduler = sched_getscheduler(0);
15013 }
15014
15015 // Stack size
15016 if (parameters->stackSize > 0) {
15017 pthreadApi->pthread_attr_setstacksize(&attr, parameters->stackSize);
15018 }
15019
15020 // Priority
15021 if (scheduler != -1) {
15022 struct sched_param sched;
15023 if (pthreadApi->pthread_attr_getschedparam(&attr, &sched) == 0) {
15024 int maxThreadPrioCount = (fplThreadPriority_Last - fplThreadPriority_First) + 1;
15025 fplAssert(maxThreadPrioCount > 0);
15026
15027 int minPrio = sched_get_priority_min(scheduler);
15028 int maxPrio = sched_get_priority_max(scheduler);
15029 int range = maxPrio - minPrio;
15030 int step = range / maxThreadPrioCount;
15031
15032 int priority;
15033 if (initialPriority == fplThreadPriority_Lowest) {
15034 priority = minPrio;
15035 } else if (initialPriority == fplThreadPriority_Highest) {
15036 priority = maxPrio;
15037 } else {
15038 int threadPrioNumber = (int)(initialPriority - fplThreadPriority_First) + 1;
15039 priority = minPrio + threadPrioNumber * step;
15040 if (priority < minPrio) {
15041 priority = minPrio;
15042 } else if (priority > maxPrio) {
15043 priority = maxPrio;
15044 }
15045 }
15046
15047 sched.sched_priority = priority;
15048 pthreadApi->pthread_attr_setschedparam(&attr, &sched);
15049 }
15050 }
15051
15052 attrPtr = &attr;
15053 }
15054
15055 // Create thread
15057 int threadRes;
15058 do {
15059 threadRes = pthreadApi->pthread_create(&thread->internalHandle.posixThread, attrPtr, fpl__PosixThreadProc, (void *)thread);
15060 } while (threadRes == EAGAIN);
15061 if (threadRes != 0) {
15062 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating thread, error code: %d", threadRes);
15063 }
15064 if (threadRes == 0) {
15065 thread->id = (uint32_t)thread->internalHandle.posixThread;
15066 thread->isValid = true;
15067 result = thread;
15068 } else {
15069 fplClearStruct(thread);
15070 }
15071 } else {
15072 FPL__ERROR(FPL__MODULE_THREADING, "All %d threads are in use, you cannot create until you free one", FPL_MAX_THREAD_COUNT);
15073 }
15074 return(result);
15075}
15076
15078 FPL__CheckArgumentNull(runFunc, fpl_null);
15079 fplThreadParameters parameters = fplZeroInit;
15080 parameters.runFunc = runFunc;
15081 parameters.userData = data;
15082 fplThreadHandle *result = fplThreadCreateWithParameters(&parameters);
15083 return(result);
15084}
15085
15087 FPL__CheckPlatform(fplThreadPriority_Unknown);
15088 const fpl__PlatformAppState *appState = fpl__global__AppState;
15089 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15090
15091 pthread_t curThread = pthreadApi->pthread_self();
15092
15093 int currentSchedulerPolicy;
15094 struct sched_param params;
15095 if (pthreadApi->pthread_getschedparam(curThread, &currentSchedulerPolicy, &params) != 0) {
15096 FPL__ERROR(FPL__MODULE_THREADING, "Failed getting scheduler parameters for pthread '%d'", curThread);
15098 }
15099
15100 int maxThreadPrioCount = (fplThreadPriority_Last - fplThreadPriority_First) + 1;
15101 fplAssert(maxThreadPrioCount > 0);
15102
15103 int minPrio = sched_get_priority_min(currentSchedulerPolicy);
15104 int maxPrio = sched_get_priority_max(currentSchedulerPolicy);
15105 int range = maxPrio - minPrio;
15106 int step = range / maxThreadPrioCount;
15107
15108 int currentPrio = params.sched_priority;
15109
15110 fplThreadPriority result;
15111 if (minPrio == maxPrio || currentPrio == minPrio) {
15112 result = fplThreadPriority_Lowest;
15113 } else if (currentPrio == maxPrio) {
15115 } else {
15116 int index = (currentPrio - minPrio) / step;
15117 fplAssert(index >= 0 && index < maxThreadPrioCount);
15118 result = (fplThreadPriority)index;
15119 }
15120
15121 return(result);
15122}
15123
15125 if (newPriority == fplThreadPriority_Unknown) return(false);
15126 FPL__CheckPlatform(false);
15127 const fpl__PlatformAppState *appState = fpl__global__AppState;
15128 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15129
15130 pthread_t curThread = pthreadApi->pthread_self();
15131
15132 int currentSchedulerPolicy;
15133 struct sched_param params;
15134 if (pthreadApi->pthread_getschedparam(curThread, &currentSchedulerPolicy, &params) != 0) {
15135 FPL__ERROR(FPL__MODULE_THREADING, "Failed getting scheduler parameters for pthread '%d'", curThread);
15136 return(false);
15137 }
15138
15139 // Build policy table
15140 int newSchedulerPolicies[3];
15141 int schedulerPolicyCount = 0;
15142 switch (newPriority) {
15146 newSchedulerPolicies[schedulerPolicyCount++] = currentSchedulerPolicy;
15147 break;
15149#if defined(SCHED_RR)
15150 newSchedulerPolicies[schedulerPolicyCount++] = SCHED_RR;
15151#endif
15152 newSchedulerPolicies[schedulerPolicyCount++] = currentSchedulerPolicy;
15153 break;
15155#if defined(SCHED_FIFO)
15156 newSchedulerPolicies[schedulerPolicyCount++] = SCHED_FIFO;
15157#endif
15158#if defined(SCHED_RR)
15159 newSchedulerPolicies[schedulerPolicyCount++] = SCHED_RR;
15160#endif
15161 newSchedulerPolicies[schedulerPolicyCount++] = currentSchedulerPolicy;
15162 break;
15163 default:
15164 break;
15165 }
15166
15167 int maxThreadPrioCount = (fplThreadPriority_Last - fplThreadPriority_First) + 1;
15168 fplAssert(maxThreadPrioCount > 0);
15169
15170 // Bring priority in range of 1-N
15171 int threadPrioNumber = (int)(newPriority - fplThreadPriority_First) + 1;
15172
15173 for (int i = 0; i < schedulerPolicyCount; ++i) {
15174 int policy = newSchedulerPolicies[i];
15175 int minPrio = sched_get_priority_min(policy);
15176 int maxPrio = sched_get_priority_max(policy);
15177 int range = maxPrio - minPrio;
15178 int step = range / maxThreadPrioCount;
15179
15180 int priority;
15181 if (newPriority == fplThreadPriority_Lowest) {
15182 priority = minPrio;
15183 } else if (newPriority == fplThreadPriority_Highest) {
15184 priority = maxPrio;
15185 } else {
15186 priority = minPrio + threadPrioNumber * step;
15187 if (priority < minPrio) {
15188 priority = minPrio;
15189 } else if (priority > maxPrio) {
15190 priority = maxPrio;
15191 }
15192 }
15193 params.sched_priority = priority;
15194 if (pthreadApi->pthread_setschedparam(curThread, policy, &params) == 0) {
15195 return(true); // Finally we found a policy and priority which is supported
15196 } else {
15197 FPL__WARNING(FPL__MODULE_THREADING, "Failed to set thread priority '%d' with policy '%d'", priority, policy);
15198 }
15199 }
15200
15201 return(false);
15202}
15203
15205 FPL__CheckPlatform(false);
15206 const fpl__PlatformAppState *appState = fpl__global__AppState;
15207 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15208 bool result = false;
15209 if ((thread != fpl_null) && (fplGetThreadState(thread) != fplThreadState_Stopped)) {
15210 pthread_t threadHandle = thread->internalHandle.posixThread;
15211
15212 // @NOTE(final): We optionally use the GNU extension "pthread_timedjoin_np" to support joining with a timeout.
15213 int joinRes;
15214 if ((pthreadApi->pthread_timedjoin_np != fpl_null) && (timeout != FPL_TIMEOUT_INFINITE)) {
15215 struct timespec t;
15216 fpl__InitWaitTimeSpec(timeout, &t);
15217 joinRes = pthreadApi->pthread_timedjoin_np(threadHandle, fpl_null, &t);
15218 } else {
15219 joinRes = pthreadApi->pthread_join(threadHandle, fpl_null);
15220 }
15221
15222 result = (joinRes == 0);
15223 }
15224 return (result);
15225}
15226
15227fpl_platform_api bool fplThreadWaitForAll(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
15228 bool result = fpl__PosixThreadWaitForMultiple(threads, count, count, stride, timeout);
15229 return(result);
15230}
15231
15232fpl_platform_api bool fplThreadWaitForAny(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
15233 bool result = fpl__PosixThreadWaitForMultiple(threads, 1, count, stride, timeout);
15234 return(result);
15235}
15236
15238 FPL__CheckPlatform(false);
15239 const fpl__PlatformAppState *appState = fpl__global__AppState;
15240 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15241 bool result;
15242 if (pthreadApi->pthread_yield != fpl_null) {
15243 result = (pthreadApi->pthread_yield() == 0);
15244 } else {
15245 result = (sched_yield() == 0);
15246 }
15247 return(result);
15248}
15249
15250fpl_platform_api void fplThreadSleep(const uint32_t milliseconds) {
15251 uint32_t ms;
15252 uint32_t s;
15253 if (milliseconds > 1000) {
15254 s = milliseconds / 1000;
15255 ms = milliseconds % 1000;
15256 } else {
15257 s = 0;
15258 ms = milliseconds;
15259 }
15260 struct timespec input, output;
15261 input.tv_sec = s;
15262 input.tv_nsec = ms * 1000000;
15263 nanosleep(&input, &output);
15264}
15265
15267 FPL__CheckArgumentNull(mutex, false);
15268 if (mutex->isValid) {
15269 FPL__ERROR(FPL__MODULE_THREADING, "Mutex '%p' is already initialized", mutex);
15270 return false;
15271 }
15272 FPL__CheckPlatform(false);
15273 const fpl__PlatformAppState *appState = fpl__global__AppState;
15274 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15275 fplAssert(sizeof(fpl__POSIXMutexHandle) >= sizeof(pthread_mutex_t));
15276 pthread_mutex_t mutexHandle;
15277 int mutexRes = fpl__PosixMutexCreate(pthreadApi, &mutexHandle);
15278 if (mutexRes != 0) {
15279 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating POSIX condition");
15280 return false;
15281 }
15282 fplClearStruct(mutex);
15283 fplMemoryCopy(&mutexHandle, sizeof(mutexHandle), &mutex->internalHandle.posixMutex);
15284 mutex->isValid = true;
15285 return(true);
15286}
15287
15289 FPL__CheckPlatformNoRet();
15290 const fpl__PlatformAppState *appState = fpl__global__AppState;
15291 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15292 if ((mutex != fpl_null) && mutex->isValid) {
15293 pthread_mutex_t *handle = (pthread_mutex_t *)&mutex->internalHandle.posixMutex;
15294 pthreadApi->pthread_mutex_destroy(handle);
15295 fplClearStruct(mutex);
15296 }
15297}
15298
15300 FPL__CheckArgumentNull(mutex, false);
15301 FPL__CheckPlatform(false);
15302 const fpl__PlatformAppState *appState = fpl__global__AppState;
15303 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15304 bool result = false;
15305 if (mutex->isValid) {
15306 pthread_mutex_t *handle = (pthread_mutex_t *)&mutex->internalHandle.posixMutex;
15307 result = fpl__PosixMutexLock(pthreadApi, handle);
15308 }
15309 return (result);
15310}
15311
15313 FPL__CheckArgumentNull(mutex, false);
15314 FPL__CheckPlatform(false);
15315 const fpl__PlatformAppState *appState = fpl__global__AppState;
15316 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15317 bool result = false;
15318 if (mutex->isValid) {
15319 pthread_mutex_t *handle = (pthread_mutex_t *)&mutex->internalHandle.posixMutex;
15320 result = fpl__PosixMutexTryLock(pthreadApi, handle);
15321 }
15322 return (result);
15323}
15324
15326 FPL__CheckArgumentNull(mutex, false);
15327 FPL__CheckPlatform(false);
15328 const fpl__PlatformAppState *appState = fpl__global__AppState;
15329 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15330 bool result = false;
15331 if (mutex->isValid) {
15332 pthread_mutex_t *handle = (pthread_mutex_t *)&mutex->internalHandle.posixMutex;
15333 result = fpl__PosixMutexUnlock(pthreadApi, handle);
15334 }
15335 return (result);
15336}
15337
15339 FPL__CheckArgumentNull(condition, false);
15340 FPL__CheckPlatform(false);
15341 const fpl__PlatformAppState *appState = fpl__global__AppState;
15342 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15343 fplAssert(sizeof(fpl__POSIXConditionVariable) >= sizeof(pthread_cond_t));
15344 pthread_cond_t handle = PTHREAD_COND_INITIALIZER;
15345 int condRes;
15346 do {
15347 condRes = pthreadApi->pthread_cond_init(&handle, fpl_null);
15348 } while (condRes == EAGAIN);
15349 if (condRes == 0) {
15350 fplClearStruct(condition);
15351 fplMemoryCopy(&handle, sizeof(handle), &condition->internalHandle.posixCondition);
15352 condition->isValid = true;
15353 }
15354 return(condition->isValid);
15355}
15356
15358 FPL__CheckPlatformNoRet();
15359 const fpl__PlatformAppState *appState = fpl__global__AppState;
15360 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15361 if ((condition != fpl_null) && condition->isValid) {
15362 pthread_cond_t *handle = (pthread_cond_t *)&condition->internalHandle.posixCondition;
15363 pthreadApi->pthread_cond_destroy(handle);
15364 fplClearStruct(condition);
15365 }
15366}
15367
15369 FPL__CheckArgumentNull(condition, false);
15370 FPL__CheckArgumentNull(mutex, false);
15371 if (!condition->isValid) {
15372 FPL__ERROR(FPL__MODULE_THREADING, "Condition is not valid");
15373 return false;
15374 }
15375 if (!mutex->isValid) {
15376 FPL__ERROR(FPL__MODULE_THREADING, "Mutex is not valid");
15377 return false;
15378 }
15379 FPL__CheckPlatform(false);
15380 const fpl__PlatformAppState *appState = fpl__global__AppState;
15381 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15382 pthread_cond_t *cond = (pthread_cond_t *)&condition->internalHandle.posixCondition;
15383 pthread_mutex_t *mut = (pthread_mutex_t *)&mutex->internalHandle.posixMutex;
15384 bool result;
15385 if (timeout == FPL_TIMEOUT_INFINITE) {
15386 result = pthreadApi->pthread_cond_wait(cond, mut) == 0;
15387 } else {
15388 struct timespec t;
15389 fpl__InitWaitTimeSpec(timeout, &t);
15390 result = pthreadApi->pthread_cond_timedwait(cond, mut, &t) == 0;
15391 }
15392 return(result);
15393}
15394
15396 FPL__CheckArgumentNull(condition, false);
15397 if (!condition->isValid) {
15398 FPL__ERROR(FPL__MODULE_THREADING, "Condition is not valid");
15399 return false;
15400 }
15401 FPL__CheckPlatform(false);
15402 const fpl__PlatformAppState *appState = fpl__global__AppState;
15403 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15404 pthread_cond_t *handle = (pthread_cond_t *)&condition->internalHandle.posixCondition;
15405 bool result = pthreadApi->pthread_cond_signal(handle) == 0;
15406 return(result);
15407}
15408
15410 FPL__CheckArgumentNull(condition, false);
15411 if (!condition->isValid) {
15412 FPL__ERROR(FPL__MODULE_THREADING, "Condition is not valid");
15413 return false;
15414 }
15415 FPL__CheckPlatform(false);
15416 const fpl__PlatformAppState *appState = fpl__global__AppState;
15417 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15418 pthread_cond_t *handle = (pthread_cond_t *)&condition->internalHandle.posixCondition;
15419 bool result = pthreadApi->pthread_cond_broadcast(handle) == 0;
15420 return(result);
15421}
15422
15423fpl_platform_api bool fplSemaphoreInit(fplSemaphoreHandle *semaphore, const uint32_t initialValue) {
15424 FPL__CheckArgumentNull(semaphore, false);
15425 FPL__CheckArgumentMax(initialValue, UINT32_MAX, false);
15426 if (semaphore->isValid) {
15427 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is already initialized", semaphore);
15428 return false;
15429 }
15430 FPL__CheckPlatform(false);
15431
15432 const fpl__PlatformAppState *appState = fpl__global__AppState;
15433 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15434 sem_t handle;
15435 int res = pthreadApi->sem_init(&handle, 0, (int)initialValue);
15436 if (res < 0) {
15437 FPL__ERROR(FPL__MODULE_THREADING, "Failed creating semaphore");
15438 return false;
15439 }
15440 fplClearStruct(semaphore);
15441 fplMemoryCopy(&handle, sizeof(handle), &semaphore->internalHandle.posixHandle);
15442 semaphore->isValid = true;
15443 return true;
15444}
15445
15447 FPL__CheckPlatformNoRet();
15448 const fpl__PlatformAppState *appState = fpl__global__AppState;
15449 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15450 if (semaphore != fpl_null) {
15451 if (semaphore->isValid) {
15452 sem_t *handle = (sem_t *)&semaphore->internalHandle.posixHandle;
15453 pthreadApi->sem_destroy(handle);
15454 }
15455 fplClearStruct(semaphore);
15456 }
15457}
15458
15460 FPL__CheckArgumentNull(semaphore, false);
15461 if (!semaphore->isValid) {
15462 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
15463 return false;
15464 }
15465 FPL__CheckPlatform(false);
15466 const fpl__PlatformAppState *appState = fpl__global__AppState;
15467 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15468 sem_t *handle = (sem_t *)&semaphore->internalHandle.posixHandle;
15469 int res;
15470 if (timeout == FPL_TIMEOUT_INFINITE) {
15471 res = pthreadApi->sem_wait(handle);
15472 } else {
15473 struct timespec t;
15474 fpl__InitWaitTimeSpec(timeout, &t);
15475 res = pthreadApi->sem_timedwait(handle, &t);
15476 }
15477 bool result = res == 0;
15478 return(result);
15479}
15480
15482 FPL__CheckArgumentNull(semaphore, false);
15483 if (!semaphore->isValid) {
15484 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
15485 return false;
15486 }
15487 FPL__CheckPlatform(false);
15488 const fpl__PlatformAppState *appState = fpl__global__AppState;
15489 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15490 sem_t *handle = (sem_t *)&semaphore->internalHandle.posixHandle;
15491 int res = pthreadApi->sem_trywait(handle);
15492 bool result = (res == 0);
15493 return(result);
15494}
15495
15497 FPL__CheckArgumentNull(semaphore, false);
15498 if (!semaphore->isValid) {
15499 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
15500 return false;
15501 }
15502 FPL__CheckPlatform(0);
15503 const fpl__PlatformAppState *appState = fpl__global__AppState;
15504 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15505 sem_t *handle = (sem_t *)&semaphore->internalHandle.posixHandle;
15506 int value = 0;
15507 int res = pthreadApi->sem_getvalue(handle, &value);
15508 if (res < 0) {
15509 return 0;
15510 }
15511 return((int32_t)value);
15512}
15513
15515 FPL__CheckArgumentNull(semaphore, false);
15516 if (!semaphore->isValid) {
15517 FPL__ERROR(FPL__MODULE_THREADING, "Semaphore '%p' is not valid", semaphore);
15518 return false;
15519 }
15520 FPL__CheckPlatform(0);
15521 const fpl__PlatformAppState *appState = fpl__global__AppState;
15522 const fpl__PThreadApi *pthreadApi = &appState->posix.pthreadApi;
15523 sem_t *handle = (sem_t *)&semaphore->internalHandle.posixHandle;
15524 int res = pthreadApi->sem_post(handle);
15525 bool result = (res == 0);
15526 return(result);
15527}
15528
15529//
15530// POSIX Library
15531//
15532fpl_platform_api bool fplDynamicLibraryLoad(const char *libraryFilePath, fplDynamicLibraryHandle *outHandle) {
15533 bool result = false;
15534 if (libraryFilePath != fpl_null && outHandle != fpl_null) {
15535 void *p = dlopen(libraryFilePath, FPL__POSIX_DL_LOADTYPE);
15536 if (p != fpl_null) {
15537 fplClearStruct(outHandle);
15538 outHandle->internalHandle.posixLibraryHandle = p;
15539 outHandle->isValid = true;
15540 result = true;
15541 }
15542 }
15543 return(result);
15544}
15545
15546fpl_platform_api void *fplGetDynamicLibraryProc(const fplDynamicLibraryHandle *handle, const char *name) {
15547 void *result = fpl_null;
15548 if ((handle != fpl_null) && (handle->internalHandle.posixLibraryHandle != fpl_null) && (name != fpl_null)) {
15549 void *p = handle->internalHandle.posixLibraryHandle;
15550 result = dlsym(p, name);
15551 }
15552 return(result);
15553}
15554
15556 if ((handle != fpl_null) && (handle->internalHandle.posixLibraryHandle != fpl_null)) {
15557 void *p = handle->internalHandle.posixLibraryHandle;
15558 dlclose(p);
15559 fplClearStruct(handle);
15560 }
15561}
15562
15563//
15564// POSIX Memory
15565//
15566fpl_platform_api void *fplMemoryAllocate(const size_t size) {
15567 FPL__CheckArgumentZero(size, fpl_null);
15568 // @NOTE(final): MAP_ANONYMOUS ensures that the memory is cleared to zero.
15569 // Allocate empty memory to hold the size + some arbitary padding + the actual data
15570 size_t newSize = sizeof(size_t) + FPL__MEMORY_PADDING + size;
15571 void *basePtr = mmap(fpl_null, newSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
15572 // Write the size at the beginning
15573 *(size_t *)basePtr = newSize;
15574 // The resulting address starts after the arbitary padding
15575 void *result = (uint8_t *)basePtr + sizeof(size_t) + FPL__MEMORY_PADDING;
15576 return(result);
15577}
15578
15579fpl_platform_api void fplMemoryFree(void *ptr) {
15580 FPL__CheckArgumentNullNoRet(ptr);
15581 // Free the base pointer which is stored to the left at the start of the size_t
15582 void *basePtr = (void *)((uint8_t *)ptr - (FPL__MEMORY_PADDING + sizeof(size_t)));
15583 size_t storedSize = *(size_t *)basePtr;
15584 munmap(basePtr, storedSize);
15585}
15586
15587//
15588// POSIX Files
15589//
15590fpl_platform_api bool fplFileOpenBinary(const char *filePath, fplFileHandle *outHandle) {
15591 FPL__CheckArgumentNull(outHandle, false);
15592 if (filePath != fpl_null) {
15593 int posixFileHandle;
15594 do {
15595 posixFileHandle = open(filePath, O_RDONLY);
15596 } while (posixFileHandle == -1 && errno == EINTR);
15597 if (posixFileHandle != -1) {
15598 fplClearStruct(outHandle);
15599 outHandle->isValid = true;
15600 outHandle->internalHandle.posixFileHandle = posixFileHandle;
15601 return true;
15602 }
15603 }
15604 return false;
15605}
15606
15607fpl_platform_api bool fplFileCreateBinary(const char *filePath, fplFileHandle *outHandle) {
15608 FPL__CheckArgumentNull(outHandle, false);
15609 if (filePath != fpl_null) {
15610 int posixFileHandle;
15611 do {
15612 posixFileHandle = open(filePath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
15613 } while (posixFileHandle == -1 && errno == EINTR);
15614 if (posixFileHandle != -1) {
15615 outHandle->isValid = true;
15616 outHandle->internalHandle.posixFileHandle = posixFileHandle;
15617 return true;
15618 }
15619 }
15620 return false;
15621}
15622
15623fpl_platform_api size_t fpl__PosixWriteFileBlock(const fplFileHandle *fileHandle, void *sourceBuffer, const size_t sourceSize) {
15624 FPL__CheckArgumentNull(fileHandle, 0);
15625 FPL__CheckArgumentZero(sourceSize, 0);
15626 FPL__CheckArgumentNull(sourceBuffer, 0);
15627 if (!fileHandle->internalHandle.posixFileHandle) {
15628 FPL__ERROR(FPL__MODULE_FILES, "File handle is not opened for writing");
15629 return 0;
15630 }
15631 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15632 ssize_t res;
15633 do {
15634 res = write(posixFileHandle, sourceBuffer, sourceSize);
15635 } while (res == -1 && errno == EINTR);
15636 size_t result = 0;
15637 if (res != -1) {
15638 result = (size_t)res;
15639 }
15640 return(result);
15641}
15642
15643fpl_platform_api uint32_t fplFileReadBlock32(const fplFileHandle *fileHandle, const uint32_t sizeToRead, void *targetBuffer, const uint32_t maxTargetBufferSize) {
15644 FPL__CheckArgumentNull(fileHandle, 0);
15645 FPL__CheckArgumentZero(sizeToRead, 0);
15646 FPL__CheckArgumentNull(targetBuffer, 0);
15647 if (!fileHandle->internalHandle.posixFileHandle) {
15648 FPL__ERROR(FPL__MODULE_FILES, "File handle is not opened for reading");
15649 return 0;
15650 }
15651 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15652 ssize_t res;
15653 do {
15654 res = read(posixFileHandle, targetBuffer, sizeToRead);
15655 } while (res == -1 && errno == EINTR);
15656 uint32_t result = 0;
15657 if (res != -1) {
15658 result = (uint32_t)res;
15659 }
15660 return(result);
15661}
15662
15663fpl_platform_api uint64_t fplFileReadBlock64(const fplFileHandle *fileHandle, const uint64_t sizeToRead, void *targetBuffer, const uint64_t maxTargetBufferSize) {
15664 FPL__CheckArgumentNull(fileHandle, 0);
15665 FPL__CheckArgumentZero(sizeToRead, 0);
15666 FPL__CheckArgumentNull(targetBuffer, 0);
15667 if (!fileHandle->internalHandle.posixFileHandle) {
15668 FPL__ERROR(FPL__MODULE_FILES, "File handle is not opened for reading");
15669 return 0;
15670 }
15671 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15672 uint64_t result = 0;
15673 uint64_t remainingSize = sizeToRead;
15674 uint64_t bufferPos = 0;
15675 const uint64_t MaxValue = (uint64_t)(size_t)-1;
15676 while (remainingSize > 0) {
15677 uint8_t *target = (uint8_t *)targetBuffer + bufferPos;
15678 size_t size = fplMin(remainingSize, MaxValue);
15679 ssize_t res;
15680 do {
15681 res = read(posixFileHandle, target, size);
15682 } while (res == -1 && errno == EINTR);
15683 if (res != -1) {
15684 result += res;
15685 } else {
15686 break;
15687 }
15688 remainingSize -= res;
15689 bufferPos += res;
15690 }
15691 return(result);
15692}
15693
15694fpl_platform_api uint32_t fplFileWriteBlock32(const fplFileHandle *fileHandle, void *sourceBuffer, const uint32_t sourceSize) {
15695 FPL__CheckArgumentNull(fileHandle, 0);
15696 FPL__CheckArgumentZero(sourceSize, 0);
15697 FPL__CheckArgumentNull(sourceBuffer, 0);
15698 if (!fileHandle->internalHandle.posixFileHandle) {
15699 FPL__ERROR(FPL__MODULE_FILES, "File handle is not opened for writing");
15700 return 0;
15701 }
15702 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15703 ssize_t res;
15704 do {
15705 res = write(posixFileHandle, sourceBuffer, sourceSize);
15706 } while (res == -1 && errno == EINTR);
15707 uint32_t result = 0;
15708 if (res != -1) {
15709 result = (uint32_t)res;
15710 }
15711 return(result);
15712}
15713
15714fpl_platform_api uint64_t fplFileWriteBlock64(const fplFileHandle *fileHandle, void *sourceBuffer, const uint64_t sourceSize) {
15715 FPL__CheckArgumentNull(fileHandle, 0);
15716 FPL__CheckArgumentZero(sourceSize, 0);
15717 FPL__CheckArgumentNull(sourceBuffer, 0);
15718 if (!fileHandle->internalHandle.posixFileHandle) {
15719 FPL__ERROR(FPL__MODULE_FILES, "File handle is not opened for writing");
15720 return 0;
15721 }
15722 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15723 uint64_t result = 0;
15724 uint64_t remainingSize = sourceSize;
15725 uint64_t bufferPos = 0;
15726 const uint64_t MaxValue = (uint64_t)(size_t)-1;
15727 while (remainingSize > 0) {
15728 uint8_t *source = (uint8_t *)sourceBuffer + bufferPos;
15729 size_t size = fplMin(remainingSize, MaxValue);
15730 ssize_t res;
15731 do {
15732 res = write(posixFileHandle, source, size);
15733 } while (res == -1 && errno == EINTR);
15734 if (res != -1) {
15735 result += res;
15736 }
15737 remainingSize -= res;
15738 bufferPos += res;
15739 }
15740 return(result);
15741}
15742
15743fpl_platform_api uint32_t fplFileSetPosition32(const fplFileHandle *fileHandle, const int32_t position, const fplFilePositionMode mode) {
15744 FPL__CheckArgumentNull(fileHandle, 0);
15745 uint32_t result = 0;
15746 if (fileHandle->internalHandle.posixFileHandle) {
15747 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15748 int whence = SEEK_SET;
15749 if (mode == fplFilePositionMode_Current) {
15750 whence = SEEK_CUR;
15751 } else if (mode == fplFilePositionMode_End) {
15752 whence = SEEK_END;
15753 }
15754 off_t r = lseek(posixFileHandle, position, whence);
15755 result = (uint32_t)r;
15756 }
15757 return(result);
15758}
15759
15760fpl_platform_api uint64_t fplFileSetPosition64(const fplFileHandle *fileHandle, const int64_t position, const fplFilePositionMode mode) {
15761 FPL__CheckArgumentNull(fileHandle, 0);
15762 uint64_t result = 0;
15763 if (fileHandle->internalHandle.posixFileHandle) {
15764 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15765 int whence = SEEK_SET;
15766 if (mode == fplFilePositionMode_Current) {
15767 whence = SEEK_CUR;
15768 } else if (mode == fplFilePositionMode_End) {
15769 whence = SEEK_END;
15770 }
15771 fpl__off64_t r = fpl__lseek64(posixFileHandle, position, whence);
15772 result = (uint64_t)r;
15773 }
15774 return(result);
15775}
15776
15777fpl_platform_api uint32_t fplFileGetPosition32(const fplFileHandle *fileHandle) {
15778 FPL__CheckArgumentNull(fileHandle, 0);
15779 uint32_t result = 0;
15780 if (fileHandle->internalHandle.posixFileHandle) {
15781 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15782 off_t res = lseek(posixFileHandle, 0, SEEK_CUR);
15783 if (res != -1) {
15784 result = (uint32_t)res;
15785 }
15786 }
15787 return(result);
15788}
15789
15790fpl_platform_api uint64_t fplFileGetPosition64(const fplFileHandle *fileHandle) {
15791 FPL__CheckArgumentNull(fileHandle, 0);
15792 uint64_t result = 0;
15793 if (fileHandle->internalHandle.posixFileHandle) {
15794 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15795 fpl__off64_t r = fpl__lseek64(posixFileHandle, 0, SEEK_CUR);
15796 if (r != -1) {
15797 result = (uint64_t)r;
15798 }
15799 }
15800 return(result);
15801}
15802
15804 FPL__CheckArgumentNull(fileHandle, false);
15805 bool result = false;
15806 if (fileHandle->internalHandle.posixFileHandle) {
15807 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15808 result = fsync(posixFileHandle) == 0;
15809 }
15810 return(result);
15811}
15812
15814 if ((fileHandle != fpl_null) && fileHandle->internalHandle.posixFileHandle) {
15815 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15816 close(posixFileHandle);
15817 fplClearStruct(fileHandle);
15818 }
15819}
15820
15821fpl_platform_api uint32_t fplFileGetSizeFromPath32(const char *filePath) {
15822 uint32_t result = 0;
15823 if (filePath != fpl_null) {
15824 int posixFileHandle;
15825 do {
15826 posixFileHandle = open(filePath, O_RDONLY);
15827 } while (posixFileHandle == -1 && errno == EINTR);
15828 if (posixFileHandle != -1) {
15829 off_t res = lseek(posixFileHandle, 0, SEEK_END);
15830 if (res != -1) {
15831 result = (uint32_t)res;
15832 }
15833 close(posixFileHandle);
15834 }
15835 }
15836 return(result);
15837}
15838
15839fpl_platform_api uint64_t fplFileGetSizeFromPath64(const char *filePath) {
15840 uint64_t result = 0;
15841 if (filePath != fpl_null) {
15842 int posixFileHandle;
15843 do {
15844 posixFileHandle = open(filePath, O_RDONLY);
15845 } while (posixFileHandle == -1 && errno == EINTR);
15846 if (posixFileHandle != -1) {
15847 fpl__off64_t r = fpl__lseek64(posixFileHandle, 0, SEEK_END);
15848 if (r != -1) {
15849 result = (uint64_t)r;
15850 }
15851 close(posixFileHandle);
15852 }
15853 }
15854 return(result);
15855}
15856
15857fpl_platform_api uint32_t fplFileGetSizeFromHandle32(const fplFileHandle *fileHandle) {
15858 uint32_t result = 0;
15859 if (fileHandle != fpl_null && fileHandle->internalHandle.posixFileHandle) {
15860 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15861 off_t curPos = lseek(posixFileHandle, 0, SEEK_CUR);
15862 if (curPos != -1) {
15863 result = (uint32_t)lseek(posixFileHandle, 0, SEEK_END);
15864 lseek(posixFileHandle, curPos, SEEK_SET);
15865 }
15866 }
15867 return(result);
15868}
15869
15870fpl_platform_api uint64_t fplFileGetSizeFromHandle64(const fplFileHandle *fileHandle) {
15871 uint64_t result = 0;
15872 if (fileHandle != fpl_null && fileHandle->internalHandle.posixFileHandle) {
15873 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15874 fpl__off64_t curPos = fpl__lseek64(posixFileHandle, 0, SEEK_CUR);
15875 if (curPos != -1) {
15876 result = (uint64_t)fpl__lseek64(posixFileHandle, 0, SEEK_END);
15877 fpl__lseek64(posixFileHandle, curPos, SEEK_SET);
15878 }
15879 }
15880 return(result);
15881}
15882
15883fpl_internal uint64_t fpl__PosixConvertTimeToUnixTimeStamp(const time_t secs) {
15884 uint64_t result = (uint64_t)secs;
15885 return(result);
15886}
15887
15888fpl_platform_api bool fplFileGetTimestampsFromPath(const char *filePath, fplFileTimeStamps *outStamps) {
15889 FPL__CheckArgumentNull(outStamps, false);
15890 bool result = false;
15891 if (filePath != fpl_null) {
15892 struct stat statBuf;
15893 if (stat(filePath, &statBuf) != -1) {
15894 outStamps->creationTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_ctime);
15895 outStamps->lastAccessTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_atime);
15896 outStamps->lastModifyTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_mtime);
15897 result = true;
15898 }
15899 }
15900 return(result);
15901}
15902
15904 FPL__CheckArgumentNull(fileHandle, false);
15905 FPL__CheckArgumentNull(outStamps, false);
15906 bool result = false;
15907 if (fileHandle != fpl_null && fileHandle->internalHandle.posixFileHandle) {
15908 int posixFileHandle = fileHandle->internalHandle.posixFileHandle;
15909 struct stat statBuf;
15910 if (fstat(posixFileHandle, &statBuf) != -1) {
15911 outStamps->creationTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_ctime);
15912 outStamps->lastAccessTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_atime);
15913 outStamps->lastModifyTime = fpl__PosixConvertTimeToUnixTimeStamp(statBuf.st_mtime);
15914 result = true;
15915 }
15916 }
15917 return(result);
15918}
15919
15920fpl_platform_api bool fplFileSetTimestamps(const char *filePath, const fplFileTimeStamps *timeStamps) {
15921 // @IMPLEMENT(final/POSIX): fplSetFileTimestamps
15922 return(false);
15923}
15924
15925fpl_platform_api bool fplFileExists(const char *filePath) {
15926 bool result = false;
15927 if (filePath != fpl_null) {
15928 result = access(filePath, F_OK) != -1;
15929 }
15930 return(result);
15931}
15932
15933fpl_platform_api bool fplFileCopy(const char *sourceFilePath, const char *targetFilePath, const bool overwrite) {
15934 FPL__CheckArgumentNull(sourceFilePath, false);
15935 FPL__CheckArgumentNull(targetFilePath, false);
15936 if (access(sourceFilePath, F_OK) == -1) {
15937 FPL__ERROR(FPL__MODULE_FILES, "Source file '%s' does not exits", sourceFilePath);
15938 return false;
15939 }
15940 if (!overwrite && access(sourceFilePath, F_OK) != -1) {
15941 FPL__ERROR(FPL__MODULE_FILES, "Target file '%s' already exits", targetFilePath);
15942 return false;
15943 }
15944 int inputFileHandle;
15945 do {
15946 inputFileHandle = open(sourceFilePath, O_RDONLY);
15947 } while (inputFileHandle == -1 && errno == EINTR);
15948 if (inputFileHandle == -1) {
15949 FPL__ERROR(FPL__MODULE_FILES, "Failed open source file '%s', error code: %d", sourceFilePath, inputFileHandle);
15950 return false;
15951 }
15952 int outputFileHandle;
15953 do {
15954 outputFileHandle = open(targetFilePath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
15955 } while (outputFileHandle == -1 && errno == EINTR);
15956 if (outputFileHandle == -1) {
15957 close(inputFileHandle);
15958 FPL__ERROR(FPL__MODULE_FILES, "Failed creating target file '%s', error code: %d", targetFilePath, inputFileHandle);
15959 return false;
15960 }
15961 uint8_t buffer[1024 * 10]; // 10 kb buffer
15962 for (;;) {
15963 ssize_t readbytes;
15964 do {
15965 readbytes = read(inputFileHandle, buffer, fplArrayCount(buffer));
15966 } while (readbytes == -1 && errno == EINTR);
15967 if (readbytes > 0) {
15968 ssize_t writtenBytes;
15969 do {
15970 writtenBytes = write(outputFileHandle, buffer, readbytes);
15971 } while (writtenBytes == -1 && errno == EINTR);
15972 if (writtenBytes <= 0) {
15973 break;
15974 }
15975 } else {
15976 break;
15977 }
15978 }
15979 close(outputFileHandle);
15980 close(inputFileHandle);
15981 return(true);
15982}
15983
15984fpl_platform_api bool fplFileMove(const char *sourceFilePath, const char *targetFilePath) {
15985 FPL__CheckArgumentNull(sourceFilePath, false);
15986 FPL__CheckArgumentNull(targetFilePath, false);
15987 bool result = rename(sourceFilePath, targetFilePath) == 0;
15988 return(result);
15989}
15990
15991fpl_platform_api bool fplFileDelete(const char *filePath) {
15992 FPL__CheckArgumentNull(filePath, false);
15993 bool result = unlink(filePath) == 0;
15994 return(result);
15995}
15996
15997fpl_platform_api bool fplDirectoryExists(const char *path) {
15998 bool result = false;
15999 if (path != fpl_null) {
16000 struct stat sb;
16001 result = (stat(path, &sb) == 0) && S_ISDIR(sb.st_mode);
16002 }
16003 return(result);
16004}
16005
16006fpl_platform_api bool fplDirectoriesCreate(const char *path) {
16007 FPL__CheckArgumentNull(path, false);
16008 bool result = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) == 0;
16009 return(result);
16010}
16011fpl_platform_api bool fplRemoveDirectory(const char *path) {
16012 FPL__CheckArgumentNull(path, false);
16013 bool result = rmdir(path) == 0;
16014 return(result);
16015}
16016
16017fpl_internal void fpl__PosixFillFileEntry(struct dirent *dp, fplFileEntry *entry) {
16018 fplAssert((dp != fpl_null) && (entry != fpl_null));
16019 fplCopyString(dp->d_name, entry->name, fplArrayCount(entry->name));
16022 entry->size = 0;
16023 entry->permissions.umask = 0;
16024 char fullPath[FPL_MAX_PATH_LENGTH];
16025 fplCopyString(entry->internalRoot.rootPath, fullPath, fplArrayCount(fullPath));
16026 fplEnforcePathSeparatorLen(fullPath, fplArrayCount(fullPath));
16027 fplStringAppend(dp->d_name, fullPath, fplArrayCount(fullPath));
16028 struct stat sb;
16029 if (stat(fullPath, &sb) == 0) {
16030 if (S_ISDIR(sb.st_mode)) {
16032 } else if (S_ISREG(sb.st_mode)) {
16033 entry->type = fplFileEntryType_File;
16034 }
16035 entry->size = (size_t)sb.st_size;
16036 if (dp->d_name[0] == '.') {
16037 // @NOTE(final): Any filename starting with dot is hidden in POSIX
16039 }
16040 if (sb.st_mode & S_IRUSR) {
16042 }
16043 if (sb.st_mode & S_IWUSR) {
16045 }
16046 if (sb.st_mode & S_IXUSR) {
16048 }
16049 if (sb.st_mode & S_IRGRP) {
16051 }
16052 if (sb.st_mode & S_IWGRP) {
16054 }
16055 if (sb.st_mode & S_IXGRP) {
16057 }
16058 if (sb.st_mode & S_IROTH) {
16060 }
16061 if (sb.st_mode & S_IWOTH) {
16063 }
16064 if (sb.st_mode & S_IXOTH) {
16066 }
16067 }
16068}
16069
16070fpl_platform_api bool fplDirectoryListBegin(const char *path, const char *filter, fplFileEntry *entry) {
16071 FPL__CheckArgumentNull(path, false);
16072 FPL__CheckArgumentNull(entry, false);
16073 DIR *dir = opendir(path);
16074 if (dir == fpl_null) {
16075 return false;
16076 }
16077 if (fplGetStringLength(filter) == 0) {
16078 filter = "*";
16079 }
16080 fplClearStruct(entry);
16081 entry->internalHandle.posixDirHandle = dir;
16082 entry->internalRoot.rootPath = path;
16083 entry->internalRoot.filter = filter;
16084 bool result = fplDirectoryListNext(entry);
16085 return(result);
16086}
16087
16089 FPL__CheckArgumentNull(entry, false);
16090 bool result = false;
16091 if (entry->internalHandle.posixDirHandle != fpl_null) {
16092 DIR *dirHandle = (DIR *)entry->internalHandle.posixDirHandle;
16093 struct dirent *dp = readdir(dirHandle);
16094 do {
16095 if (dp == fpl_null) {
16096 break;
16097 }
16098 bool isMatch = fplIsStringMatchWildcard(dp->d_name, entry->internalRoot.filter);
16099 if (isMatch) {
16100 break;
16101 }
16102 dp = readdir(dirHandle);
16103 } while (dp != fpl_null);
16104 if (dp == fpl_null) {
16105 closedir(dirHandle);
16106 fplClearStruct(entry);
16107 } else {
16108 fpl__PosixFillFileEntry(dp, entry);
16109 result = true;
16110 }
16111 }
16112 return(result);
16113}
16114
16116 FPL__CheckArgumentNullNoRet(entry);
16117 if (entry->internalHandle.posixDirHandle != fpl_null) {
16118 DIR *dirHandle = (DIR *)entry->internalHandle.posixDirHandle;
16119 closedir(dirHandle);
16120 fplClearStruct(entry);
16121 }
16122}
16123
16124//
16125// POSIX Operating System
16126//
16127fpl_platform_api size_t fplSessionGetUsername(char *nameBuffer, const size_t maxNameBufferLen) {
16128 uid_t uid = geteuid();
16129 struct passwd *pw = getpwuid(uid);
16130 size_t result = 0;
16131 if (pw != fpl_null) {
16132 size_t nameLen = result = fplGetStringLength(pw->pw_name);
16133 if (nameLen > 0 && nameBuffer != fpl_null) {
16134 size_t requiredLen = nameLen + 1;
16135 FPL__CheckArgumentMin(maxNameBufferLen, requiredLen, 0);
16136 fplCopyStringLen(pw->pw_name, nameLen, nameBuffer, maxNameBufferLen);
16137 }
16138 }
16139 return(result);
16140}
16141
16143 size_t result = sysconf(_SC_NPROCESSORS_ONLN);
16144 return(result);
16145}
16146
16147fpl_platform_api size_t fplGetExecutableFilePath(char *destPath, const size_t maxDestLen) {
16148 const char *procNames[] = {
16149 "/proc/self/exe",
16150 "/proc/curproc/exe",
16151 "/proc/curproc/file",
16152 };
16153 char buf[FPL_MAX_PATH_LENGTH];
16154 size_t result = 0;
16155 for (int i = 0; i < fplArrayCount(procNames); ++i) {
16156 const char *procName = procNames[i];
16157 if (readlink(procName, buf, fplArrayCount(buf) - 1)) {
16158 int len = fplGetStringLength(buf);
16159 if (len > 0) {
16160 char *lastP = buf + (len - 1);
16161 char *p = lastP;
16162 while (p != buf) {
16163 if (*p == '/') {
16164 len = (lastP - buf) + 1;
16165 break;
16166 }
16167 --p;
16168 }
16169 result = len;
16170 if (destPath != fpl_null) {
16171 size_t requiredLen = len + 1;
16172 FPL__CheckArgumentMin(maxDestLen, requiredLen, 0);
16173 fplCopyStringLen(buf, len, destPath, maxDestLen);
16174 break;
16175 }
16176 }
16177 }
16178 }
16179 return(result);
16180}
16181
16182fpl_platform_api size_t fplGetHomePath(char *destPath, const size_t maxDestLen) {
16183 const char *homeDir = getenv("HOME");
16184 if (homeDir == fpl_null) {
16185 int userId = getuid();
16186 struct passwd *userPwd = getpwuid(userId);
16187 homeDir = userPwd->pw_dir;
16188 }
16189 size_t result = fplGetStringLength(homeDir);
16190 if (destPath != fpl_null) {
16191 size_t requiredLen = result + 1;
16192 FPL__CheckArgumentMin(maxDestLen, requiredLen, 0);
16193 fplCopyStringLen(homeDir, result, destPath, maxDestLen);
16194 }
16195 return(result);
16196}
16197
16198
16201 struct utsname nameInfos;
16202 if (uname(&nameInfos) == 0) {
16203 const char *machineName = nameInfos.machine;
16204 if (fplIsStringEqual("x86_64", machineName) || fplIsStringEqual("amd64", machineName)) {
16205 result = fplCPUArchType_x86_64;
16206 } else if (fplIsStringEqual("x86", machineName) || fplIsStringEqual("i386", machineName) || fplIsStringEqual("i686", machineName)) {
16207 result = fplCPUArchType_x86;
16208 } else if (fplIsStringEqual("ia64", machineName) || fplIsStringEqual("i686-64", machineName)) {
16209 result = fplCPUArchType_x64;
16210 } else {
16211 if (fplIsStringEqualLen("armv", 4, machineName, 4)) {
16212 const char *m = machineName + 4;
16213 const char *p = m;
16214 while (*p >= '0' && *p <= '9') {
16215 ++p;
16216 }
16217 size_t l = p - m;
16218 int32_t version = fplStringToS32Len(m, l);
16219 if (version == 6) {
16220 result = fplCPUArchType_Arm32;
16221 } else if (version >= 7) {
16222 result = fplCPUArchType_Arm64;
16223 }
16224 }
16225 }
16226 }
16227 return(result);
16228}
16229
16231 bool result = false;
16232 struct utsname nameInfos;
16233 if (uname(&nameInfos) == 0) {
16234 const char *kernelName = nameInfos.sysname;
16235 const char *kernelVersion = nameInfos.release;
16236 const char *systemName = nameInfos.version;
16237 fplCopyString(kernelName, outInfos->osName, fplArrayCount(outInfos->osName));
16238 fplCopyString(systemName, outInfos->distributionName, fplArrayCount(outInfos->distributionName));
16239 fpl__ParseVersionString(kernelVersion, &outInfos->osVersion);
16240
16241 // @TODO(final/POSIX): Get distro version
16242 // /etc/os-release
16243
16244 result = true;
16245 }
16246 return(result);
16247}
16248#endif // FPL_SUBPLATFORM_POSIX
16249
16250// ############################################################################
16251//
16252// > STD_STRINGS_SUBPLATFORM
16253//
16254// Strings Implementation using C Standard Library
16255//
16256// ############################################################################
16257#if defined(FPL_SUBPLATFORM_STD_STRINGS)
16258// @NOTE(final): stdio.h is already included
16259fpl_platform_api size_t fplWideStringToUTF8String(const wchar_t *wideSource, const size_t wideSourceLen, char *utf8Dest, const size_t maxUtf8DestLen) {
16260 // @NOTE(final): Expect locale to be UTF-8
16261 FPL__CheckArgumentNull(wideSource, 0);
16262 FPL__CheckArgumentZero(wideSourceLen, 0);
16263 size_t result = wcstombs(fpl_null, wideSource, wideSourceLen);
16264 if (utf8Dest != fpl_null) {
16265 size_t requiredLen = result + 1;
16266 FPL__CheckArgumentMin(maxUtf8DestLen, requiredLen, 0);
16267 wcstombs(utf8Dest, wideSource, wideSourceLen);
16268 utf8Dest[result] = 0;
16269 }
16270 return(result);
16271}
16272fpl_platform_api size_t fplUTF8StringToWideString(const char *utf8Source, const size_t utf8SourceLen, wchar_t *wideDest, const size_t maxWideDestLen) {
16273 // @NOTE(final): Expect locale to be UTF-8
16274 FPL__CheckArgumentNull(utf8Source, 0);
16275 FPL__CheckArgumentZero(utf8SourceLen, 0);
16276 size_t result = mbstowcs(fpl_null, utf8Source, utf8SourceLen);
16277 if (wideDest != fpl_null) {
16278 size_t requiredLen = result + 1;
16279 FPL__CheckArgumentMin(maxWideDestLen, requiredLen, 0);
16280 mbstowcs(wideDest, utf8Source, utf8SourceLen);
16281 wideDest[result] = 0;
16282 }
16283 return(result);
16284}
16285#endif // FPL_SUBPLATFORM_STD_STRINGS
16286
16287// ############################################################################
16288//
16289// > STD_CONSOLE_SUBPLATFORM
16290//
16291// Console Implementation using C Standard Library
16292//
16293// ############################################################################
16294#if defined(FPL_SUBPLATFORM_STD_CONSOLE)
16295// @NOTE(final): stdio.h is already included
16296fpl_platform_api void fplConsoleOut(const char *text) {
16297 if (text != fpl_null) {
16298 fprintf(stdout, "%s", text);
16299 }
16300}
16301fpl_platform_api void fplConsoleError(const char *text) {
16302 if (text != fpl_null) {
16303 fprintf(stderr, "%s", text);
16304 }
16305}
16307 int c = getchar();
16308 const char result = (c >= 0 && c < 256) ? (char)c : 0;
16309 return(result);
16310}
16311#endif // FPL_SUBPLATFORM_STD_CONSOLE
16312
16313// ############################################################################
16314//
16315// > X11_SUBPLATFORM
16316//
16317// ############################################################################
16318#if defined(FPL_SUBPLATFORM_X11)
16319
16320#define FPL__X11_DEFAULT_WINDOW_WIDTH 400
16321#define FPL__X11_DEFAULT_WINDOW_HEIGHT 400
16322
16323fpl_internal void fpl__X11ReleaseSubplatform(fpl__X11SubplatformState *subplatform) {
16324 fplAssert(subplatform != fpl_null);
16325 fpl__UnloadX11Api(&subplatform->api);
16326}
16327
16328fpl_internal bool fpl__X11InitSubplatform(fpl__X11SubplatformState *subplatform) {
16329 fplAssert(subplatform != fpl_null);
16330 if (!fpl__LoadX11Api(&subplatform->api)) {
16331 FPL__ERROR(FPL__MODULE_X11, "Failed loading x11 api");
16332 return false;
16333 }
16334 return true;
16335}
16336
16337fpl_internal void fpl__X11ReleaseWindow(const fpl__X11SubplatformState *subplatform, fpl__X11WindowState *windowState) {
16338 fplAssert((subplatform != fpl_null) && (windowState != fpl_null));
16339 const fpl__X11Api *x11Api = &subplatform->api;
16340 if (windowState->window) {
16341 FPL_LOG_DEBUG("X11", "Hide window '%d' from display '%p'", (int)windowState->window, windowState->display);
16342 x11Api->XUnmapWindow(windowState->display, windowState->window);
16343 FPL_LOG_DEBUG("X11", "Destroy window '%d' on display '%p'", (int)windowState->window, windowState->display);
16344 x11Api->XDestroyWindow(windowState->display, windowState->window);
16345 x11Api->XFlush(windowState->display);
16346 windowState->window = 0;
16347 }
16348 if (windowState->colorMap) {
16349 FPL_LOG_DEBUG("X11", "Release color map '%d' from display '%p'", (int)windowState->colorMap, windowState->display);
16350 x11Api->XFreeColormap(windowState->display, windowState->colorMap);
16351 windowState->colorMap = 0;
16352 }
16353 if (windowState->display) {
16354 FPL_LOG_DEBUG("X11", "Close display '%p'", windowState->display);
16355 x11Api->XCloseDisplay(windowState->display);
16356 windowState->display = fpl_null;
16357
16358#if 0
16359 FPL_LOG_DEBUG("X11", "Restore previous error handler '%p'", windowState->lastErrorHandler);
16360 x11Api->XSetErrorHandler(windowState->lastErrorHandler);
16361#endif
16362
16363 }
16364 fplClearStruct(windowState);
16365}
16366
16367fpl_internal fplKey fpl__X11TranslateKeySymbol(const KeySym keySym) {
16368 switch (keySym) {
16369 case XK_BackSpace:
16370 return fplKey_Backspace;
16371 case XK_Tab:
16372 return fplKey_Tab;
16373
16374 case XK_Return:
16375 return fplKey_Return;
16376
16377 case XK_Pause:
16378 return fplKey_Pause;
16379 case XK_Caps_Lock:
16380 return fplKey_CapsLock;
16381
16382 case XK_Escape:
16383 return fplKey_Escape;
16384 case XK_space:
16385 return fplKey_Space;
16386 case XK_Page_Up:
16387 return fplKey_PageUp;
16388 case XK_Page_Down:
16389 return fplKey_PageDown;
16390 case XK_End:
16391 return fplKey_End;
16392 case XK_Home:
16393 return fplKey_Home;
16394 case XK_Left:
16395 return fplKey_Left;
16396 case XK_Up:
16397 return fplKey_Up;
16398 case XK_Right:
16399 return fplKey_Right;
16400 case XK_Down:
16401 return fplKey_Down;
16402 case XK_Print:
16403 return fplKey_Print;
16404 case XK_Insert:
16405 return fplKey_Insert;
16406 case XK_Delete:
16407 return fplKey_Delete;
16408
16409 case XK_0:
16410 return fplKey_0;
16411 case XK_1:
16412 return fplKey_1;
16413 case XK_2:
16414 return fplKey_2;
16415 case XK_3:
16416 return fplKey_3;
16417 case XK_4:
16418 return fplKey_4;
16419 case XK_5:
16420 return fplKey_5;
16421 case XK_6:
16422 return fplKey_6;
16423 case XK_7:
16424 return fplKey_7;
16425 case XK_8:
16426 return fplKey_8;
16427 case XK_9:
16428 return fplKey_9;
16429
16430 case XK_a:
16431 return fplKey_A;
16432 case XK_b:
16433 return fplKey_B;
16434 case XK_c:
16435 return fplKey_C;
16436 case XK_d:
16437 return fplKey_D;
16438 case XK_e:
16439 return fplKey_E;
16440 case XK_f:
16441 return fplKey_F;
16442 case XK_g:
16443 return fplKey_G;
16444 case XK_h:
16445 return fplKey_H;
16446 case XK_i:
16447 return fplKey_I;
16448 case XK_j:
16449 return fplKey_J;
16450 case XK_k:
16451 return fplKey_K;
16452 case XK_l:
16453 return fplKey_L;
16454 case XK_m:
16455 return fplKey_M;
16456 case XK_n:
16457 return fplKey_N;
16458 case XK_o:
16459 return fplKey_O;
16460 case XK_p:
16461 return fplKey_P;
16462 case XK_q:
16463 return fplKey_Q;
16464 case XK_r:
16465 return fplKey_R;
16466 case XK_s:
16467 return fplKey_S;
16468 case XK_t:
16469 return fplKey_T;
16470 case XK_u:
16471 return fplKey_U;
16472 case XK_v:
16473 return fplKey_V;
16474 case XK_w:
16475 return fplKey_W;
16476 case XK_x:
16477 return fplKey_X;
16478 case XK_y:
16479 return fplKey_Y;
16480 case XK_z:
16481 return fplKey_Z;
16482
16483 case XK_Super_L:
16484 return fplKey_LeftSuper;
16485 case XK_Super_R:
16486 return fplKey_RightSuper;
16487
16488 case XK_KP_0:
16489 return fplKey_NumPad0;
16490 case XK_KP_1:
16491 return fplKey_NumPad1;
16492 case XK_KP_2:
16493 return fplKey_NumPad2;
16494 case XK_KP_3:
16495 return fplKey_NumPad3;
16496 case XK_KP_4:
16497 return fplKey_NumPad4;
16498 case XK_KP_5:
16499 return fplKey_NumPad5;
16500 case XK_KP_6:
16501 return fplKey_NumPad6;
16502 case XK_KP_7:
16503 return fplKey_NumPad7;
16504 case XK_KP_8:
16505 return fplKey_NumPad8;
16506 case XK_KP_9:
16507 return fplKey_NumPad9;
16508 case XK_KP_Multiply:
16509 return fplKey_Multiply;
16510 case XK_KP_Add:
16511 return fplKey_Add;
16512 case XK_KP_Subtract:
16513 return fplKey_Substract;
16514 case XK_KP_Delete:
16515 return fplKey_Decimal;
16516 case XK_KP_Divide:
16517 return fplKey_Divide;
16518 case XK_F1:
16519 return fplKey_F1;
16520 case XK_F2:
16521 return fplKey_F2;
16522 case XK_F3:
16523 return fplKey_F3;
16524 case XK_F4:
16525 return fplKey_F4;
16526 case XK_F5:
16527 return fplKey_F5;
16528 case XK_F6:
16529 return fplKey_F6;
16530 case XK_F7:
16531 return fplKey_F7;
16532 case XK_F8:
16533 return fplKey_F8;
16534 case XK_F9:
16535 return fplKey_F9;
16536 case XK_F10:
16537 return fplKey_F10;
16538 case XK_F11:
16539 return fplKey_F11;
16540 case XK_F12:
16541 return fplKey_F12;
16542 case XK_F13:
16543 return fplKey_F13;
16544 case XK_F14:
16545 return fplKey_F14;
16546 case XK_F15:
16547 return fplKey_F15;
16548 case XK_F16:
16549 return fplKey_F16;
16550 case XK_F17:
16551 return fplKey_F17;
16552 case XK_F18:
16553 return fplKey_F18;
16554 case XK_F19:
16555 return fplKey_F19;
16556 case XK_F20:
16557 return fplKey_F20;
16558 case XK_F21:
16559 return fplKey_F21;
16560 case XK_F22:
16561 return fplKey_F22;
16562 case XK_F23:
16563 return fplKey_F23;
16564 case XK_F24:
16565 return fplKey_F24;
16566
16567 case XK_Shift_L:
16568 return fplKey_LeftShift;
16569 case XK_Shift_R:
16570 return fplKey_RightShift;
16571 case XK_Control_L:
16572 return fplKey_LeftControl;
16573 case XK_Control_R:
16574 return fplKey_RightControl;
16575 case XK_Meta_L:
16576 case XK_Alt_L:
16577 return fplKey_LeftAlt;
16578 case XK_Mode_switch:
16579 case XK_ISO_Level3_Shift:
16580 case XK_Meta_R:
16581 case XK_Alt_R:
16582 return fplKey_RightAlt;
16583
16584 case XK_comma:
16585 return fplKey_OemComma;
16586 case XK_period:
16587 return fplKey_OemPeriod;
16588 case XK_minus:
16589 return fplKey_OemMinus;
16590 case XK_plus:
16591 return fplKey_OemPlus;
16592
16593 // @TODO(final/X11): X11 map OEM1-OEM8 key
16594
16595 default:
16596 return fplKey_None;
16597 }
16598}
16599
16600fpl_internal void fpl__X11LoadWindowIcon(const fpl__X11Api *x11Api, fpl__X11WindowState *x11WinState, fplWindowSettings *windowSettings) {
16601 // @BUG(final/X11): Setting the window icon on X11 does not fail, but it does not show up in any of the bars in gnome/ubuntu the icon is always shown as "unset"
16602
16603 int iconSourceCount = 0;
16604 fplImageSource iconSources[2] = fplZeroInit;
16605
16606 if (windowSettings->icons[0].width > 0) {
16607 iconSources[iconSourceCount++] = windowSettings->icons[0];
16608 }
16609 if (windowSettings->icons[1].width > 0) {
16610 iconSources[iconSourceCount++] = windowSettings->icons[1];
16611 }
16612
16613 if (iconSourceCount > 0) {
16614 int targetSize = 0;
16615 for (int i = 0; i < iconSourceCount; ++i) {
16616 targetSize += 2 + iconSources[i].width * iconSources[i].height;
16617 }
16618
16619 long *data = (long *)fpl__AllocateTemporaryMemory(sizeof(long) * targetSize, 16);
16620 long *target = data;
16621
16622 for (int i = 0; i < iconSourceCount; ++i) {
16623 const fplImageSource *iconSource = iconSources + i;
16624 fplAssert(iconSource->type == fplImageType_RGBA);
16625 *target++ = (int32_t)iconSource->width;
16626 *target++ = (int32_t)iconSource->height;
16627 const uint32_t *source = (const uint32_t *)iconSource->data;
16628 for (int j = 0; j < iconSource->width * iconSource->height; ++j) {
16629 *target++ = (iconSource->data[j * 4 + 0] << 16) | (iconSource->data[j * 4 + 1] << 8) | (iconSource->data[j * 4 + 2] << 0) | (iconSource->data[j * 4 + 3] << 24);
16630 }
16631 }
16632
16633 x11Api->XChangeProperty(x11WinState->display, x11WinState->window, x11WinState->netWMIcon, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, targetSize);
16634
16635 fpl__ReleaseTemporaryMemory(data);
16636 } else {
16637 x11Api->XDeleteProperty(x11WinState->display, x11WinState->window, x11WinState->netWMIcon);
16638 }
16639
16640 x11Api->XFlush(x11WinState->display);
16641}
16642
16643#if 0
16644fpl_internal int fpl__X11ErrorHandler(Display *display, XErrorEvent *ev) {
16645 FPL__CheckPlatform(0);
16646 fpl__PlatformAppState *appState = fpl__global__AppState;
16647 const fpl__X11SubplatformState *subplatform = &appState->x11;
16648 const fpl__X11Api *x11Api = &subplatform->api;
16649 const fpl__X11WindowState *windowState = &appState->window.x11;
16650
16651 if (windowState->lastErrorHandler != fpl_null) {
16652 return windowState->lastErrorHandler(display, ev);
16653 }
16654
16655 return(0);
16656}
16657#endif
16658
16659fpl_internal bool fpl__X11InitWindow(const fplSettings *initSettings, fplWindowSettings *currentWindowSettings, fpl__PlatformAppState *appState, fpl__X11SubplatformState *subplatform, fpl__X11WindowState *windowState, const fpl__SetupWindowCallbacks *setupCallbacks) {
16660 fplAssert((initSettings != fpl_null) && (currentWindowSettings != fpl_null) && (appState != fpl_null) && (subplatform != fpl_null) && (windowState != fpl_null) && (setupCallbacks != fpl_null));
16661 const fpl__X11Api *x11Api = &subplatform->api;
16662
16663 FPL_LOG_DEBUG(FPL__MODULE_X11, "Set init threads");
16664 x11Api->XInitThreads();
16665
16666#if 0
16667 FPL_LOG_DEBUG("X11", "Enable error handler");
16668 windowState->lastErrorHandler = x11Api->XSetErrorHandler(fpl__X11ErrorHandler);
16669#endif
16670
16671 const fplWindowSettings *initWindowSettings = &initSettings->window;
16672
16673 FPL_LOG_DEBUG(FPL__MODULE_X11, "Open default Display");
16674 windowState->display = x11Api->XOpenDisplay(fpl_null);
16675 if (windowState->display == fpl_null) {
16676 FPL__ERROR(FPL__MODULE_X11, "Failed opening default Display!");
16677 return false;
16678 }
16679 FPL_LOG_DEBUG(FPL__MODULE_X11, "Successfully opened default Display: %p", windowState->display);
16680
16681 FPL_LOG_DEBUG(FPL__MODULE_X11, "Get default screen from display '%p'", windowState->display);
16682 windowState->screen = x11Api->XDefaultScreen(windowState->display);
16683 FPL_LOG_DEBUG(FPL__MODULE_X11, "Got default screen from display '%p': %d", windowState->display, windowState->screen);
16684
16685 FPL_LOG_DEBUG(FPL__MODULE_X11, "Get root window from display '%p' and screen '%d'", windowState->display, windowState->screen);
16686 windowState->root = x11Api->XRootWindow(windowState->display, windowState->screen);
16687 FPL_LOG_DEBUG(FPL__MODULE_X11, "Got root window from display '%p' and screen '%d': %d", windowState->display, windowState->screen, (int)windowState->root);
16688
16689 bool usePreSetupWindow = false;
16690 if (setupCallbacks->preSetup != fpl_null) {
16691 FPL_LOG_DEBUG(FPL__MODULE_X11, "Call Pre-Setup for Window");
16692 setupCallbacks->preSetup(appState, appState->initFlags, initSettings);
16693 }
16694
16695 Visual *visual = windowState->visual;
16696 int colorDepth = windowState->colorDepth;
16697 Colormap colormap;
16698 if (visual != fpl_null && colorDepth > 0) {
16699 FPL_LOG_DEBUG(FPL__MODULE_X11, "Got visual '%p' and color depth '%d' from pre-setup", visual, colorDepth);
16700 windowState->colorMap = colormap = x11Api->XCreateColormap(windowState->display, windowState->root, visual, AllocNone);
16701 } else {
16702 FPL_LOG_DEBUG(FPL__MODULE_X11, "Using default visual, color depth, colormap");
16703 windowState->visual = visual = x11Api->XDefaultVisual(windowState->display, windowState->screen);
16704 windowState->colorDepth = colorDepth = x11Api->XDefaultDepth(windowState->display, windowState->screen);
16705 windowState->colorMap = colormap = x11Api->XDefaultColormap(windowState->display, windowState->screen);
16706 }
16707
16708 FPL_LOG_DEBUG(FPL__MODULE_X11, "Using visual: %p", visual);
16709 FPL_LOG_DEBUG(FPL__MODULE_X11, "Using color depth: %d", colorDepth);
16710 FPL_LOG_DEBUG(FPL__MODULE_X11, "Using color map: %d", (int)colormap);
16711
16712 int flags = CWColormap | CWBorderPixel | CWEventMask | CWBitGravity | CWWinGravity;
16713
16714 // Window background, either as default or custom color
16715 unsigned long backgroundPixel;
16716 if (initWindowSettings->background.value == 0) {
16717 backgroundPixel = 0;
16718 } else {
16719 flags |= CWBackPixel;
16720 backgroundPixel = (unsigned long)((0xFF << 24) | (initWindowSettings->background.r << 16) | (initWindowSettings->background.g << 8) | initWindowSettings->background.b);
16721 }
16722
16723 XSetWindowAttributes swa = fplZeroInit;
16724 swa.colormap = colormap;
16725 swa.event_mask =
16726 StructureNotifyMask |
16727 ExposureMask | FocusChangeMask | VisibilityChangeMask |
16728 EnterWindowMask | LeaveWindowMask | PropertyChangeMask |
16729 KeyPressMask | KeyReleaseMask |
16730 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ButtonMotionMask;
16731 swa.background_pixel = backgroundPixel;
16732 swa.border_pixel = 0; // NOTE(final): Use default X11 border
16733 swa.bit_gravity = NorthWestGravity;
16734 swa.win_gravity = NorthWestGravity;
16735
16736 int windowX = 0;
16737 int windowY = 0;
16738 int windowWidth;
16739 int windowHeight;
16740 if ((initSettings->window.windowSize.width > 0) &&
16741 (initSettings->window.windowSize.height > 0)) {
16742 windowWidth = initSettings->window.windowSize.width;
16743 windowHeight = initSettings->window.windowSize.height;
16744 } else {
16745 windowWidth = FPL__X11_DEFAULT_WINDOW_WIDTH;
16746 windowHeight = FPL__X11_DEFAULT_WINDOW_HEIGHT;
16747 }
16748
16749 windowState->lastWindowStateInfo.state = fplWindowState_Normal;
16750 windowState->lastWindowStateInfo.visibility = fplWindowVisibilityState_Show;
16751 windowState->lastWindowStateInfo.position = fplStructInit(fplWindowPosition, windowWidth, windowHeight);
16752 windowState->lastWindowStateInfo.size = fplStructInit(fplWindowSize, windowX, windowY);
16753
16754 FPL_LOG_DEBUG(FPL__MODULE_X11, "Create window with (Display='%p', Root='%d', Size=%dx%d, Colordepth='%d', visual='%p', colormap='%d'", windowState->display, (int)windowState->root, windowWidth, windowHeight, colorDepth, visual, (int)swa.colormap);
16755 windowState->window = x11Api->XCreateWindow(windowState->display,
16756 windowState->root,
16757 windowX,
16758 windowY,
16759 windowWidth,
16760 windowHeight,
16761 0,
16762 colorDepth,
16763 InputOutput,
16764 visual,
16765 flags,
16766 &swa);
16767 if (!windowState->window) {
16768 FPL__ERROR(FPL__MODULE_X11, "Failed creating window with (Display='%p', Root='%d', Size=%dx%d, Colordepth='%d', visual='%p', colormap='%d'!", windowState->display, (int)windowState->root, windowWidth, windowHeight, colorDepth, visual, (int)swa.colormap);
16769 fpl__X11ReleaseWindow(subplatform, windowState);
16770 return false;
16771 }
16772 FPL_LOG_DEBUG(FPL__MODULE_X11, "Successfully created window with (Display='%p', Root='%d', Size=%dx%d, Colordepth='%d', visual='%p', colormap='%d': %d", windowState->display, (int)windowState->root, windowWidth, windowHeight, colorDepth, visual, (int)swa.colormap, (int)windowState->window);
16773
16774 // Type atoms
16775 windowState->utf8String = x11Api->XInternAtom(windowState->display, "UTF8_STRING", False);
16776
16777 // Window manager atoms
16778 windowState->wmDeleteWindow = x11Api->XInternAtom(windowState->display, "WM_DELETE_WINDOW", False);
16779 windowState->wmProtocols = x11Api->XInternAtom(windowState->display, "WM_PROTOCOLS", False);
16780 windowState->wmState = x11Api->XInternAtom(windowState->display, "WM_STATE", False);
16781 windowState->netWMPing = x11Api->XInternAtom(windowState->display, "_NET_WM_PING", False);
16782 windowState->netWMState = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE", False);
16783 windowState->netWMStateFocused = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE_FOCUSED", False);
16784 windowState->netWMStateFullscreen = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE_FULLSCREEN", False);
16785 windowState->netWMStateHidden = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE_HIDDEN", False);
16786 windowState->netWMStateMaximizedVert = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
16787 windowState->netWMStateMaximizedHorz = x11Api->XInternAtom(windowState->display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
16788 windowState->netWMPid = x11Api->XInternAtom(windowState->display, "_NET_WM_PID", False);
16789 windowState->netWMIcon = x11Api->XInternAtom(windowState->display, "_NET_WM_ICON", False);
16790 windowState->netWMName = x11Api->XInternAtom(windowState->display, "_NET_WM_NAME", False);
16791 windowState->netWMIconName = x11Api->XInternAtom(windowState->display, "_NET_WM_ICON_NAME", False);
16792 windowState->motifWMHints = x11Api->XInternAtom(windowState->display, "_MOTIF_WM_HINTS", False);
16793 // xdnd atoms
16794 windowState->xdndAware = x11Api->XInternAtom(windowState->display, "XdndAware", False);
16795 windowState->xdndEnter = x11Api->XInternAtom(windowState->display, "XdndEnter", False);
16796 windowState->xdndPosition = x11Api->XInternAtom(windowState->display, "XdndPosition", False);
16797 windowState->xdndStatus = x11Api->XInternAtom(windowState->display, "XdndStatus", False);
16798 windowState->xdndActionCopy = x11Api->XInternAtom(windowState->display, "XdndActionCopy", False);
16799 windowState->xdndDrop = x11Api->XInternAtom(windowState->display, "XdndDrop", False);
16800 windowState->xdndFinished = x11Api->XInternAtom(windowState->display, "XdndFinished", False);
16801 windowState->xdndSelection = x11Api->XInternAtom(windowState->display, "XdndSelection", False);
16802 windowState->xdndTypeList = x11Api->XInternAtom(windowState->display, "XdndTypeList", False);
16803 windowState->textUriList = x11Api->XInternAtom(windowState->display, "text/uri-list", False);
16804
16805 // Register window manager protocols
16806 {
16807 Atom protocols[] = {
16808 windowState->wmDeleteWindow,
16809 windowState->netWMPing
16810 };
16811 x11Api->XSetWMProtocols(windowState->display, windowState->window, protocols, fplArrayCount(protocols));
16812 }
16813
16814 // Declare our process id
16815 {
16816 const long pid = getpid();
16817 x11Api->XChangeProperty(windowState->display, windowState->window, windowState->netWMPid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1);
16818 }
16819
16820 char nameBuffer[FPL_MAX_NAME_LENGTH] = fplZeroInit;
16821 if (fplGetStringLength(initSettings->window.title) > 0) {
16822 fplCopyString(initSettings->window.title, nameBuffer, fplArrayCount(nameBuffer));
16823 } else {
16824 fplCopyString("Unnamed FPL X11 Window", nameBuffer, fplArrayCount(nameBuffer));
16825 }
16826 FPL_LOG_DEBUG(FPL__MODULE_X11, "Show window '%d' on display '%p' with title '%s'", (int)windowState->window, windowState->display, nameBuffer);
16827 fpl__X11LoadWindowIcon(x11Api, windowState, currentWindowSettings);
16828 fplSetWindowTitle(nameBuffer);
16829 x11Api->XMapWindow(windowState->display, windowState->window);
16830 x11Api->XFlush(windowState->display);
16831
16832 fplAssert(fplArrayCount(appState->window.keyMap) >= 256);
16833
16834 // @NOTE(final): Valid key range for XLib is 8 to 255
16835 FPL_LOG_DEBUG(FPL__MODULE_X11, "Build X11 Keymap");
16836 fplClearStruct(appState->window.keyMap);
16837 for (int keyCode = 8; keyCode <= 255; ++keyCode) {
16838 int dummy = 0;
16839 KeySym *keySyms = x11Api->XGetKeyboardMapping(windowState->display, keyCode, 1, &dummy);
16840 KeySym keySym = keySyms[0];
16841 fplKey mappedKey = fpl__X11TranslateKeySymbol(keySym);
16842 appState->window.keyMap[keyCode] = mappedKey;
16843 x11Api->XFree(keySyms);
16844 }
16845
16846 if (initSettings->window.isFullscreen) {
16848 }
16849
16850 // Announce support for Xdnd (drag and drop)
16851 {
16852 const Atom version = FPL__XDND_VERSION;
16853 x11Api->XChangeProperty(windowState->display, windowState->window, windowState->xdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&version, 1);
16854 }
16855
16856 appState->window.isRunning = true;
16857
16858 return true;
16859}
16860
16861fpl_internal fplKeyboardModifierFlags fpl__X11TranslateModifierFlags(const int state) {
16863 if (state & ShiftMask) {
16866 }
16867 if (state & ControlMask) {
16870 }
16871 if (state & Mod1Mask) {
16874 }
16875 if (state & Mod4Mask) {
16878 }
16879 return(result);
16880}
16881
16882fpl_internal unsigned long fpl__X11GetWindowProperty(const fpl__X11Api *x11Api, Display *display, Window window, Atom prop, Atom type, unsigned char **value) {
16883 Atom actualType;
16884 int actualFormat;
16885 unsigned long itemCount, bytesAfter;
16886 x11Api->XGetWindowProperty(display, window, prop, 0, LONG_MAX, False, type, &actualType, &actualFormat, &itemCount, &bytesAfter, value);
16887 return(itemCount);
16888}
16889
16890fpl_internal const int fpl__X11GetWMState(const fpl__X11Api *x11Api, fpl__X11WindowState *windowState) {
16891 struct { int state; Window icon; } *value = NULL;
16892 unsigned long numItems = fpl__X11GetWindowProperty(x11Api, windowState->display, windowState->window, windowState->wmState, windowState->wmState, (unsigned char **)&value);
16893 int state = WithdrawnState;
16894 if (value) {
16895 state = value->state;
16896 x11Api->XFree(value);
16897 }
16898 return state;
16899}
16900
16901#define fpl__X11NetWMStateHiddenFlag (1 << 0)
16902#define fpl__X11NetWMStateMaximizedFlag (1 << 1)
16903#define fpl__X11NetWMStateFullscreenFlag (1 << 2)
16904
16905fpl_internal unsigned int fpl__X11GetNetWMState(const fpl__X11Api *x11Api, fpl__X11WindowState *windowState) {
16906 Atom *atoms = NULL;
16907 unsigned long numItems = fpl__X11GetWindowProperty(x11Api, windowState->display, windowState->window, windowState->netWMState, XA_ATOM, (unsigned char **)&atoms);
16908 unsigned int flags = 0;
16909 if (atoms) {
16910 int i, maximized = 0;
16911 for (i = 0; i < numItems; ++i) {
16912 if (atoms[i] == windowState->netWMStateHidden) {
16913 flags |= fpl__X11NetWMStateHiddenFlag;
16914 } else if (atoms[i] == windowState->netWMStateMaximizedVert) {
16915 maximized |= 1;
16916 } else if (atoms[i] == windowState->netWMStateMaximizedHorz) {
16917 maximized |= 2;
16918 } else if (atoms[i] == windowState->netWMStateFullscreen) {
16919 flags |= fpl__X11NetWMStateFullscreenFlag;
16920 }
16921 }
16922 if (maximized == 3) {
16923 flags |= fpl__X11NetWMStateMaximizedFlag;
16924 }
16925 // additional visibility check for unmapped window
16926 {
16927 XWindowAttributes attr;
16928 fplMemorySet(&attr, 0, sizeof(attr));
16929 x11Api->XGetWindowAttributes(windowState->display, windowState->window, &attr);
16930 if (attr.map_state == IsUnmapped) {
16931 flags |= fpl__X11NetWMStateHiddenFlag;
16932 }
16933 }
16934 x11Api->XFree(atoms);
16935 }
16936 return flags;
16937}
16938
16939fpl_internal fpl__X11WindowStateInfo fpl__X11GetWindowStateInfo(const fpl__X11Api *x11Api, fpl__X11WindowState *windowState) {
16940 fpl__X11WindowStateInfo result = fplZeroInit;
16941 const int state = fpl__X11GetWMState(x11Api, windowState);
16942 unsigned int flags = fpl__X11GetNetWMState(x11Api, windowState);
16943 if (state == NormalState) {
16944 result.state = fplWindowState_Normal;
16945 } else if (state == IconicState) {
16946 result.state = fplWindowState_Iconify;
16947 }
16948 // reset visibility to default
16949 result.visibility = fplWindowVisibilityState_Show;
16950 if (flags & fpl__X11NetWMStateHiddenFlag) {
16951 result.visibility = fplWindowVisibilityState_Hide;
16952 }
16953 if (flags & fpl__X11NetWMStateFullscreenFlag) {
16954 result.state = fplWindowState_Fullscreen;
16955 } else if (state != IconicState && flags & fpl__X11NetWMStateMaximizedFlag) {
16956 result.state = fplWindowState_Maximize;
16957 }
16958 return result;
16959}
16960
16961fpl_internal fpl__X11WindowStateInfo fpl__X11ReconcilWindowStateInfo(fpl__X11WindowStateInfo *last, fpl__X11WindowStateInfo *next) {
16962 fpl__X11WindowStateInfo change = fplZeroInit;
16963 if (last->state != next->state) {
16964 change.state = next->state;
16965 }
16966 if (last->visibility != next->visibility) {
16967 change.visibility = next->visibility;
16968 }
16969 return change;
16970}
16971
16972fpl_internal void *fpl__X11ParseUriPaths(const char *text, size_t *size, int *count, int textLength) {
16973 const char *textCursor = text;
16974 const char *textEnd = text + textLength;
16975 int fileCount = 0;
16976 // count file entries
16977 while (*textCursor != '\0' || textCursor != textEnd) {
16978 if (*textCursor == '\r' && *(textCursor + 1) == '\n')
16979 ++fileCount;
16980 ++textCursor;
16981 }
16982 textCursor = text;
16983 size_t filesTableSize = fileCount * sizeof(char **);
16984 size_t maxFileStride = FPL_MAX_PATH_LENGTH + 1;
16985 size_t filesMemorySize = filesTableSize + FPL__ARBITARY_PADDING + maxFileStride * fileCount;
16986 void *filesTableMemory = fpl__AllocateDynamicMemory(filesMemorySize, 8);
16987 char **filesTable = (char **)filesTableMemory;
16988 for (int fileIndex = 0; fileIndex < fileCount; ++fileIndex) {
16989 filesTable[fileIndex] = (char *)((uint8_t *)filesTableMemory + filesTableSize + FPL__ARBITARY_PADDING + fileIndex * maxFileStride);
16990 }
16991 for (int fileIndex = 0; fileIndex < fileCount; ++fileIndex) {
16992 char *file = filesTable[fileIndex];
16993 const char *line = textCursor;
16994 // split on '\r\n' divider
16995 while (*textCursor != '\r' && (*textCursor != '\0' || textCursor != textEnd)) {
16996 ++textCursor;
16997 }
16998 // strip protocol
16999 if (fplIsStringEqualLen(line, 7, "file://", 7)) {
17000 line += 7;
17001 }
17002 fplCopyStringLen(line, (textCursor - line), file, maxFileStride);
17003 textCursor += 2;
17004 }
17005 *size = filesMemorySize;
17006 *count = fileCount;
17007 return filesTableMemory;
17008}
17009
17010fpl_internal void fpl__X11HandleTextInputEvent(const fpl__X11Api *x11Api, fpl__PlatformWindowState *winState, const uint64_t keyCode, XEvent *ev) {
17011 char buf[32];
17012 KeySym keysym = 0;
17013 if (x11Api->XLookupString(&ev->xkey, buf, 32, &keysym, NULL) != NoSymbol) {
17014 wchar_t wideBuffer[4] = fplZeroInit;
17015 fplUTF8StringToWideString(buf, fplGetStringLength(buf), wideBuffer, fplArrayCount(wideBuffer));
17016 uint32_t textCode = (uint32_t)wideBuffer[0];
17017 if (textCode > 0) {
17018 fpl__HandleKeyboardInputEvent(winState, keyCode, textCode);
17019 }
17020 }
17021}
17022
17023fpl_internal void fpl__X11HandleEvent(const fpl__X11SubplatformState *subplatform, fpl__PlatformAppState *appState, XEvent *ev) {
17024 fplAssert((subplatform != fpl_null) && (appState != fpl_null) && (ev != fpl_null));
17025 fpl__PlatformWindowState *winState = &appState->window;
17026 fpl__X11WindowState *x11WinState = &winState->x11;
17027 fpl__X11WindowStateInfo *lastX11WinInfo = &x11WinState->lastWindowStateInfo;
17028 const fpl__X11Api *x11Api = &appState->x11.api;
17029
17030 if (appState->currentSettings.window.callbacks.eventCallback != fpl_null) {
17031 appState->currentSettings.window.callbacks.eventCallback(fplGetPlatformType(), x11WinState, ev, appState->currentSettings.window.callbacks.eventUserData);
17032 }
17033
17034 switch (ev->type) {
17035 case ConfigureNotify:
17036 {
17037# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
17038 if (appState->currentSettings.video.backend == fplVideoBackendType_Software) {
17039 if (appState->initSettings.video.isAutoSize) {
17040 uint32_t w = (uint32_t)ev->xconfigure.width;
17041 uint32_t h = (uint32_t)ev->xconfigure.height;
17043 }
17044 }
17045# endif
17046 // Window resized
17047 if (ev->xconfigure.width != lastX11WinInfo->size.width || ev->xconfigure.height != lastX11WinInfo->size.height) {
17048 fpl__PushWindowSizeEvent(fplWindowEventType_Resized, (uint32_t)ev->xconfigure.width, (uint32_t)ev->xconfigure.height);
17049 lastX11WinInfo->size.width = (int32_t)ev->xconfigure.width;
17050 lastX11WinInfo->size.height = (int32_t)ev->xconfigure.height;
17051 }
17052
17053 // Window moved
17054 if (ev->xconfigure.x != lastX11WinInfo->position.left || ev->xconfigure.y != lastX11WinInfo->position.top) {
17055 fpl__PushWindowPositionEvent(fplWindowEventType_PositionChanged, (int32_t)ev->xconfigure.x, (int32_t)ev->xconfigure.y);
17056 lastX11WinInfo->position.left = (int32_t)ev->xconfigure.x;
17057 lastX11WinInfo->position.top = (int32_t)ev->xconfigure.y;
17058 }
17059 } break;
17060
17061 case ClientMessage:
17062 {
17063 if (ev->xclient.message_type == x11WinState->wmProtocols) {
17064 const Atom protocol = (Atom)ev->xclient.data.l[0];
17065 if (protocol != None) {
17066 if (protocol == x11WinState->wmDeleteWindow) {
17067 // Window asked for closing
17068 winState->isRunning = false;
17069 fpl__PushWindowStateEvent(fplWindowEventType_Closed);
17070 } else if (protocol == x11WinState->netWMPing) {
17071 // Window manager asks us if we are still alive
17072 XEvent reply = *ev;
17073 reply.xclient.window = x11WinState->root;
17074 x11Api->XSendEvent(x11WinState->display, x11WinState->root, False, SubstructureNotifyMask | SubstructureRedirectMask, &reply);
17075 }
17076 }
17077 } else if (ev->xclient.message_type == x11WinState->xdndEnter) {
17078 // A drag operation has entered the window
17079 unsigned long i, count;
17080 Atom *formats = NULL;
17081 bool list = ev->xclient.data.l[1] & 1;
17082 x11WinState->xdnd.source = ev->xclient.data.l[0];
17083 x11WinState->xdnd.version = ev->xclient.data.l[1] >> 24;
17084 x11WinState->xdnd.format = None;
17085 if (x11WinState->xdnd.version > FPL__XDND_VERSION) {
17086 return;
17087 }
17088 if (list) {
17089 count = fpl__X11GetWindowProperty(x11Api, x11WinState->display, x11WinState->xdnd.source, x11WinState->xdndTypeList, XA_ATOM, (unsigned char **)&formats);
17090 } else {
17091 count = 3;
17092 formats = (Atom *)ev->xclient.data.l + 2;
17093 }
17094 for (i = 0; i < count; ++i) {
17095 if (formats[i] == x11WinState->textUriList) {
17096 x11WinState->xdnd.format = x11WinState->textUriList;
17097 break;
17098 }
17099 }
17100 if (list && formats) {
17101 x11Api->XFree(formats);
17102 }
17103 } else if (ev->xclient.message_type == x11WinState->xdndDrop) {
17104 // The drag operation has finished by dropping on the window
17105 Time time = CurrentTime;
17106 if (x11WinState->xdnd.version > FPL__XDND_VERSION) {
17107 return;
17108 }
17109 if (x11WinState->xdnd.format) {
17110 if (x11WinState->xdnd.version >= 1) {
17111 time = ev->xclient.data.l[2];
17112 }
17113 // Request the chosen format from the source window
17114 x11Api->XConvertSelection(x11WinState->display, x11WinState->xdndSelection, x11WinState->xdnd.format, x11WinState->xdndSelection, x11WinState->window, time);
17115 } else if (x11WinState->xdnd.version >= 2) {
17116 XEvent reply;
17117 fplMemorySet(&reply, 0, sizeof(reply));
17118
17119 reply.type = ClientMessage;
17120 reply.xclient.window = x11WinState->xdnd.source;
17121 reply.xclient.message_type = x11WinState->xdndFinished;
17122 reply.xclient.format = 32;
17123 reply.xclient.data.l[0] = x11WinState->window;
17124 reply.xclient.data.l[1] = 0; // The drag was rejected
17125 reply.xclient.data.l[2] = None;
17126
17127 x11Api->XSendEvent(x11WinState->display, x11WinState->xdnd.source, False, NoEventMask, &reply);
17128 x11Api->XFlush(x11WinState->display);
17129 }
17130 } else if (ev->xclient.message_type == x11WinState->xdndPosition) {
17131 // The drag operation has moved over the window
17132 Window dummy;
17133 int xpos, ypos;
17134 const int xabs = (ev->xclient.data.l[2] >> 16) & 0xffff;
17135 const int yabs = (ev->xclient.data.l[2]) & 0xffff;
17136 if (x11WinState->xdnd.version > FPL__XDND_VERSION) {
17137 return;
17138 }
17139 XEvent reply;
17140 fplMemorySet(&reply, 0, sizeof(reply));
17141
17142 reply.type = ClientMessage;
17143 reply.xclient.window = x11WinState->xdnd.source;
17144 reply.xclient.message_type = x11WinState->xdndStatus;
17145 reply.xclient.format = 32;
17146 reply.xclient.data.l[0] = x11WinState->window;
17147 reply.xclient.data.l[2] = 0; // Specify an empty rectangle
17148 reply.xclient.data.l[3] = 0;
17149
17150 if (x11WinState->xdnd.format) {
17151 // Reply that we are ready to copy the dragged data
17152 reply.xclient.data.l[1] = 1; // Accept with no rectangle
17153 if (x11WinState->xdnd.version >= 2)
17154 reply.xclient.data.l[4] = x11WinState->xdndActionCopy;
17155 }
17156 x11Api->XSendEvent(x11WinState->display, x11WinState->xdnd.source, False, NoEventMask, &reply);
17157 x11Api->XFlush(x11WinState->display);
17158 }
17159 } break;
17160
17161 case SelectionNotify:
17162 {
17163 if (ev->xselection.property == x11WinState->xdndSelection) {
17164 // The converted data from the drag operation has arrived
17165 char *data;
17166 const unsigned long result = fpl__X11GetWindowProperty(x11Api, x11WinState->display, ev->xselection.requestor, ev->xselection.property, ev->xselection.target, (unsigned char **)&data);
17167 if (result) {
17168 size_t filesTableSize;
17169 int fileCount;
17170 void *filesTable = fpl__X11ParseUriPaths(data, &filesTableSize, &fileCount, result);
17171 fplMemoryBlock memory = fplZeroInit;
17172 memory.size = filesTableSize;
17173 memory.base = filesTable;
17174 fpl__PushWindowDropFilesEvent(NULL, fileCount, (const char **)filesTable, &memory);
17175 }
17176 if (data) {
17177 x11Api->XFree(data);
17178 }
17179 if (x11WinState->xdnd.version >= 2) {
17180 XEvent reply;
17181 fplMemorySet(&reply, 0, sizeof(reply));
17182
17183 reply.type = ClientMessage;
17184 reply.xclient.window = x11WinState->xdnd.source;
17185 reply.xclient.message_type = x11WinState->xdndFinished;
17186 reply.xclient.format = 32;
17187 reply.xclient.data.l[0] = x11WinState->window;
17188 reply.xclient.data.l[1] = result;
17189 reply.xclient.data.l[2] = x11WinState->xdndActionCopy;
17190
17191 x11Api->XSendEvent(x11WinState->display, x11WinState->xdnd.source, False, NoEventMask, &reply);
17192 x11Api->XFlush(x11WinState->display);
17193 }
17194 }
17195 } break;
17196
17197 case KeyPress:
17198 {
17199 // Keyboard button down
17200 if (!appState->currentSettings.input.disabledEvents) {
17201 int keyState = ev->xkey.state;
17202 uint64_t keyCode = (uint64_t)ev->xkey.keycode;
17203 Time keyTime = ev->xkey.time;
17204 Time lastPressTime = winState->keyPressTimes[keyCode];
17205 Time diffTime = keyTime - lastPressTime;
17206 FPL_LOG_INFO("X11", "Diff for key '%llu', time: %lu, diff: %lu, last: %lu", keyCode, keyTime, diffTime, lastPressTime);
17207 if (diffTime == keyTime || (diffTime > 0 && diffTime < (1 << 31))) {
17208 if (keyCode) {
17209 fpl__HandleKeyboardButtonEvent(winState, (uint64_t)keyTime, keyCode, fpl__X11TranslateModifierFlags(keyState), fplButtonState_Press, false);
17210 fpl__X11HandleTextInputEvent(x11Api, winState, keyCode, ev);
17211 }
17212 winState->keyPressTimes[keyCode] = keyTime;
17213 }
17214 }
17215 } break;
17216
17217 case KeyRelease:
17218 {
17219 // Keyboard button up
17220 if (!appState->currentSettings.input.disabledEvents) {
17221 bool isRepeat = false;
17222 if (x11Api->XEventsQueued(x11WinState->display, QueuedAfterReading)) {
17223 XEvent nextEvent;
17224 x11Api->XPeekEvent(x11WinState->display, &nextEvent);
17225 if ((nextEvent.type == KeyPress) && (nextEvent.xkey.time == ev->xkey.time) && (nextEvent.xkey.keycode == ev->xkey.keycode)) {
17226 // Delete the peeked event, which is a key-repeat
17227 x11Api->XNextEvent(x11WinState->display, ev);
17228 isRepeat = true;
17229 }
17230 }
17231
17232 int keyState = ev->xkey.state;
17233 uint64_t keyCode = (uint64_t)ev->xkey.keycode;
17234
17235 if (isRepeat) {
17236 fpl__X11HandleTextInputEvent(x11Api, winState, keyCode, ev);
17237 fpl__HandleKeyboardButtonEvent(winState, (uint64_t)ev->xkey.time, (uint64_t)keyCode, fpl__X11TranslateModifierFlags(keyState), fplButtonState_Repeat, false);
17238 } else {
17239 fpl__HandleKeyboardButtonEvent(winState, (uint64_t)ev->xkey.time, (uint64_t)keyCode, fpl__X11TranslateModifierFlags(keyState), fplButtonState_Release, true);
17240 }
17241 }
17242 } break;
17243
17244 case ButtonPress:
17245 {
17246 int x = ev->xbutton.x;
17247 int y = ev->xbutton.y;
17248
17249 if (!appState->currentSettings.input.disabledEvents) {
17250 // Mouse button
17251 if (ev->xbutton.button == Button1) {
17252 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Left, fplButtonState_Press);
17253 } else if (ev->xbutton.button == Button2) {
17254 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Middle, fplButtonState_Press);
17255 } else if (ev->xbutton.button == Button3) {
17256 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Right, fplButtonState_Press);
17257 }
17258 }
17259
17260 // Mouse wheel
17261 if (ev->xbutton.button == Button4) {
17262 fpl__HandleMouseWheelEvent(winState, x, y, 1.0f);
17263 } else if (ev->xbutton.button == Button5) {
17264 fpl__HandleMouseWheelEvent(winState, x, y, -1.0f);
17265 }
17266 } break;
17267
17268 case ButtonRelease:
17269 {
17270 // Mouse up
17271 if (!appState->currentSettings.input.disabledEvents) {
17272 int x = ev->xbutton.x;
17273 int y = ev->xbutton.y;
17274 if (ev->xbutton.button == Button1) {
17275 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Left, fplButtonState_Release);
17276 } else if (ev->xbutton.button == Button2) {
17277 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Middle, fplButtonState_Release);
17278 } else if (ev->xbutton.button == Button3) {
17279 fpl__HandleMouseButtonEvent(winState, x, y, fplMouseButtonType_Right, fplButtonState_Release);
17280 }
17281 }
17282 } break;
17283
17284 case MotionNotify:
17285 {
17286 // Mouse move
17287 if (!appState->currentSettings.input.disabledEvents) {
17288 fpl__HandleMouseMoveEvent(winState, ev->xmotion.x, ev->xmotion.y);
17289 }
17290 } break;
17291
17292 case Expose:
17293 {
17294 // Repaint
17295 if (appState->currentSettings.window.callbacks.exposedCallback != fpl_null) {
17296 appState->currentSettings.window.callbacks.exposedCallback(fplGetPlatformType(), x11WinState, ev, appState->currentSettings.window.callbacks.exposedUserData);
17297 }
17298 } break;
17299
17300 case FocusIn:
17301 {
17302 // Ignore focus events from popup indicator windows, window menu
17303 // key chords and window dragging
17304 if (ev->xfocus.mode == NotifyGrab || ev->xfocus.mode == NotifyUngrab) {
17305 return;
17306 }
17307 fpl__PushWindowStateEvent(fplWindowEventType_GotFocus);
17308 } break;
17309
17310 case FocusOut:
17311 {
17312 // Ignore focus events from popup indicator windows, window menu
17313 // key chords and window dragging
17314 if (ev->xfocus.mode == NotifyGrab || ev->xfocus.mode == NotifyUngrab) {
17315 return;
17316 }
17317 fpl__PushWindowStateEvent(fplWindowEventType_LostFocus);
17318 } break;
17319
17320 case PropertyNotify:
17321 {
17322 if (ev->xproperty.atom == x11WinState->netWMState || ev->xproperty.atom == x11WinState->wmState) {
17323 fpl__X11WindowStateInfo nextWindowStateInfo = fpl__X11GetWindowStateInfo(x11Api, x11WinState);
17324 fpl__X11WindowStateInfo changedWindowStateInfo = fpl__X11ReconcilWindowStateInfo(&x11WinState->lastWindowStateInfo, &nextWindowStateInfo);
17325 switch (changedWindowStateInfo.visibility) {
17327 fpl__PushWindowStateEvent(fplWindowEventType_Hidden);
17328 break;
17330 fpl__PushWindowStateEvent(fplWindowEventType_Shown);
17331 break;
17332 default:
17333 break;
17334 }
17335 switch (changedWindowStateInfo.state) {
17337 fpl__PushWindowStateEvent(fplWindowEventType_Minimized);
17338 break;
17340 fpl__PushWindowStateEvent(fplWindowEventType_Maximized);
17341 break;
17343 fpl__PushWindowStateEvent(fplWindowEventType_Restored);
17344 break;
17345 default:
17346 break;
17347 }
17348 x11WinState->lastWindowStateInfo.state = nextWindowStateInfo.state;
17349 x11WinState->lastWindowStateInfo.visibility = nextWindowStateInfo.visibility;
17350 }
17351 } break;
17352
17353 default:
17354 break;
17355 }
17356}
17357
17359 FPL__CheckPlatform(false);
17360 bool result = fpl__global__AppState->window.isRunning;
17361 return(result);
17362}
17363
17365 FPL__CheckPlatformNoRet();
17366 fpl__PlatformAppState *appState = fpl__global__AppState;
17367 if (appState->window.isRunning) {
17368 appState->window.isRunning = false;
17369 const fpl__X11SubplatformState *subplatform = &appState->x11;
17370 const fpl__X11Api *x11Api = &subplatform->api;
17371 const fpl__X11WindowState *windowState = &appState->window.x11;
17372 XEvent ev = fplZeroInit;
17373 ev.type = ClientMessage;
17374 ev.xclient.window = windowState->window;
17375 ev.xclient.message_type = windowState->wmProtocols;
17376 ev.xclient.format = 32;
17377 ev.xclient.data.l[0] = windowState->wmDeleteWindow;
17378 ev.xclient.data.l[1] = 0;
17379 x11Api->XSendEvent(windowState->display, windowState->root, False, SubstructureRedirectMask | SubstructureNotifyMask, &ev);
17380 }
17381}
17382
17383fpl_internal bool fpl__X11ProcessNextEvent(const fpl__X11SubplatformState *subplatform, fpl__PlatformAppState *appState) {
17384 bool result = false;
17385 const fpl__X11Api *x11Api = &subplatform->api;
17386 fpl__X11WindowState *windowState = &appState->window.x11;
17387 if (x11Api->XPending(windowState->display)) {
17388 XEvent ev;
17389 x11Api->XNextEvent(windowState->display, &ev);
17390 fpl__X11HandleEvent(subplatform, appState, &ev);
17391 result = true;
17392 }
17393 return(result);
17394}
17395
17397 FPL__CheckPlatform(false);
17398 fpl__PlatformAppState *appState = fpl__global__AppState;
17399 const fpl__X11SubplatformState *subplatform = &appState->x11;
17400 const fpl__X11Api *x11Api = &subplatform->api;
17401 const fpl__X11WindowState *windowState = &appState->window.x11;
17402
17403 // Poll next event from the internal queue first
17404 if (fpl__PollInternalEvent(ev)) {
17405 return(true);
17406 }
17407
17408 // Create new event(s) from the X11 event queue
17409 if (!fpl__X11ProcessNextEvent(subplatform, appState)) {
17410 return(false);
17411 }
17412
17413 // Poll the first event from the internal queue
17414 if (fpl__PollInternalEvent(ev)) {
17415 return(true);
17416 }
17417
17418 // No events left
17419 return(false);
17420}
17421
17423 FPL__CheckPlatformNoRet();
17424 fpl__PlatformAppState *appState = fpl__global__AppState;
17425 const fpl__X11SubplatformState *subplatform = &appState->x11;
17426 const fpl__X11Api *x11Api = &subplatform->api;
17427 const fpl__X11WindowState *windowState = &appState->window.x11;
17428 while (x11Api->XPending(windowState->display)) {
17429 XEvent ev;
17430 x11Api->XNextEvent(windowState->display, &ev);
17431 fpl__X11HandleEvent(subplatform, appState, &ev);
17432 }
17433 fpl__ClearInternalEvents();
17434}
17435
17437 FPL__CheckPlatform(false);
17438 fpl__PlatformAppState *appState = fpl__global__AppState;
17439 const fpl__X11SubplatformState *subplatform = &appState->x11;
17440 const fpl__X11Api *x11Api = &subplatform->api;
17441 const fpl__X11WindowState *windowState = &appState->window.x11;
17442
17443 fpl__ClearInternalEvents();
17444
17445 // Dont like this, maybe a callback would be better?
17446#if defined(FPL_PLATFORM_LINUX)
17447 if ((!appState->currentSettings.input.disabledEvents) && (appState->initFlags & fplInitFlags_GameController)) {
17448 fpl__LinuxAppState *linuxAppState = &appState->plinux;
17449 fpl__LinuxPollGameControllers(&appState->currentSettings, &linuxAppState->controllersState, true);
17450 }
17451#endif
17452
17453 bool result = appState->window.isRunning;
17454 return(result);
17455}
17456
17457fpl_platform_api void fplSetWindowCursorEnabled(const bool value) {
17458 // @IMPLEMENT(final/X11): fplSetWindowCursorEnabled
17459}
17460
17462 FPL__CheckArgumentNull(outSize, false);
17463 FPL__CheckPlatform(false);
17464 fpl__PlatformAppState *appState = fpl__global__AppState;
17465 const fpl__X11SubplatformState *subplatform = &appState->x11;
17466 const fpl__X11Api *x11Api = &subplatform->api;
17467 const fpl__X11WindowState *windowState = &appState->window.x11;
17468 XWindowAttributes attribs;
17469 x11Api->XGetWindowAttributes(windowState->display, windowState->window, &attribs);
17470 outSize->width = attribs.width;
17471 outSize->height = attribs.height;
17472 return(true);
17473}
17474
17475fpl_platform_api void fplSetWindowSize(const uint32_t width, const uint32_t height) {
17476 fpl__PlatformAppState *appState = fpl__global__AppState;
17477 FPL__CheckPlatformNoRet();
17478 const fpl__X11SubplatformState *subplatform = &appState->x11;
17479 const fpl__X11Api *x11Api = &subplatform->api;
17480 const fpl__X11WindowState *windowState = &appState->window.x11;
17481 x11Api->XResizeWindow(windowState->display, windowState->window, width, height);
17482 x11Api->XFlush(windowState->display);
17483}
17484
17486 // @IMPLEMENT(final/X11): fplIsWindowResizable
17487 return false;
17488}
17489
17490fpl_platform_api void fplSetWindowResizeable(const bool value) {
17491 // @IMPLEMENT(final/X11): fplSetWindowResizeable
17492}
17493
17495 FPL__CheckPlatform(false);
17496 fpl__PlatformAppState *appState = fpl__global__AppState;
17497 bool result = appState->currentSettings.window.isDecorated;
17498 return(result);
17499}
17500
17501#define FPL__MWM_HINTS_DECORATIONS (1L << 1)
17502#define FPL__MWM_HINTS_FUNCTIONS (1L << 0)
17503#define FPL__MWM_FUNC_ALL (1L<<0)
17504#define FPL__PROPERTY_MOTIF_WM_HINTS_ELEMENT_COUNT 5
17505
17506typedef struct {
17507 unsigned long flags;
17508 unsigned long functions;
17509 unsigned long decorations;
17510 long input_mode;
17511 unsigned long status;
17512} fpl__MotifWMHints;
17513
17514fpl_platform_api void fplSetWindowDecorated(const bool value) {
17515 FPL__CheckPlatformNoRet();
17516 fpl__PlatformAppState *appState = fpl__global__AppState;
17517 const fpl__X11SubplatformState *subplatform = &appState->x11;
17518 const fpl__X11Api *x11Api = &subplatform->api;
17519 const fpl__X11WindowState *windowState = &appState->window.x11;
17520
17521 fpl__MotifWMHints hints = fplZeroInit;
17522 hints.flags = FPL__MWM_HINTS_DECORATIONS | FPL__MWM_HINTS_FUNCTIONS;
17523 hints.decorations = value ? 1 : 0;
17524 hints.functions = value ? FPL__MWM_FUNC_ALL : 0;
17525
17526 x11Api->XChangeProperty(windowState->display, windowState->window,
17527 windowState->motifWMHints,
17528 windowState->motifWMHints, 32,
17529 PropModeReplace,
17530 (unsigned char *)&hints,
17531 FPL__PROPERTY_MOTIF_WM_HINTS_ELEMENT_COUNT);
17532
17533 appState->currentSettings.window.isDecorated = value;
17534}
17535
17537 // @IMPLEMENT(final/X11): fplIsWindowFloating
17538 return false;
17539}
17540
17541fpl_platform_api void fplSetWindowFloating(const bool value) {
17542 // @IMPLEMENT(final/X11): fplSetWindowFloating
17543}
17544
17546 // @IMPLEMENT(final/X11): fplGetWindowState
17547 return(fplWindowState_Unknown);
17548}
17549
17551 // @IMPLEMENT(final/X11): fplSetWindowState
17552 return(false);
17553}
17554
17556 // @IMPLEMENT(final/X11): fplGetDisplayCount
17557 return(0);
17558}
17559
17560fpl_platform_api size_t fplGetDisplays(fplDisplayInfo *displays, const size_t maxDisplayCount) {
17561 // @IMPLEMENT(final/X11): fplGetDisplays
17562 return(0);
17563}
17564
17566 // @IMPLEMENT(final/X11): fplGetPrimaryDisplay
17567 return(false);
17568}
17569
17571 // @IMPLEMENT(final/X11): fplGetWindowDisplay
17572 return(false);
17573}
17574
17575fpl_platform_api bool fplGetDisplayFromPosition(const int32_t x, const int32_t y, fplDisplayInfo *outDisplay) {
17576 // @IMPLEMENT(final/X11): fplGetDisplayFromPosition
17577 return(false);
17578}
17579
17580fpl_platform_api size_t fplGetDisplayModes(const char *id, fplDisplayMode *modes, const size_t maxDisplayModeCount) {
17581 // @IMPLEMENT(final/X11): fplGetDisplayModes
17582 return(0);
17583}
17584
17585fpl_platform_api bool fplSetWindowFullscreenSize(const bool value, const uint32_t fullscreenWidth, const uint32_t fullscreenHeight, const uint32_t refreshRate) {
17586 FPL__CheckPlatform(false);
17587 fpl__PlatformAppState *appState = fpl__global__AppState;
17588 const fpl__X11SubplatformState *subplatform = &appState->x11;
17589 const fpl__X11Api *x11Api = &subplatform->api;
17590 const fpl__X11WindowState *windowState = &appState->window.x11;
17591
17592 // https://stackoverflow.com/questions/10897503/opening-a-fullscreen-opengl-window
17593 XEvent xev = fplZeroInit;
17594 xev.type = ClientMessage;
17595 xev.xclient.window = windowState->window;
17596 xev.xclient.message_type = windowState->netWMState;
17597 xev.xclient.format = 32;
17598 xev.xclient.data.l[0] = value ? 1 : 0; // 1 = Add, 0 = Remove
17599 xev.xclient.data.l[1] = windowState->netWMStateFullscreen; // _NET_WM_STATE_FULLSCREEN
17600 xev.xclient.data.l[3] = 1l; // Application source
17601
17602 // @TODO(final/X11): Support for changing the display resolution + refresh rate in X11
17603
17604 bool result = x11Api->XSendEvent(windowState->display, windowState->root, 0, SubstructureRedirectMask | SubstructureNotifyMask, &xev) != 0;
17605 if (result) {
17606 appState->currentSettings.window.isFullscreen = value;
17607 }
17608 return(result);
17609}
17610
17611fpl_platform_api bool fplSetWindowFullscreenRect(const bool value, const int32_t x, const int32_t y, const int32_t width, const int32_t height) {
17612 // @IMPLEMENT(final/X11): fplSetWindowFullscreenRect
17613 return(false);
17614}
17615
17617 bool result = fplSetWindowFullscreenSize(true, 0, 0, 0);
17618 return(result);
17619}
17620
17622 bool result = fplSetWindowFullscreenSize(false, 0, 0, 0);
17623 return(result);
17624}
17625
17627 FPL__CheckPlatform(false);
17628 fpl__PlatformAppState *appState = fpl__global__AppState;
17629 bool result = appState->currentSettings.window.isFullscreen;
17630 return(result);
17631}
17632
17634 FPL__CheckArgumentNull(outPos, false);
17635 FPL__CheckPlatform(false);
17636 fpl__PlatformAppState *appState = fpl__global__AppState;
17637 const fpl__X11SubplatformState *subplatform = &appState->x11;
17638 const fpl__X11Api *x11Api = &subplatform->api;
17639 const fpl__X11WindowState *windowState = &appState->window.x11;
17640 XWindowAttributes attribs;
17641 x11Api->XGetWindowAttributes(windowState->display, windowState->window, &attribs);
17642 outPos->left = attribs.x;
17643 outPos->top = attribs.y;
17644 return(true);
17645}
17646
17647fpl_platform_api void fplSetWindowPosition(const int32_t left, const int32_t top) {
17648 FPL__CheckPlatformNoRet();
17649 fpl__PlatformAppState *appState = fpl__global__AppState;
17650 const fpl__X11SubplatformState *subplatform = &appState->x11;
17651 const fpl__X11Api *x11Api = &subplatform->api;
17652 const fpl__X11WindowState *windowState = &appState->window.x11;
17653 x11Api->XMoveWindow(windowState->display, windowState->window, left, top);
17654}
17655
17656fpl_platform_api void fplSetWindowTitle(const char *title) {
17657 // @BUG(final/X11): Setting the window title on X11 works, but it wont be set for the icon in the bars in gnome/ubuntu the icon title is always "Unbekannt" on my german environment.
17658
17659 FPL__CheckArgumentNullNoRet(title);
17660 FPL__CheckPlatformNoRet();
17661 fpl__PlatformAppState *appState = fpl__global__AppState;
17662 const fpl__X11SubplatformState *subplatform = &appState->x11;
17663 const fpl__X11Api *x11Api = &subplatform->api;
17664 const fpl__X11WindowState *windowState = &appState->window.x11;
17665
17666 fplCopyString(title, appState->currentSettings.window.title, fplArrayCount(appState->currentSettings.window.title));
17667
17668 x11Api->XChangeProperty(windowState->display, windowState->window,
17669 windowState->netWMName, windowState->utf8String, 8,
17670 PropModeReplace,
17671 (unsigned char *)title, (int)fplGetStringLength(title));
17672
17673 x11Api->XChangeProperty(windowState->display, windowState->window,
17674 windowState->netWMIconName, windowState->utf8String, 8,
17675 PropModeReplace,
17676 (unsigned char *)title, (int)fplGetStringLength(title));
17677
17678 x11Api->XFlush(windowState->display);
17679}
17680
17681fpl_platform_api bool fplGetClipboardText(char *dest, const uint32_t maxDestLen) {
17682 // @IMPLEMENT(final/X11): fplGetClipboardText
17683 return false;
17684}
17685
17686fpl_platform_api bool fplSetClipboardText(const char *text) {
17687 // @IMPLEMENT(final/X11): fplSetClipboardText
17688 return false;
17689}
17690
17692 FPL__CheckPlatform(false);
17693 FPL__CheckArgumentNull(outState, false);
17694 fpl__PlatformAppState *appState = fpl__global__AppState;
17695 const fpl__X11SubplatformState *subplatform = &appState->x11;
17696 const fpl__X11Api *x11Api = &subplatform->api;
17697 const fpl__X11WindowState *windowState = &appState->window.x11;
17698 bool result = false;
17699 char keysReturn[32] = fplZeroInit;
17700 if (x11Api->XQueryKeymap(windowState->display, keysReturn)) {
17701 fplClearStruct(outState);
17702 for (uint64_t keyCode = 0; keyCode < 256; ++keyCode) {
17703 bool isDown = (keysReturn[keyCode / 8] & (1 << (keyCode % 8))) != 0;
17704 outState->keyStatesRaw[keyCode] = isDown ? 1 : 0;
17705 fplKey mappedKey = fpl__GetMappedKey(&appState->window, keyCode);
17706 if (outState->buttonStatesMapped[(int)mappedKey] == fplButtonState_Release) {
17707 outState->buttonStatesMapped[(int)mappedKey] = isDown ? fplButtonState_Press : fplButtonState_Release;
17708 }
17709 }
17711 if (outState->buttonStatesMapped[fplKey_LeftShift] == fplButtonState_Press) {
17713 }
17714 if (outState->buttonStatesMapped[fplKey_RightShift] == fplButtonState_Press) {
17716 }
17717 if (outState->buttonStatesMapped[fplKey_LeftControl] == fplButtonState_Press) {
17719 }
17720 if (outState->buttonStatesMapped[fplKey_RightControl] == fplButtonState_Press) {
17722 }
17723 if (outState->buttonStatesMapped[fplKey_LeftAlt] == fplButtonState_Press) {
17725 }
17726 if (outState->buttonStatesMapped[fplKey_RightAlt] == fplButtonState_Press) {
17728 }
17729 if (outState->buttonStatesMapped[fplKey_LeftSuper] == fplButtonState_Press) {
17731 }
17732 if (outState->buttonStatesMapped[fplKey_RightSuper] == fplButtonState_Press) {
17734 }
17735 // @FINISH(fina/X11): Get caps states (Capslock, Numlock, Scrolllock)
17736
17737 result = true;
17738 }
17739 return(result);
17740}
17741
17743 FPL__CheckPlatform(false);
17744 FPL__CheckArgumentNull(outState, false);
17745 fpl__PlatformAppState *appState = fpl__global__AppState;
17746 const fpl__X11SubplatformState *subplatform = &appState->x11;
17747 const fpl__X11Api *x11Api = &subplatform->api;
17748 const fpl__X11WindowState *windowState = &appState->window.x11;
17749 bool result = false;
17750 Window root, child;
17751 int rootx, rooty, winx, winy;
17752 unsigned int mask;
17753 if (x11Api->XQueryPointer(windowState->display, windowState->window, &root, &child, &rootx, &rooty, &winx, &winy, &mask)) {
17754 outState->x = winx;
17755 outState->y = winy;
17759 result = true;
17760 }
17761 return(result);
17762}
17763
17764fpl_platform_api bool fplQueryCursorPosition(int32_t *outX, int32_t *outY) {
17765 // @IMPLEMENT(final/X11) fplQueryCursorPosition
17766 return(false);
17767}
17768#endif // FPL_SUBPLATFORM_X11
17769
17770// ############################################################################
17771//
17772// > LINUX_PLATFORM
17773//
17774// ############################################################################
17775#if defined(FPL_PLATFORM_LINUX)
17776# include <locale.h> // setlocale
17777# include <sys/eventfd.h> // eventfd
17778# include <sys/epoll.h> // epoll_create, epoll_ctl, epoll_wait
17779# include <sys/select.h> // select
17780# include <linux/joystick.h> // js_event, axis_state, etc.
17781
17782fpl_internal void fpl__LinuxReleasePlatform(fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
17783#if defined(FPL__ENABLE_WINDOW)
17784 if (appState->initFlags & fplInitFlags_GameController) {
17785 fpl__LinuxFreeGameControllers(&appState->plinux.controllersState);
17786 }
17787#endif
17788}
17789
17790fpl_internal bool fpl__LinuxInitPlatform(const fplInitFlags initFlags, const fplSettings *initSettings, fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
17791 setlocale(LC_ALL, "");
17792 return true;
17793}
17794
17795//
17796// Linux OS
17797//
17798
17799
17800//
17801// Linux Input
17802//
17803#if defined(FPL__ENABLE_WINDOW)
17804fpl_internal void fpl__LinuxFreeGameControllers(fpl__LinuxGameControllersState *controllersState) {
17805 for (int controllerIndex = 0; controllerIndex < fplArrayCount(controllersState->controllers); ++controllerIndex) {
17806 fpl__LinuxGameController *controller = controllersState->controllers + controllerIndex;
17807 if (controller->fd > 0) {
17808 close(controller->fd);
17809 controller->fd = 0;
17810 }
17811 }
17812}
17813
17814fpl_internal float fpl__LinuxJoystickProcessStickValue(const int16_t value, const int16_t deadZoneThreshold) {
17815 float result = 0;
17816 if (value < -deadZoneThreshold) {
17817 result = (float)((value + deadZoneThreshold) / (32768.0f - deadZoneThreshold));
17818 } else if (value > deadZoneThreshold) {
17819 result = (float)((value - deadZoneThreshold) / (32767.0f - deadZoneThreshold));
17820 }
17821 return(result);
17822}
17823
17824fpl_internal void fpl__LinuxPushGameControllerStateUpdateEvent(const struct js_event *event, fpl__LinuxGameController *controller) {
17825 fplGamepadState *padState = &controller->state;
17826
17827 // @TODO(final): Use a static offset table instead of a pointer mapping table
17828 fplGamepadButton *buttonMappingTable[12] = fplZeroInit;
17829 buttonMappingTable[0] = &padState->actionA;
17830 buttonMappingTable[1] = &padState->actionB;
17831 buttonMappingTable[2] = &padState->actionX;
17832 buttonMappingTable[3] = &padState->actionY;
17833 buttonMappingTable[4] = &padState->leftShoulder;
17834 buttonMappingTable[5] = &padState->rightShoulder;
17835 buttonMappingTable[6] = &padState->back;
17836 buttonMappingTable[7] = &padState->start;
17837 // 8 = XBox-Button
17838 buttonMappingTable[9] = &padState->leftThumb;
17839 buttonMappingTable[10] = &padState->rightThumb;
17840
17841 const int16_t deadZoneThresholdLeftStick = 5000;
17842 const int16_t deadZoneThresholdRightStick = 5000;
17843
17844 switch (event->type & ~JS_EVENT_INIT) {
17845 case JS_EVENT_AXIS:
17846 {
17847 switch (event->number) {
17848 // Left stick
17849 case 0:
17850 {
17851 padState->leftStickX = fpl__LinuxJoystickProcessStickValue(event->value, deadZoneThresholdLeftStick);
17852 } break;
17853 case 1:
17854 {
17855 padState->leftStickY = fpl__LinuxJoystickProcessStickValue(-event->value, deadZoneThresholdLeftStick);
17856 } break;
17857
17858 // Right stick
17859 case 3:
17860 {
17861 padState->rightStickX = fpl__LinuxJoystickProcessStickValue(event->value, deadZoneThresholdRightStick);
17862 } break;
17863 case 4:
17864 {
17865 padState->rightStickY = fpl__LinuxJoystickProcessStickValue(-event->value, deadZoneThresholdRightStick);
17866 } break;
17867
17868 // Left/right trigger
17869 case 2:
17870 {
17871 padState->leftTrigger = (float)((event->value + 32768) >> 8) / 255.0f;
17872 } break;
17873 case 5:
17874 {
17875 padState->rightTrigger = (float)((event->value + 32768) >> 8) / 255.0f;
17876 } break;
17877
17878 // DPad X-Axis
17879 case 6:
17880 {
17881 if (event->value == -32767) {
17882 padState->dpadLeft.isDown = true;
17883 padState->dpadRight.isDown = false;
17884 } else if (event->value == 32767) {
17885 padState->dpadLeft.isDown = false;
17886 padState->dpadRight.isDown = true;
17887 } else {
17888 padState->dpadLeft.isDown = false;
17889 padState->dpadRight.isDown = false;
17890 }
17891 } break;
17892
17893 // DPad Y-Axis
17894 case 7:
17895 {
17896 if (event->value == -32767) {
17897 padState->dpadUp.isDown = true;
17898 padState->dpadDown.isDown = false;
17899 } else if (event->value == 32767) {
17900 padState->dpadUp.isDown = false;
17901 padState->dpadDown.isDown = true;
17902 } else {
17903 padState->dpadUp.isDown = false;
17904 padState->dpadDown.isDown = false;
17905 }
17906 } break;
17907
17908 default:
17909 break;
17910 }
17911 } break;
17912
17913 case JS_EVENT_BUTTON:
17914 {
17915 if ((event->number >= 0) && (event->number < fplArrayCount(buttonMappingTable))) {
17916 fplGamepadButton *mappedButton = buttonMappingTable[event->number];
17917 if (mappedButton != fpl_null) {
17918 mappedButton->isDown = event->value != 0;
17919 }
17920 }
17921 } break;
17922
17923 default:
17924 break;
17925 }
17926}
17927
17928fpl_internal void fpl__LinuxPollGameControllers(const fplSettings *settings, fpl__LinuxGameControllersState *controllersState, const bool useEvents) {
17929 // https://github.com/underdoeg/ofxGamepad
17930 // https://github.com/elanthis/gamepad
17931 // https://gist.github.com/jasonwhite/c5b2048c15993d285130
17932 // https://github.com/Tasssadar/libenjoy/blob/master/src/libenjoy_linux.c
17933
17934 if (((controllersState->lastCheckTime == 0) || ((fplMillisecondsQuery() - controllersState->lastCheckTime) >= settings->input.controllerDetectionFrequency)) || !useEvents) {
17935 controllersState->lastCheckTime = fplMillisecondsQuery();
17936
17937 //
17938 // Detect new controllers
17939 //
17940 const char *deviceNames[] = {
17941 "/dev/input/js0",
17942 };
17943 for (int deviceNameIndex = 0; deviceNameIndex < fplArrayCount(deviceNames); ++deviceNameIndex) {
17944 const char *deviceName = deviceNames[deviceNameIndex];
17945 bool alreadyFound = false;
17946 int freeIndex = -1;
17947 for (uint32_t controllerIndex = 0; controllerIndex < fplArrayCount(controllersState->controllers); ++controllerIndex) {
17948 fpl__LinuxGameController *controller = controllersState->controllers + controllerIndex;
17949 if ((controller->fd > 0) && fplIsStringEqual(deviceName, controller->deviceName)) {
17950 alreadyFound = true;
17951 break;
17952 }
17953 if (controller->fd == 0) {
17954 if (freeIndex == -1) {
17955 freeIndex = controllerIndex;
17956 }
17957 }
17958 }
17959 if (!alreadyFound && freeIndex >= 0) {
17960 int fd = open(deviceName, O_RDONLY);
17961 if (fd < 0) {
17962 FPL_LOG_ERROR(FPL__MODULE_LINUX, "Failed opening joystick device '%s'", deviceName);
17963 continue;
17964 }
17965 uint8_t numAxis = 0;
17966 uint8_t numButtons = 0;
17967 ioctl(fd, JSIOCGAXES, &numAxis);
17968 ioctl(fd, JSIOCGBUTTONS, &numButtons);
17969 if (numAxis == 0 || numButtons == 0) {
17970 FPL_LOG_ERROR(FPL__MODULE_LINUX, "Joystick device '%s' does not have enough buttons/axis to map to a XInput controller!", deviceName);
17971 close(fd);
17972 continue;
17973 }
17974
17975 // NOTE(final): We do not want to detect devices which are not proper joysticks, such as gaming keyboards
17976 struct js_event msg;
17977 if ((read(fd, &msg, sizeof(struct js_event)) != sizeof(struct js_event)) || !((msg.type == JS_EVENT_INIT) || (msg.type == JS_EVENT_AXIS) || (msg.type == JS_EVENT_BUTTON))) {
17978 // No joystick message
17979 close(fd);
17980 continue;
17981 }
17982
17983 fpl__LinuxGameController *controller = controllersState->controllers + freeIndex;
17984 fplClearStruct(controller);
17985 controller->fd = fd;
17986 controller->axisCount = numAxis;
17987 controller->buttonCount = numButtons;
17988 fplCopyString(deviceName, controller->deviceName, fplArrayCount(controller->deviceName));
17989 ioctl(fd, JSIOCGNAME(fplArrayCount(controller->displayName)), controller->displayName);
17990 fcntl(fd, F_SETFL, O_NONBLOCK);
17991
17992 if (useEvents) {
17993 // Push connected event
17994 fplEvent ev = fplZeroInit;
17997 ev.gamepad.deviceIndex = (uint32_t)freeIndex;
17998 ev.gamepad.deviceName = controller->deviceName;
17999 fpl__PushInternalEvent(&ev);
18000 }
18001 }
18002 }
18003 }
18004
18005 // Update controller states
18006 for (uint32_t controllerIndex = 0; controllerIndex < fplArrayCount(controllersState->controllers); ++controllerIndex) {
18007 fpl__LinuxGameController *controller = controllersState->controllers + controllerIndex;
18008 if (controller->fd > 0) {
18009 // Update button/axis state
18010 struct js_event event;
18011 bool wasDisconnected = false;
18012 for (;;) {
18013 errno = 0;
18014 if (read(controller->fd, &event, sizeof(event)) < 0) {
18015 if (errno == ENODEV) {
18016 close(controller->fd);
18017 controller->fd = 0;
18018 fplClearStruct(&controller->state);
18019 wasDisconnected = true;
18020 if (useEvents) {
18021 // Push disconnected event
18022 fplEvent ev = fplZeroInit;
18025 ev.gamepad.deviceIndex = controllerIndex;
18026 ev.gamepad.deviceName = controller->deviceName;
18027 fpl__PushInternalEvent(&ev);
18028 }
18029 }
18030 break;
18031 }
18032 fpl__LinuxPushGameControllerStateUpdateEvent(&event, controller);
18033 }
18034
18035 controller->state.isActive = !fpl__IsZeroMemory(&controller->state, sizeof(fplGamepadState));
18036 controller->state.isConnected = !wasDisconnected;
18037 controller->state.deviceName = controller->deviceName;
18038
18039 if (controller->fd > 0) {
18040 if (useEvents) {
18041 // Push state event
18042 fplEvent ev = fplZeroInit;
18045 ev.gamepad.deviceIndex = controllerIndex;
18046 ev.gamepad.deviceName = controller->deviceName;
18047 ev.gamepad.state = controller->state;
18048 fpl__PushInternalEvent(&ev);
18049 }
18050 }
18051 }
18052 }
18053}
18054
18056 FPL__CheckPlatform(false);
18057 FPL__CheckArgumentNull(outStates, false);
18058 fpl__PlatformAppState *appState = fpl__global__AppState;
18059 if (appState->initFlags & fplInitFlags_GameController) {
18060#if defined(FPL_PLATFORM_LINUX)
18061 fpl__LinuxGameControllersState *controllersState = &appState->plinux.controllersState;
18062 fpl__LinuxPollGameControllers(&appState->currentSettings, controllersState, false);
18063
18064 fplAssert(fplArrayCount(controllersState->controllers) == fplArrayCount(outStates->deviceStates));
18065 for (int i = 0; i < fplArrayCount(controllersState->controllers); ++i) {
18066 outStates->deviceStates[i] = controllersState->controllers[i].state;
18067 }
18068 return(true);
18069#endif
18070 }
18071 return(false);
18072}
18073
18074#endif // FPL__ENABLE_WINDOW
18075
18076//
18077// Linux Threading
18078//
18079fpl_platform_api bool fplSignalInit(fplSignalHandle *signal, const fplSignalValue initialValue) {
18080 FPL__CheckArgumentNull(signal, false);
18081 if (signal->isValid) {
18082 FPL__ERROR(FPL__MODULE_THREADING, "Signal '%p' is already valid", signal);
18083 return false;
18084 }
18085 int linuxEventHandle = eventfd((initialValue == fplSignalValue_Set) ? 1 : 0, EFD_CLOEXEC);
18086 if (linuxEventHandle == -1) {
18087 FPL__ERROR(FPL__MODULE_THREADING, "Failed initializing signal '%p'", signal);
18088 return false;
18089 }
18090 fplClearStruct(signal);
18091 signal->isValid = true;
18092 signal->internalHandle.linuxEventHandle = linuxEventHandle;
18093 return(true);
18094}
18095
18097 if (signal != fpl_null && signal->isValid) {
18098 close(signal->internalHandle.linuxEventHandle);
18099 fplClearStruct(signal);
18100 }
18101}
18102
18104 FPL__CheckArgumentNull(signal, false);
18105 if (!signal->isValid) {
18106 FPL__ERROR(FPL__MODULE_THREADING, "Signal '%p' is not valid", signal);
18107 return(false);
18108 }
18109 int ev = signal->internalHandle.linuxEventHandle;
18110 if (timeout == FPL_TIMEOUT_INFINITE) {
18111 uint64_t value;
18112 read(ev, &value, sizeof(value));
18113 return true;
18114 } else {
18115 fd_set f;
18116 FD_ZERO(&f);
18117 FD_SET(ev, &f);
18118 struct timeval t = { 0, timeout * 1000 };
18119 int selectResult = select(1, &f, NULL, NULL, &t);
18120 if (selectResult == 0) {
18121 // Timeout
18122 return false;
18123 } else if (selectResult == -1) {
18124 // Error
18125 return false;
18126 } else {
18127 return true;
18128 }
18129 }
18130}
18131
18132fpl_internal bool fpl__LinuxSignalWaitForMultiple(fplSignalHandle *signals[], const uint32_t minCount, const uint32_t maxCount, const size_t stride, const fplTimeoutValue timeout) {
18133 FPL__CheckArgumentNull(signals, false);
18134 FPL__CheckArgumentMax(maxCount, FPL_MAX_SIGNAL_COUNT, false);
18135 const size_t actualStride = stride > 0 ? stride : sizeof(fplSignalHandle *);
18136 for (uint32_t index = 0; index < maxCount; ++index) {
18137 fplSignalHandle *signal = *(fplSignalHandle **)((uint8_t *)signals + index * actualStride);
18138 if (signal == fpl_null) {
18139 FPL__ERROR(FPL__MODULE_THREADING, "Signal for index '%d' are not allowed to be null", index);
18140 return false;
18141 }
18142 if (!signal->isValid) {
18143 FPL__ERROR(FPL__MODULE_THREADING, "Signal '%p' for index '%d' is not valid", signal, index);
18144 return false;
18145 }
18146 }
18147
18148 int e = epoll_create(maxCount);
18149 fplAssert(e != 0);
18150
18151 // @MEMORY(final): This wastes a lof memory, use temporary memory allocation here
18152
18153 // Register events and map each to the array index
18154 struct epoll_event events[FPL_MAX_SIGNAL_COUNT];
18155 for (int index = 0; index < maxCount; index++) {
18156 events[index].events = EPOLLIN;
18157 events[index].data.u32 = index;
18158 fplSignalHandle *signal = *(fplSignalHandle **)((uint8_t *)signals + index * actualStride);
18159 int x = epoll_ctl(e, EPOLL_CTL_ADD, signal->internalHandle.linuxEventHandle, events + index);
18160 fplAssert(x == 0);
18161 }
18162
18163 // Wait
18164 int t = timeout == FPL_TIMEOUT_INFINITE ? -1 : timeout;
18165 int eventsResult = -1;
18166 int waiting = minCount;
18167 struct epoll_event revent[FPL_MAX_SIGNAL_COUNT];
18168 while (waiting > 0) {
18169 int ret = epoll_wait(e, revent, waiting, t);
18170 if (ret == 0) {
18171 if (minCount == maxCount) {
18172 eventsResult = -1;
18173 }
18174 break;
18175 }
18176 for (int eventIndex = 0; eventIndex < ret; eventIndex++) {
18177 uint32_t signalIndex = revent[eventIndex].data.u32;
18178 fplSignalHandle *signal = *(fplSignalHandle **)((uint8_t *)signals + signalIndex * actualStride);
18179 epoll_ctl(e, EPOLL_CTL_DEL, signal->internalHandle.linuxEventHandle, NULL);
18180 }
18181 eventsResult = revent[0].data.u32;
18182 waiting -= ret;
18183 }
18184 close(e);
18185 bool result = (waiting == 0);
18186 return(result);
18187}
18188
18189fpl_platform_api bool fplSignalWaitForAll(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
18190 bool result = fpl__LinuxSignalWaitForMultiple(signals, count, count, stride, timeout);
18191 return(result);
18192}
18193
18194fpl_platform_api bool fplSignalWaitForAny(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout) {
18195 bool result = fpl__LinuxSignalWaitForMultiple(signals, 1, count, stride, timeout);
18196 return(result);
18197}
18198
18200 FPL__CheckArgumentNull(signal, false);
18201 if (!signal->isValid) {
18202 FPL__ERROR(FPL__MODULE_THREADING, "Signal '%p' is not valid", signal);
18203 return(false);
18204 }
18205 uint64_t value = 1;
18206 int writtenBytes = write(signal->internalHandle.linuxEventHandle, &value, sizeof(value));
18207 bool result = writtenBytes == sizeof(value);
18208 return(result);
18209}
18210
18211//
18212// Linux Hardware
18213//
18215 FPL__CheckArgumentNull(outInfos, false);
18216 bool result = false;
18217
18218 // https://git.i-scream.org/?p=libstatgrab.git;a=blob;f=src/libstatgrab/memory_stats.c;h=a6f6fb926b72d3b691848202e397e3db58255648;hb=HEAD
18219
18220 return(result);
18221}
18222
18223//
18224// Linux Paths
18225//
18226fpl_internal size_t fpl__LinuxLocaleToISO639(const char *source, char *target, const size_t maxTargetLen) {
18227 size_t result = fplGetStringLength(source);
18228 if (target != fpl_null) {
18229 fplCopyStringLen(source, result, target, maxTargetLen);
18230 char *p = target;
18231 while (*p) {
18232 if (*p == '_') {
18233 *p = '-'; // Replace underscore with minus
18234 } else if (*p == '.') {
18235 *p = '\0'; // Replace dot with zero char
18236 break;
18237 }
18238 ++p;
18239 }
18240 }
18241 return(result);
18242}
18243
18244//
18245// Linux Localization
18246//
18247fpl_platform_api size_t fplGetSystemLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
18248 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
18249 char *locale = setlocale(LC_CTYPE, NULL);
18250 size_t result = fpl__LinuxLocaleToISO639(locale, buffer, maxBufferLen);
18251 return(result);
18252}
18253
18254fpl_platform_api size_t fplGetUserLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
18255 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
18256 char *locale = setlocale(LC_ALL, NULL);
18257 size_t result = fpl__LinuxLocaleToISO639(locale, buffer, maxBufferLen);
18258 return(result);
18259}
18260
18261fpl_platform_api size_t fplGetInputLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
18262 FPL__CheckArgumentInvalid(targetFormat, targetFormat == fplLocaleFormat_None, 0);
18263 char *locale = setlocale(LC_ALL, NULL);
18264 size_t result = fpl__LinuxLocaleToISO639(locale, buffer, maxBufferLen);
18265 return(result);
18266}
18267
18268#endif // FPL_PLATFORM_LINUX
18269
18270// ############################################################################
18271//
18272// > UNIX_PLATFORM
18273//
18274// ############################################################################
18275#if defined(FPL_PLATFORM_UNIX)
18276fpl_internal void fpl__UnixReleasePlatform(fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
18277}
18278
18279fpl_internal bool fpl__UnixInitPlatform(const fplInitFlags initFlags, const fplSettings *initSettings, fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
18280 return true;
18281}
18282
18283//
18284// Unix Hardware
18285//
18287 // @IMPLEMENT(final/Unix): fplMemoryGetInfos
18288 return(false);
18289}
18290
18291//
18292// Unix Localization
18293//
18294fpl_platform_api size_t fplGetSystemLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
18295 // @IMPLEMENT(final/Unix): fplGetSystemLocale
18296 return(0);
18297}
18298
18299fpl_platform_api size_t fplGetUserLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
18300 // @IMPLEMENT(final/Unix): fplGetUserLocale
18301 return(0);
18302}
18303
18304fpl_platform_api size_t fplGetInputLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen) {
18305 // @IMPLEMENT(final/Unix): fplGetInputLocale
18306 return(0);
18307}
18308#endif // FPL_PLATFORM_UNIX
18309
18310// ****************************************************************************
18311//
18312// > VIDEO_BACKENDS
18313//
18314// ****************************************************************************
18315#if !defined(FPL__VIDEO_BACKENDS_IMPLEMENTED) && defined(FPL__ENABLE_VIDEO)
18316# define FPL__VIDEO_BACKENDS_IMPLEMENTED
18317
18318//
18319// Video Backend Abstraction
18320//
18321
18322// Video data used for every backend (Software backbuffer for now)
18323typedef struct fpl__VideoData {
18324#if defined(FPL__ENABLE_VIDEO_SOFTWARE)
18325 fplVideoBackBuffer backbuffer;
18326#endif
18327 uint64_t dummy;
18328} fpl__VideoData;
18329
18330struct fpl__VideoBackend;
18331
18332#define FPL__FUNC_VIDEO_BACKEND_LOAD(name) bool name(const fpl__PlatformAppState *appState, struct fpl__VideoBackend *backend)
18333typedef FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__func_VideoBackendLoad);
18334
18335#define FPL__FUNC_VIDEO_BACKEND_UNLOAD(name) void name(const fpl__PlatformAppState *appState, struct fpl__VideoBackend *backend)
18336typedef FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__func_VideoBackendUnload);
18337
18338#define FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(name) bool name(const fpl__PlatformAppState *appState, const fplVideoSettings *videoSettings, fpl__PlatformWindowState *windowState, struct fpl__VideoBackend *backend)
18339typedef FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(fpl__func_VideoBackendPrepareWindow);
18340
18341#define FPL__FUNC_VIDEO_BACKEND_FINALIZEWINDOW(name) bool name(const fpl__PlatformAppState *appState, const fplVideoSettings *videoSettings, fpl__PlatformWindowState *windowState, struct fpl__VideoBackend *backend)
18342typedef FPL__FUNC_VIDEO_BACKEND_FINALIZEWINDOW(fpl__func_VideoBackendFinalizeWindow);
18343
18344#define FPL__FUNC_VIDEO_BACKEND_DESTROYEDWINDOW(name) void name(const fpl__PlatformAppState *appState, struct fpl__VideoBackend *backend)
18345typedef FPL__FUNC_VIDEO_BACKEND_DESTROYEDWINDOW(fpl__func_VideoBackendDestroyedWindow);
18346
18347#define FPL__FUNC_VIDEO_BACKEND_INITIALIZE(name) bool name(const fpl__PlatformAppState *appState, const fpl__PlatformWindowState *windowState, const fplVideoSettings *videoSettings, const fpl__VideoData *data, struct fpl__VideoBackend *backend)
18348typedef FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__func_VideoBackendInitialize);
18349
18350#define FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(name) void name(const fpl__PlatformAppState *appState, const fpl__PlatformWindowState *windowState, struct fpl__VideoBackend *backend)
18351typedef FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__func_VideoBackendShutdown);
18352
18353#define FPL__FUNC_VIDEO_BACKEND_PRESENT(name) void name(const fpl__PlatformAppState *appState, const fpl__PlatformWindowState *windowState, const fpl__VideoData *data, const struct fpl__VideoBackend *backend)
18354typedef FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__func_VideoBackendPresent);
18355
18356#define FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(name) const void *name(const struct fpl__VideoBackend *backend, const char *procName)
18357typedef FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(fpl__func_VideoBackendGetProcedure);
18358
18359#define FPL__FUNC_VIDEO_BACKEND_GETREQUIREMENTS(name) bool name(fplVideoRequirements *requirements)
18360typedef FPL__FUNC_VIDEO_BACKEND_GETREQUIREMENTS(fpl__func_VideoBackendGetRequirements);
18361
18362typedef struct fpl__VideoContext {
18363 fpl__func_VideoBackendLoad *loadFunc;
18364 fpl__func_VideoBackendUnload *unloadFunc;
18365 fpl__func_VideoBackendInitialize *initializeFunc;
18366 fpl__func_VideoBackendShutdown *shutdownFunc;
18367 fpl__func_VideoBackendPrepareWindow *prepareWindowFunc;
18368 fpl__func_VideoBackendFinalizeWindow *finalizeWindowFunc;
18369 fpl__func_VideoBackendDestroyedWindow *destroyedWindowFunc;
18370 fpl__func_VideoBackendPresent *presentFunc;
18371 fpl__func_VideoBackendGetProcedure *getProcedureFunc;
18372 fpl__func_VideoBackendGetRequirements *getRequirementsFunc;
18373 fpl_b32 recreateOnResize;
18374} fpl__VideoContext;
18375
18376// Video context stubs
18377fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_Load_Stub) { return(true); }
18378fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_Unload_Stub) {}
18379fpl_internal FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(fpl__VideoBackend_PrepareWindow_Stub) { return(true); }
18380fpl_internal FPL__FUNC_VIDEO_BACKEND_FINALIZEWINDOW(fpl__VideoBackend_FinalizeWindow_Stub) { return(true); }
18381fpl_internal FPL__FUNC_VIDEO_BACKEND_DESTROYEDWINDOW(fpl__VideoBackend_DestroyedWindow_Stub) {}
18382fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_Initialize_Stub) { return(false); }
18383fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_Shutdown_Stub) {}
18384fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_Present_Stub) {}
18385fpl_internal FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(fpl__VideoBackend_GetProcedure_Stub) { return(fpl_null); }
18386fpl_internal FPL__FUNC_VIDEO_BACKEND_GETREQUIREMENTS(fpl__VideoBackend_GetRequirements_Stub) { return(false); }
18387
18388fpl_internal fpl__VideoContext fpl__StubVideoContext() {
18389 fpl__VideoContext result = fplZeroInit;
18390 result.loadFunc = fpl__VideoBackend_Load_Stub;
18391 result.unloadFunc = fpl__VideoBackend_Unload_Stub;
18392 result.prepareWindowFunc = fpl__VideoBackend_PrepareWindow_Stub;
18393 result.finalizeWindowFunc = fpl__VideoBackend_FinalizeWindow_Stub;
18394 result.destroyedWindowFunc = fpl__VideoBackend_DestroyedWindow_Stub;
18395 result.initializeFunc = fpl__VideoBackend_Initialize_Stub;
18396 result.shutdownFunc = fpl__VideoBackend_Shutdown_Stub;
18397 result.presentFunc = fpl__VideoBackend_Present_Stub;
18398 result.getProcedureFunc = fpl__VideoBackend_GetProcedure_Stub;
18399 result.getRequirementsFunc = fpl__VideoBackend_GetRequirements_Stub;
18400 return(result);
18401}
18402
18403// "VIDEOSYS" Video Backend Magic 8CC
18404#define FPL__VIDEOBACKEND_MAGIC (uint64_t)0x564944454f535953
18405
18406typedef struct fpl__VideoBackend {
18407 uint64_t magic;
18408 fplVideoSurface surface;
18409} fpl__VideoBackend;
18410
18411// ############################################################################
18412//
18413// > VIDEO_BACKEND_OPENGL_WIN32
18414//
18415// ############################################################################
18416#if defined(FPL__ENABLE_VIDEO_OPENGL) && defined(FPL_PLATFORM_WINDOWS)
18417
18418#define FPL__GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001
18419#define FPL__GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002
18420#define FPL__GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT 0x00000004
18421#define FPL__GL_CONTEXT_FLAG_NO_ERROR_BIT 0x00000008
18422#define FPL__GL_CONTEXT_CORE_PROFILE_BIT 0x00000001
18423#define FPL__GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002
18424
18425#define FPL__WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
18426#define FPL__WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
18427#define FPL__WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
18428#define FPL__WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
18429#define FPL__WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
18430#define FPL__WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
18431#define FPL__WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
18432#define FPL__WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
18433#define FPL__WGL_CONTEXT_FLAGS_ARB 0x2094
18434#define FPL__WGL_DRAW_TO_WINDOW_ARB 0x2001
18435#define FPL__WGL_ACCELERATION_ARB 0x2003
18436#define FPL__WGL_SWAP_METHOD_ARB 0x2007
18437#define FPL__WGL_SUPPORT_OPENGL_ARB 0x2010
18438#define FPL__WGL_DOUBLE_BUFFER_ARB 0x2011
18439#define FPL__WGL_PIXEL_TYPE_ARB 0x2013
18440#define FPL__WGL_COLOR_BITS_ARB 0x2014
18441#define FPL__WGL_ALPHA_BITS_ARB 0x201B
18442#define FPL__WGL_DEPTH_BITS_ARB 0x2022
18443#define FPL__WGL_STENCIL_BITS_ARB 0x2023
18444#define FPL__WGL_FULL_ACCELERATION_ARB 0x2027
18445#define FPL__WGL_SWAP_EXCHANGE_ARB 0x2028
18446#define FPL__WGL_TYPE_RGBA_ARB 0x202B
18447#define FPL__WGL_SAMPLE_BUFFERS_ARB 0x2041
18448#define FPL__WGL_SAMPLES_ARB 0x2042
18449
18450#define FPL__FUNC_WGL_wglMakeCurrent(name) BOOL WINAPI name(HDC deviceContext, HGLRC renderingContext)
18451typedef FPL__FUNC_WGL_wglMakeCurrent(fpl__win32_func_wglMakeCurrent);
18452#define FPL__FUNC_WGL_wglGetProcAddress(name) PROC WINAPI name(LPCSTR procedure)
18453typedef FPL__FUNC_WGL_wglGetProcAddress(fpl__win32_func_wglGetProcAddress);
18454#define FPL__FUNC_WGL_wglDeleteContext(name) BOOL WINAPI name(HGLRC renderingContext)
18455typedef FPL__FUNC_WGL_wglDeleteContext(fpl__win32_func_wglDeleteContext);
18456#define FPL__FUNC_WGL_wglCreateContext(name) HGLRC WINAPI name(HDC deviceContext)
18457typedef FPL__FUNC_WGL_wglCreateContext(fpl__win32_func_wglCreateContext);
18458
18459#define FPL__FUNC_WGL_wglChoosePixelFormatARB(name) BOOL WINAPI name(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats)
18460typedef FPL__FUNC_WGL_wglChoosePixelFormatARB(fpl__win32_func_wglChoosePixelFormatARB);
18461#define FPL__FUNC_WGL_wglCreateContextAttribsARB(name) HGLRC WINAPI name(HDC hDC, HGLRC hShareContext, const int *attribList)
18462typedef FPL__FUNC_WGL_wglCreateContextAttribsARB(fpl__win32_func_wglCreateContextAttribsARB);
18463#define FPL__FUNC_WGL_wglSwapIntervalEXT(name) BOOL WINAPI name(int interval)
18464typedef FPL__FUNC_WGL_wglSwapIntervalEXT(fpl__win32_func_wglSwapIntervalEXT);
18465
18466typedef struct fpl__Win32OpenGLApi {
18467 HMODULE openglLibrary;
18468 fpl__win32_func_wglMakeCurrent *wglMakeCurrent;
18469 fpl__win32_func_wglGetProcAddress *wglGetProcAddress;
18470 fpl__win32_func_wglDeleteContext *wglDeleteContext;
18471 fpl__win32_func_wglCreateContext *wglCreateContext;
18472 fpl__win32_func_wglChoosePixelFormatARB *wglChoosePixelFormatARB;
18473 fpl__win32_func_wglCreateContextAttribsARB *wglCreateContextAttribsARB;
18474 fpl__win32_func_wglSwapIntervalEXT *wglSwapIntervalEXT;
18475} fpl__Win32OpenGLApi;
18476
18477fpl_internal void fpl__UnloadWin32OpenGLApi(fpl__Win32OpenGLApi *api) {
18478 if (api->openglLibrary != fpl_null) {
18479 FreeLibrary(api->openglLibrary);
18480 }
18481 fplClearStruct(api);
18482}
18483
18484fpl_internal bool fpl__LoadWin32OpenGLApi(fpl__Win32OpenGLApi *api, const char *libraryName) {
18485 if (fplGetStringLength(libraryName) == 0) {
18486 libraryName = "opengl32.dll";
18487 }
18488 bool result = false;
18489 fplClearStruct(api);
18490 do {
18491 HMODULE openglLibrary = fpl_null;
18492 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_VIDEO_OPENGL, openglLibrary, libraryName);
18493 api->openglLibrary = openglLibrary;
18494 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_VIDEO_OPENGL, openglLibrary, libraryName, api, fpl__win32_func_wglGetProcAddress, wglGetProcAddress);
18495 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_VIDEO_OPENGL, openglLibrary, libraryName, api, fpl__win32_func_wglCreateContext, wglCreateContext);
18496 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_VIDEO_OPENGL, openglLibrary, libraryName, api, fpl__win32_func_wglDeleteContext, wglDeleteContext);
18497 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_VIDEO_OPENGL, openglLibrary, libraryName, api, fpl__win32_func_wglMakeCurrent, wglMakeCurrent);
18498 result = true;
18499 } while (0);
18500 if (!result) {
18501 fpl__UnloadWin32OpenGLApi(api);
18502 }
18503 return(result);
18504}
18505
18506typedef struct fpl__VideoBackendWin32OpenGL {
18507 fpl__VideoBackend base;
18508 fpl__Win32OpenGLApi api;
18509 HGLRC renderingContext;
18510} fpl__VideoBackendWin32OpenGL;
18511
18512fpl_internal FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(fpl__VideoBackend_Win32OpenGL_GetProcedure) {
18513 const fpl__VideoBackendWin32OpenGL *nativeBackend = (const fpl__VideoBackendWin32OpenGL *)backend;
18514 void *result = (void *)GetProcAddress(nativeBackend->api.openglLibrary, procName);
18515 return(result);
18516}
18517
18518fpl_internal LRESULT CALLBACK fpl__Win32OpenGLTemporaryWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
18519 fpl__Win32AppState *appState = &fpl__global__AppState->win32;
18520 const fpl__Win32Api *wapi = &appState->winApi;
18521 switch (message) {
18522 case WM_CLOSE:
18523 wapi->user.PostQuitMessage(0);
18524 break;
18525 default:
18526 return wapi->user.DefWindowProcW(hWnd, message, wParam, lParam);
18527 }
18528 return 0;
18529}
18530
18531fpl_internal FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(fpl__VideoBackend_Win32OpenGL_PrepareWindow) {
18532 const fpl__Win32AppState *nativeAppState = &appState->win32;
18533 const fpl__Win32Api *wapi = &nativeAppState->winApi;
18534 fpl__Win32WindowState *nativeWindowState = &windowState->win32;
18535
18536 nativeWindowState->pixelFormat = 0;
18537
18538 if (videoSettings->graphics.opengl.compabilityFlags != fplOpenGLCompabilityFlags_Legacy) {
18539 fpl__Win32OpenGLApi glApi;
18540 if (fpl__LoadWin32OpenGLApi(&glApi, videoSettings->graphics.opengl.libraryFile)) {
18541 // Register temporary window class
18542 WNDCLASSEXW windowClass = fplZeroInit;
18543 windowClass.cbSize = sizeof(windowClass);
18544 windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
18545 windowClass.lpfnWndProc = fpl__Win32OpenGLTemporaryWindowProc;
18546 windowClass.hInstance = GetModuleHandleW(fpl_null);
18547 windowClass.hCursor = fpl__win32_LoadCursor(fpl_null, IDC_ARROW);
18548 windowClass.lpszClassName = L"FPL_Temp_GL_Window";
18549 if (wapi->user.RegisterClassExW(&windowClass)) {
18550 // Create temporary window
18551 HWND tempWindowHandle = wapi->user.CreateWindowExW(0, windowClass.lpszClassName, L"FPL Temp GL Window", 0, 0, 0, 1, 1, fpl_null, fpl_null, windowClass.hInstance, fpl_null);
18552 if (tempWindowHandle != fpl_null) {
18553 // Get temporary device context
18554 HDC tempDC = wapi->user.GetDC(tempWindowHandle);
18555 if (tempDC != fpl_null) {
18556 // Get legacy pixel format
18557 PIXELFORMATDESCRIPTOR fakePFD = fplZeroInit;
18558 fakePFD.nSize = sizeof(fakePFD);
18559 fakePFD.nVersion = 1;
18560 fakePFD.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
18561 fakePFD.iPixelType = PFD_TYPE_RGBA;
18562 fakePFD.cColorBits = 32;
18563 fakePFD.cAlphaBits = 8;
18564 fakePFD.cDepthBits = 24;
18565 int fakePFDID = wapi->gdi.ChoosePixelFormat(tempDC, &fakePFD);
18566 if (fakePFDID != 0) {
18567 if (wapi->gdi.SetPixelFormat(tempDC, fakePFDID, &fakePFD)) {
18568 // Create temporary rendering context
18569 HGLRC tempCtx = glApi.wglCreateContext(tempDC);
18570 if (tempCtx != fpl_null) {
18571 if (glApi.wglMakeCurrent(tempDC, tempCtx)) {
18572 glApi.wglChoosePixelFormatARB = (fpl__win32_func_wglChoosePixelFormatARB *)glApi.wglGetProcAddress("wglChoosePixelFormatARB");
18573 if (glApi.wglChoosePixelFormatARB != fpl_null) {
18574 int multisampleCount = (int)videoSettings->graphics.opengl.multiSamplingCount;
18575 const int pixelAttribs[] = {
18576 FPL__WGL_DRAW_TO_WINDOW_ARB, 1,
18577 FPL__WGL_SUPPORT_OPENGL_ARB, 1,
18578 FPL__WGL_DOUBLE_BUFFER_ARB, 1,
18579 FPL__WGL_PIXEL_TYPE_ARB, FPL__WGL_TYPE_RGBA_ARB,
18580 FPL__WGL_ACCELERATION_ARB, FPL__WGL_FULL_ACCELERATION_ARB,
18581 FPL__WGL_COLOR_BITS_ARB, 32,
18582 FPL__WGL_ALPHA_BITS_ARB, 8,
18583 FPL__WGL_DEPTH_BITS_ARB, 24,
18584 FPL__WGL_STENCIL_BITS_ARB, 8,
18585 FPL__WGL_SAMPLE_BUFFERS_ARB, (multisampleCount > 0) ? 1 : 0,
18586 FPL__WGL_SAMPLES_ARB, multisampleCount,
18587 0
18588 };
18589 int pixelFormat;
18590 UINT numFormats;
18591 if (glApi.wglChoosePixelFormatARB(tempDC, pixelAttribs, NULL, 1, &pixelFormat, &numFormats)) {
18592 nativeWindowState->pixelFormat = pixelFormat;
18593 }
18594 }
18595 glApi.wglMakeCurrent(fpl_null, fpl_null);
18596 }
18597 glApi.wglDeleteContext(tempCtx);
18598 }
18599 }
18600 }
18601 wapi->user.ReleaseDC(tempWindowHandle, tempDC);
18602 }
18603 wapi->user.DestroyWindow(tempWindowHandle);
18604 }
18605 }
18606 fpl__UnloadWin32OpenGLApi(&glApi);
18607 }
18608 }
18609 return(true);
18610}
18611
18612fpl_internal FPL__FUNC_VIDEO_BACKEND_FINALIZEWINDOW(fpl__VideoBackend_Win32OpenGL_FinalizeWindow) {
18613 const fpl__Win32AppState *nativeAppState = &appState->win32;
18614 const fpl__Win32Api *wapi = &nativeAppState->winApi;
18615 fpl__Win32WindowState *nativeWindowState = &windowState->win32;
18616
18617 //
18618 // Prepare window for OpenGL
18619 //
18620 HDC deviceContext = nativeWindowState->deviceContext;
18621 HWND handle = nativeWindowState->windowHandle;
18622 PIXELFORMATDESCRIPTOR pfd = fplZeroInit;
18623
18624 // We may got a pixel format from the pre-setup
18625 bool pixelFormatSet = false;
18626 if (nativeWindowState->pixelFormat != 0) {
18627 wapi->gdi.DescribePixelFormat(deviceContext, nativeWindowState->pixelFormat, sizeof(pfd), &pfd);
18628 pixelFormatSet = wapi->gdi.SetPixelFormat(deviceContext, nativeWindowState->pixelFormat, &pfd) == TRUE;
18629 if (!pixelFormatSet) {
18630 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "Failed setting Pixelformat '%d' from pre setup", nativeWindowState->pixelFormat);
18631 }
18632 }
18633 if (!pixelFormatSet) {
18634 fplClearStruct(&pfd);
18635 pfd.nSize = sizeof(pfd);
18636 pfd.nVersion = 1;
18637 pfd.dwFlags = PFD_DOUBLEBUFFER | PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW;
18638 pfd.iPixelType = PFD_TYPE_RGBA;
18639 pfd.cColorBits = 32;
18640 pfd.cDepthBits = 24;
18641 pfd.cAlphaBits = 8;
18642 pfd.cStencilBits = 8;
18643 pfd.iLayerType = PFD_MAIN_PLANE;
18644 int pixelFormat = wapi->gdi.ChoosePixelFormat(deviceContext, &pfd);
18645 if (!pixelFormat) {
18646 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "Failed choosing RGBA Legacy Pixelformat for Color/Depth/Alpha (%d,%d,%d) and DC '%x'", pfd.cColorBits, pfd.cDepthBits, pfd.cAlphaBits, deviceContext);
18647 return false;
18648 }
18649 wapi->gdi.DescribePixelFormat(deviceContext, pixelFormat, sizeof(pfd), &pfd);
18650 if (!wapi->gdi.SetPixelFormat(deviceContext, pixelFormat, &pfd)) {
18651 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "Failed setting RGBA Pixelformat '%d' for Color/Depth/Alpha (%d,%d,%d and DC '%x')", pixelFormat, pfd.cColorBits, pfd.cDepthBits, pfd.cAlphaBits, deviceContext);
18652 return false;
18653 }
18654 }
18655 return true;
18656}
18657
18658fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_Win32OpenGL_Shutdown) {
18659 const fpl__Win32AppState *nativeAppState = &appState->win32;
18660
18661 fpl__VideoBackendWin32OpenGL *nativeBackend = (fpl__VideoBackendWin32OpenGL *)backend;
18662 const fpl__Win32OpenGLApi *glapi = &nativeBackend->api;
18663
18664 if (nativeBackend->renderingContext) {
18665 glapi->wglMakeCurrent(fpl_null, fpl_null);
18666 glapi->wglDeleteContext(nativeBackend->renderingContext);
18667 nativeBackend->renderingContext = fpl_null;
18668 }
18669}
18670
18671fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_Win32OpenGL_Initialize) {
18672 const fpl__Win32AppState *nativeAppState = &appState->win32;
18673 const fpl__Win32WindowState *nativeWindowState = &windowState->win32;
18674 const fpl__Win32Api *wapi = &nativeAppState->winApi;
18675
18676 fpl__VideoBackendWin32OpenGL *nativeBackend = (fpl__VideoBackendWin32OpenGL *)backend;
18677
18678 fpl__Win32OpenGLApi *glapi = &nativeBackend->api;
18679
18680 //
18681 // Create opengl rendering context
18682 //
18683 HDC deviceContext = nativeWindowState->deviceContext;
18684 HGLRC legacyRenderingContext = glapi->wglCreateContext(deviceContext);
18685 if (!legacyRenderingContext) {
18686 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "Failed creating Legacy OpenGL Rendering Context for DC '%x')", deviceContext);
18687 return false;
18688 }
18689 if (!glapi->wglMakeCurrent(deviceContext, legacyRenderingContext)) {
18690 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "Failed activating Legacy OpenGL Rendering Context for DC '%x' and RC '%x')", deviceContext, legacyRenderingContext);
18691 glapi->wglDeleteContext(legacyRenderingContext);
18692 return false;
18693 }
18694
18695 // Load WGL Extensions
18696 glapi->wglSwapIntervalEXT = (fpl__win32_func_wglSwapIntervalEXT *)glapi->wglGetProcAddress("wglSwapIntervalEXT");
18697 glapi->wglChoosePixelFormatARB = (fpl__win32_func_wglChoosePixelFormatARB *)glapi->wglGetProcAddress("wglChoosePixelFormatARB");
18698 glapi->wglCreateContextAttribsARB = (fpl__win32_func_wglCreateContextAttribsARB *)glapi->wglGetProcAddress("wglCreateContextAttribsARB");
18699
18700 // Disable legacy context
18701 glapi->wglMakeCurrent(fpl_null, fpl_null);
18702
18703 HGLRC activeRenderingContext;
18704 if (videoSettings->graphics.opengl.compabilityFlags != fplOpenGLCompabilityFlags_Legacy) {
18705 // @NOTE(final): This is only available in OpenGL 3.0+
18706 if (!(videoSettings->graphics.opengl.majorVersion >= 3 && videoSettings->graphics.opengl.minorVersion >= 0)) {
18707 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "You have not specified the 'majorVersion' and 'minorVersion' in the VideoSettings");
18708 return false;
18709 }
18710 if (glapi->wglChoosePixelFormatARB == fpl_null) {
18711 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "wglChoosePixelFormatARB is not available, modern OpenGL is not available for your video card");
18712 return false;
18713 }
18714 if (glapi->wglCreateContextAttribsARB == fpl_null) {
18715 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "wglCreateContextAttribsARB is not available, modern OpenGL is not available for your video card");
18716 return false;
18717 }
18718
18719 int profile = 0;
18720 int flags = 0;
18721 if (videoSettings->graphics.opengl.compabilityFlags & fplOpenGLCompabilityFlags_Core) {
18722 profile = FPL__WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
18723 } else if (videoSettings->graphics.opengl.compabilityFlags & fplOpenGLCompabilityFlags_Compability) {
18724 profile = FPL__WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
18725 } else {
18726 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "No opengl compability profile selected, please specific Core fplOpenGLCompabilityFlags_Core or fplOpenGLCompabilityFlags_Compability");
18727 return false;
18728 }
18729 if (videoSettings->graphics.opengl.compabilityFlags & fplOpenGLCompabilityFlags_Forward) {
18730 flags = FPL__WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
18731 }
18732
18733 int contextAttribIndex = 0;
18734 int contextAttribList[20 + 1] = fplZeroInit;
18735 contextAttribList[contextAttribIndex++] = FPL__WGL_CONTEXT_MAJOR_VERSION_ARB;
18736 contextAttribList[contextAttribIndex++] = (int)videoSettings->graphics.opengl.majorVersion;
18737 contextAttribList[contextAttribIndex++] = FPL__WGL_CONTEXT_MINOR_VERSION_ARB;
18738 contextAttribList[contextAttribIndex++] = (int)videoSettings->graphics.opengl.minorVersion;
18739 contextAttribList[contextAttribIndex++] = FPL__WGL_CONTEXT_PROFILE_MASK_ARB;
18740 contextAttribList[contextAttribIndex++] = profile;
18741 if (flags > 0) {
18742 contextAttribList[contextAttribIndex++] = FPL__WGL_CONTEXT_FLAGS_ARB;
18743 contextAttribList[contextAttribIndex++] = flags;
18744 }
18745
18746 // Create modern opengl rendering context
18747 HGLRC modernRenderingContext = glapi->wglCreateContextAttribsARB(deviceContext, 0, contextAttribList);
18748 if (modernRenderingContext) {
18749 if (!glapi->wglMakeCurrent(deviceContext, modernRenderingContext)) {
18750 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "Warning: Failed activating Modern OpenGL Rendering Context for version (%d.%d) and compability flags (%d) and DC '%x') -> Fallback to legacy context", videoSettings->graphics.opengl.majorVersion, videoSettings->graphics.opengl.minorVersion, videoSettings->graphics.opengl.compabilityFlags, deviceContext);
18751
18752 glapi->wglDeleteContext(modernRenderingContext);
18753 modernRenderingContext = fpl_null;
18754
18755 // Fallback to legacy context
18756 glapi->wglMakeCurrent(deviceContext, legacyRenderingContext);
18757 activeRenderingContext = legacyRenderingContext;
18758 } else {
18759 // Destroy legacy rendering context
18760 glapi->wglDeleteContext(legacyRenderingContext);
18761 legacyRenderingContext = fpl_null;
18762 activeRenderingContext = modernRenderingContext;
18763 }
18764 } else {
18765 FPL__ERROR(FPL__MODULE_VIDEO_OPENGL, "Warning: Failed creating Modern OpenGL Rendering Context for version (%d.%d) and compability flags (%d) and DC '%x') -> Fallback to legacy context", videoSettings->graphics.opengl.majorVersion, videoSettings->graphics.opengl.minorVersion, videoSettings->graphics.opengl.compabilityFlags, deviceContext);
18766
18767 // Fallback to legacy context
18768 glapi->wglMakeCurrent(deviceContext, legacyRenderingContext);
18769 activeRenderingContext = legacyRenderingContext;
18770 }
18771 } else {
18772 // Caller wants legacy context
18773 glapi->wglMakeCurrent(deviceContext, legacyRenderingContext);
18774 activeRenderingContext = legacyRenderingContext;
18775 }
18776
18777 fplAssert(activeRenderingContext != fpl_null);
18778 nativeBackend->renderingContext = activeRenderingContext;
18779
18780 // Set vertical syncronisation if available
18781 if (glapi->wglSwapIntervalEXT != fpl_null) {
18782 int swapInterval = videoSettings->isVSync ? 1 : 0;
18783 glapi->wglSwapIntervalEXT(swapInterval);
18784 }
18785
18786 backend->surface.window.win32.deviceContext = deviceContext;
18787 backend->surface.window.win32.windowHandle = nativeWindowState->windowHandle;
18788 backend->surface.opengl.renderingContext = (void *)activeRenderingContext;
18789
18790 return true;
18791}
18792
18793fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_Win32OpenGL_Unload) {
18794 fpl__VideoBackendWin32OpenGL *nativeBackend = (fpl__VideoBackendWin32OpenGL *)backend;
18795 fpl__UnloadWin32OpenGLApi(&nativeBackend->api);
18796 fplClearStruct(nativeBackend);
18797}
18798
18799fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_Win32OpenGL_Load) {
18800 fpl__VideoBackendWin32OpenGL *nativeBackend = (fpl__VideoBackendWin32OpenGL *)backend;
18801 const fplVideoSettings *videoSettings = &appState->currentSettings.video;
18802 fplClearStruct(nativeBackend);
18803 nativeBackend->base.magic = FPL__VIDEOBACKEND_MAGIC;
18804 if (!fpl__LoadWin32OpenGLApi(&nativeBackend->api, videoSettings->graphics.opengl.libraryFile)) {
18805 return(false);
18806 }
18807 return(true);
18808}
18809
18810fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_Win32OpenGL_Present) {
18811 const fpl__Win32AppState *win32AppState = &appState->win32;
18812 const fpl__Win32WindowState *win32WindowState = &appState->window.win32;
18813 const fpl__Win32Api *wapi = &win32AppState->winApi;
18814 const fpl__VideoBackendWin32OpenGL *nativeBackend = (fpl__VideoBackendWin32OpenGL *)backend;
18815 wapi->gdi.SwapBuffers(win32WindowState->deviceContext);
18816}
18817
18818fpl_internal fpl__VideoContext fpl__VideoBackend_Win32OpenGL_Construct() {
18819 fpl__VideoContext result = fpl__StubVideoContext();
18820 result.loadFunc = fpl__VideoBackend_Win32OpenGL_Load;
18821 result.unloadFunc = fpl__VideoBackend_Win32OpenGL_Unload;
18822 result.getProcedureFunc = fpl__VideoBackend_Win32OpenGL_GetProcedure;
18823 result.initializeFunc = fpl__VideoBackend_Win32OpenGL_Initialize;
18824 result.shutdownFunc = fpl__VideoBackend_Win32OpenGL_Shutdown;
18825 result.prepareWindowFunc = fpl__VideoBackend_Win32OpenGL_PrepareWindow;
18826 result.finalizeWindowFunc = fpl__VideoBackend_Win32OpenGL_FinalizeWindow;
18827 result.presentFunc = fpl__VideoBackend_Win32OpenGL_Present;
18828 return(result);
18829}
18830#endif // FPL__ENABLE_VIDEO_OPENGL && FPL_PLATFORM_WINDOWS
18831
18832// ############################################################################
18833//
18834// > VIDEO_BACKEND_OPENGL_X11
18835//
18836// ############################################################################
18837#if defined(FPL__ENABLE_VIDEO_OPENGL) && defined(FPL_SUBPLATFORM_X11)
18838#ifndef __gl_h_
18839typedef uint8_t GLubyte;
18840#endif
18841#ifndef GLX_H
18842typedef XID GLXDrawable;
18843typedef XID GLXWindow;
18844typedef void GLXContext_Void;
18845typedef GLXContext_Void *GLXContext;
18846typedef void GLXFBConfig_Void;
18847typedef GLXFBConfig_Void *GLXFBConfig;
18848
18849#define GLX_RGBA 4
18850#define GLX_DOUBLEBUFFER 5
18851#define GLX_RED_SIZE 8
18852#define GLX_GREEN_SIZE 9
18853#define GLX_BLUE_SIZE 10
18854#define GLX_ALPHA_SIZE 11
18855#define GLX_DEPTH_SIZE 12
18856#define GLX_STENCIL_SIZE 13
18857#define GLX_SAMPLE_BUFFERS 0x186a0
18858#define GLX_SAMPLES 0x186a1
18859
18860#define GLX_X_VISUAL_TYPE 0x22
18861#define GLX_TRUE_COLOR 0x8002
18862#define GLX_RGBA_TYPE 0x8014
18863#endif
18864
18865#define FPL__FUNC_GLX_glXQueryVersion(name) Bool name(Display *dpy, int *major, int *minor)
18866typedef FPL__FUNC_GLX_glXQueryVersion(fpl__func_glx_glXQueryVersion);
18867#define FPL__FUNC_GLX_glXChooseVisual(name) XVisualInfo* name(Display *dpy, int screen, int *attribList)
18868typedef FPL__FUNC_GLX_glXChooseVisual(fpl__func_glx_glXChooseVisual);
18869#define FPL__FUNC_GLX_glXCreateContext(name) GLXContext name(Display *dpy, XVisualInfo *vis, GLXContext shareList, Bool direct)
18870typedef FPL__FUNC_GLX_glXCreateContext(fpl__func_glx_glXCreateContext);
18871#define FPL__FUNC_GLX_glXCreateNewContext(name) GLXContext name(Display *dpy, GLXFBConfig config, int render_type, GLXContext share_list, Bool direct)
18872typedef FPL__FUNC_GLX_glXCreateNewContext(fpl__func_glx_glXCreateNewContext);
18873#define FPL__FUNC_GLX_glXDestroyContext(name) void name(Display *dpy, GLXContext ctx)
18874typedef FPL__FUNC_GLX_glXDestroyContext(fpl__func_glx_glXDestroyContext);
18875#define FPL__FUNC_GLX_glXMakeCurrent(name) Bool name(Display *dpy, GLXDrawable drawable, GLXContext ctx)
18876typedef FPL__FUNC_GLX_glXMakeCurrent(fpl__func_glx_glXMakeCurrent);
18877#define FPL__FUNC_GLX_glXSwapBuffers(name) void name(Display *dpy, GLXDrawable drawable)
18878typedef FPL__FUNC_GLX_glXSwapBuffers(fpl__func_glx_glXSwapBuffers);
18879#define FPL__FUNC_GLX_glXGetProcAddress(name) void *name(const GLubyte *procName)
18880typedef FPL__FUNC_GLX_glXGetProcAddress(fpl__func_glx_glXGetProcAddress);
18881#define FPL__FUNC_GLX_glXChooseFBConfig(name) GLXFBConfig *name(Display *dpy, int screen, const int *attrib_list, int *nelements)
18882typedef FPL__FUNC_GLX_glXChooseFBConfig(fpl__func_glx_glXChooseFBConfig);
18883#define FPL__FUNC_GLX_glXGetFBConfigs(name) GLXFBConfig *name(Display *dpy, int screen, int *nelements)
18884typedef FPL__FUNC_GLX_glXGetFBConfigs(fpl__func_glx_glXGetFBConfigs);
18885#define FPL__FUNC_GLX_glXGetVisualFromFBConfig(name) XVisualInfo *name(Display *dpy, GLXFBConfig config)
18886typedef FPL__FUNC_GLX_glXGetVisualFromFBConfig(fpl__func_glx_glXGetVisualFromFBConfig);
18887#define FPL__FUNC_GLX_glXGetFBConfigAttrib(name) int name(Display *dpy, GLXFBConfig config, int attribute, int *value)
18888typedef FPL__FUNC_GLX_glXGetFBConfigAttrib(fpl__func_glx_glXGetFBConfigAttrib);
18889#define FPL__FUNC_GLX_glXCreateWindow(name) GLXWindow name(Display *dpy, GLXFBConfig config, Window win, const int *attrib_list)
18890typedef FPL__FUNC_GLX_glXCreateWindow(fpl__func_glx_glXCreateWindow);
18891#define FPL__FUNC_GLX_glXQueryExtension(name) Bool name(Display *dpy, int *errorBase, int *eventBase)
18892typedef FPL__FUNC_GLX_glXQueryExtension(fpl__func_glx_glXQueryExtension);
18893#define FPL__FUNC_GLX_glXQueryExtensionsString(name) const char *name(Display *dpy, int screen)
18894typedef FPL__FUNC_GLX_glXQueryExtensionsString(fpl__func_glx_glXQueryExtensionsString);
18895
18896// Modern GLX
18897#define FPL__FUNC_GLX_glXCreateContextAttribsARB(name) GLXContext name(Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list)
18898typedef FPL__FUNC_GLX_glXCreateContextAttribsARB(fpl__func_glx_glXCreateContextAttribsARB);
18899
18900#define FPL__GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091
18901#define FPL__GLX_CONTEXT_MINOR_VERSION_ARB 0x2092
18902#define FPL__GLX_CONTEXT_FLAGS_ARB 0x2094
18903#define FPL__GLX_CONTEXT_PROFILE_MASK_ARB 0x9126
18904
18905#define FPL__GLX_CONTEXT_DEBUG_BIT_ARB 0x0001
18906#define FPL__GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
18907#define FPL__GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
18908#define FPL__GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
18909
18910typedef struct fpl__X11VideoOpenGLApi {
18911 void *libHandle;
18912 fpl__func_glx_glXQueryVersion *glXQueryVersion;
18913 fpl__func_glx_glXChooseVisual *glXChooseVisual;
18914 fpl__func_glx_glXCreateContext *glXCreateContext;
18915 fpl__func_glx_glXDestroyContext *glXDestroyContext;
18916 fpl__func_glx_glXCreateNewContext *glXCreateNewContext;
18917 fpl__func_glx_glXMakeCurrent *glXMakeCurrent;
18918 fpl__func_glx_glXSwapBuffers *glXSwapBuffers;
18919 fpl__func_glx_glXGetProcAddress *glXGetProcAddress;
18920 fpl__func_glx_glXChooseFBConfig *glXChooseFBConfig;
18921 fpl__func_glx_glXGetFBConfigs *glXGetFBConfigs;
18922 fpl__func_glx_glXGetVisualFromFBConfig *glXGetVisualFromFBConfig;
18923 fpl__func_glx_glXGetFBConfigAttrib *glXGetFBConfigAttrib;
18924 fpl__func_glx_glXCreateWindow *glXCreateWindow;
18925 fpl__func_glx_glXQueryExtension *glXQueryExtension;
18926 fpl__func_glx_glXQueryExtensionsString *glXQueryExtensionsString;
18927 fpl__func_glx_glXCreateContextAttribsARB *glXCreateContextAttribsARB;
18928} fpl__X11VideoOpenGLApi;
18929
18930fpl_internal void fpl__UnloadX11OpenGLApi(fpl__X11VideoOpenGLApi *api) {
18931 if (api->libHandle != fpl_null) {
18932 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Unload Api (Library '%p')", api->libHandle);
18933 dlclose(api->libHandle);
18934 }
18935 fplClearStruct(api);
18936}
18937
18938fpl_internal bool fpl__LoadX11OpenGLApi(fpl__X11VideoOpenGLApi *api, const char *libraryName) {
18939 uint32_t libFileCount = 0;
18940
18941 const char *libFileNames[4];
18942 if (fplGetStringLength(libraryName) > 0) {
18943 libFileNames[libFileCount++] = libraryName;
18944 } else {
18945 libFileNames[libFileCount++] = "libGL.so.1";
18946 libFileNames[libFileCount++] = "libGL.so";
18947 }
18948
18949 bool result = false;
18950 for (uint32_t index = 0; index < libFileCount; ++index) {
18951 const char *libName = libFileNames[index];
18952 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Load GLX Api from Library: %s", libName);
18953 do {
18954 void *libHandle = fpl_null;
18955 FPL__POSIX_LOAD_LIBRARY(FPL__MODULE_GLX, libHandle, libName);
18956 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXQueryVersion, glXQueryVersion);
18957 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXChooseVisual, glXChooseVisual);
18958 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXCreateContext, glXCreateContext);
18959 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXDestroyContext, glXDestroyContext);
18960 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXCreateNewContext, glXCreateNewContext);
18961 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXMakeCurrent, glXMakeCurrent);
18962 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXSwapBuffers, glXSwapBuffers);
18963 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXGetProcAddress, glXGetProcAddress);
18964 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXChooseFBConfig, glXChooseFBConfig);
18965 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXGetFBConfigs, glXGetFBConfigs);
18966 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXGetVisualFromFBConfig, glXGetVisualFromFBConfig);
18967 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXGetFBConfigAttrib, glXGetFBConfigAttrib);
18968 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXCreateWindow, glXCreateWindow);
18969 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXQueryExtension, glXQueryExtension);
18970 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_GLX, libHandle, libName, api, fpl__func_glx_glXQueryExtensionsString, glXQueryExtensionsString);
18971 api->libHandle = libHandle;
18972 result = true;
18973 } while (0);
18974 if (result) {
18975 FPL_LOG_DEBUG(FPL__MODULE_GLX, , "Successfully loaded GLX Api from Library '%s'", libName);
18976 break;
18977 }
18978 fpl__UnloadX11OpenGLApi(api);
18979 }
18980 return (result);
18981}
18982
18983typedef struct fpl__VideoBackendX11OpenGL {
18984 fpl__VideoBackend base;
18985 fpl__X11VideoOpenGLApi api;
18986 GLXFBConfig fbConfig;
18987 XVisualInfo *visualInfo;
18988 GLXContext context;
18989 bool isActiveContext;
18990} fpl__VideoBackendX11OpenGL;
18991
18992fpl_internal FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(fpl__VideoBackend_X11OpenGL_GetProcedure) {
18993 const fpl__VideoBackendX11OpenGL *nativeBackend = (const fpl__VideoBackendX11OpenGL *)backend;
18994 void *result = dlsym(nativeBackend->api.libHandle, procName);
18995 return(result);
18996}
18997
18998fpl_internal FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(fpl__VideoBackend_X11OpenGL_PrepareWindow) {
18999 const fpl__X11SubplatformState *nativeAppState = &appState->x11;
19000 const fpl__X11Api *x11Api = &nativeAppState->api;
19001
19002 fpl__X11WindowState *nativeWindowState = &windowState->x11;
19003 fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
19004 fpl__X11VideoOpenGLApi *glApi = &nativeBackend->api;
19005
19006 Display *display = nativeWindowState->display;
19007 Window window = nativeWindowState->window;
19008 int screen = nativeWindowState->screen;
19009
19010 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Query GLX version for display '%p'", display);
19011 int major = 0, minor = 0;
19012 if (!glApi->glXQueryVersion(display, &major, &minor)) {
19013 FPL_LOG_ERROR(FPL__MODULE_GLX, "Failed querying GLX version for display '%p'", display);
19014 return false;
19015 }
19016 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Successfully queried GLX version for display '%p': %d.%d", display, major, minor);
19017
19018 // @NOTE(final): Required for AMD Drivers?
19019
19020 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Query OpenGL extension on display '%p'", display);
19021 if (!glApi->glXQueryExtension(display, fpl_null, fpl_null)) {
19022 FPL__ERROR(FPL__MODULE_GLX, "OpenGL GLX Extension is not supported by the active display '%p'", display);
19023 return false;
19024 }
19025
19026 const char *extensionString = glApi->glXQueryExtensionsString(display, screen);
19027 if (extensionString != fpl_null) {
19028 FPL_LOG_DEBUG(FPL__MODULE_GLX, "OpenGL GLX extensions: %s", extensionString);
19029 }
19030
19031 bool isModern = major > 1 || (major == 1 && minor >= 3);
19032
19033 int attr[32] = fplZeroInit;
19034 int attrIndex = 0;
19035
19036 attr[attrIndex++] = GLX_X_VISUAL_TYPE;
19037 attr[attrIndex++] = GLX_TRUE_COLOR;
19038
19039 if (!isModern) {
19040 attr[attrIndex++] = GLX_RGBA;
19041 attr[attrIndex++] = True;
19042 }
19043
19044 attr[attrIndex++] = GLX_DOUBLEBUFFER;
19045 attr[attrIndex++] = True;
19046
19047 attr[attrIndex++] = GLX_RED_SIZE;
19048 attr[attrIndex++] = 8;
19049
19050 attr[attrIndex++] = GLX_GREEN_SIZE;
19051 attr[attrIndex++] = 8;
19052
19053 attr[attrIndex++] = GLX_BLUE_SIZE;
19054 attr[attrIndex++] = 8;
19055
19056 attr[attrIndex++] = GLX_ALPHA_SIZE;
19057 attr[attrIndex++] = 8;
19058
19059 attr[attrIndex++] = GLX_DEPTH_SIZE;
19060 attr[attrIndex++] = 24;
19061
19062 attr[attrIndex++] = GLX_STENCIL_SIZE;
19063 attr[attrIndex++] = 8;
19064
19065 if (videoSettings->graphics.opengl.multiSamplingCount > 0) {
19066 attr[attrIndex++] = GLX_SAMPLE_BUFFERS;
19067 attr[attrIndex++] = 1;
19068
19069 attr[attrIndex++] = GLX_SAMPLES;
19070 attr[attrIndex++] = (int)videoSettings->graphics.opengl.multiSamplingCount;
19071 }
19072
19073 attr[attrIndex] = 0;
19074
19075 if (isModern) {
19076 // Use frame buffer config approach (GLX >= 1.3)
19077 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Get framebuffer configuration from display '%p' and screen '%d'", display, screen);
19078 int configCount = 0;
19079 GLXFBConfig *configs = glApi->glXChooseFBConfig(display, screen, attr, &configCount);
19080 if (configs == fpl_null || !configCount) {
19081 FPL__ERROR(FPL__MODULE_GLX, "No framebuffer configuration from display '%p' and screen '%d' found!", display, screen);
19082 nativeBackend->fbConfig = fpl_null;
19083 return false;
19084 }
19085 nativeBackend->fbConfig = configs[0];
19086 nativeBackend->visualInfo = fpl_null;
19087 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Successfully got framebuffer configuration from display '%p' and screen '%d': %p", display, screen, nativeBackend->fbConfig);
19088
19089 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Release %d framebuffer configurations", configCount);
19090 x11Api->XFree(configs);
19091 } else {
19092 // Use choose visual (Old way)
19093 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Choose visual from display '%p' and screen '%d'", display, screen);
19094 XVisualInfo *visualInfo = glApi->glXChooseVisual(display, screen, attr);
19095 if (visualInfo == fpl_null) {
19096 FPL__ERROR(FPL__MODULE_GLX, "No visual info for display '%p' and screen '%d' found!", display, screen);
19097 return false;
19098 }
19099 nativeBackend->visualInfo = visualInfo;
19100 nativeBackend->fbConfig = fpl_null;
19101 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Successfully got visual info from display '%p' and screen '%d': %p", display, screen, nativeBackend->visualInfo);
19102 }
19103
19104 if (nativeBackend->fbConfig != fpl_null) {
19105 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Get visual info from display '%p' and frame buffer config '%p'", display, nativeBackend->fbConfig);
19106 XVisualInfo *visualInfo = glApi->glXGetVisualFromFBConfig(display, nativeBackend->fbConfig);
19107 if (visualInfo == fpl_null) {
19108 FPL__ERROR(FPL__MODULE_GLX, "Failed getting visual info from display '%p' and frame buffer config '%p'", display, nativeBackend->fbConfig);
19109 return false;
19110 }
19111 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Successfully got visual info from display '%p' and frame buffer config '%p': %p", display, nativeBackend->fbConfig, visualInfo);
19112
19113 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Using visual: %p", visualInfo->visual);
19114 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Using color depth: %d", visualInfo->depth);
19115
19116 nativeWindowState->visual = visualInfo->visual;
19117 nativeWindowState->colorDepth = visualInfo->depth;
19118
19119 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Release visual info '%p'", visualInfo);
19120 x11Api->XFree(visualInfo);
19121 } else if (nativeBackend->visualInfo != fpl_null) {
19122 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Using existing visual info: %p", nativeBackend->visualInfo);
19123 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Using visual: %p", nativeBackend->visualInfo->visual);
19124 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Using color depth: %d", nativeBackend->visualInfo->depth);
19125 nativeWindowState->visual = nativeBackend->visualInfo->visual;
19126 nativeWindowState->colorDepth = nativeBackend->visualInfo->depth;
19127 } else {
19128 FPL__ERROR(FPL__MODULE_GLX, "No visual info or frame buffer config defined!");
19129 return false;
19130 }
19131
19132 return true;
19133}
19134
19135fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_X11OpenGL_Shutdown) {
19136 const fpl__X11SubplatformState *nativeAppState = &appState->x11;
19137 const fpl__X11Api *x11Api = &nativeAppState->api;
19138 const fpl__X11WindowState *nativeWindowState = &windowState->x11;
19139
19140 fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
19141 fpl__X11VideoOpenGLApi *glApi = &nativeBackend->api;
19142
19143 if (nativeBackend->isActiveContext) {
19144 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Deactivate GLX rendering context for display '%p'", nativeWindowState->display);
19145 glApi->glXMakeCurrent(nativeWindowState->display, 0, fpl_null);
19146 nativeBackend->isActiveContext = false;
19147 }
19148
19149 if (nativeBackend->context != fpl_null) {
19150 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Destroy GLX rendering context '%p' for display '%p'", nativeBackend->context, nativeWindowState->display);
19151 glApi->glXDestroyContext(nativeWindowState->display, nativeBackend->context);
19152 nativeBackend->context = fpl_null;
19153 }
19154
19155 if (nativeBackend->visualInfo != fpl_null) {
19156 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Destroy visual info '%p' (Fallback)", nativeBackend->visualInfo);
19157 x11Api->XFree(nativeBackend->visualInfo);
19158 nativeBackend->visualInfo = fpl_null;
19159 }
19160}
19161
19162fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_X11OpenGL_Initialize) {
19163 const fpl__X11SubplatformState *nativeAppState = &appState->x11;
19164 const fpl__X11Api *x11Api = &nativeAppState->api;
19165 const fpl__X11WindowState *nativeWindowState = &windowState->x11;
19166
19167 fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
19168 fpl__X11VideoOpenGLApi *glApi = &nativeBackend->api;
19169
19170 Display *display = nativeWindowState->display;
19171 Window window = nativeWindowState->window;
19172
19173 //
19174 // Create legacy context
19175 //
19176 GLXContext legacyRenderingContext;
19177 if (nativeBackend->fbConfig != fpl_null) {
19178 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Create GLX legacy rendering context on display '%p' and frame buffer config '%p'", display, nativeBackend->fbConfig);
19179 legacyRenderingContext = glApi->glXCreateNewContext(display, nativeBackend->fbConfig, GLX_RGBA_TYPE, fpl_null, 1);
19180 if (!legacyRenderingContext) {
19181 FPL__ERROR(FPL__MODULE_GLX, "Failed creating GLX legacy rendering context on display '%p' and frame buffer config '%p'", display, nativeBackend->fbConfig);
19182 goto failed_x11_glx;
19183 }
19184 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Successfully created GLX legacy rendering context '%p' on display '%p' and frame buffer config '%p'", legacyRenderingContext, display, nativeBackend->fbConfig);
19185 } else if (nativeBackend->visualInfo != fpl_null) {
19186 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Create GLX legacy rendering context on display '%p' and visual info '%p'", display, nativeBackend->visualInfo);
19187 legacyRenderingContext = glApi->glXCreateContext(display, nativeBackend->visualInfo, fpl_null, 1);
19188 if (!legacyRenderingContext) {
19189 FPL__ERROR(FPL__MODULE_GLX, "Failed creating GLX legacy rendering context on display '%p' and visual info '%p'", display, nativeBackend->visualInfo);
19190 goto failed_x11_glx;
19191 }
19192 } else {
19193 FPL__ERROR(FPL__MODULE_GLX, "No visual info or frame buffer config defined!");
19194 goto failed_x11_glx;
19195 }
19196
19197 //
19198 // Activate legacy context
19199 //
19200 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Activate GLX legacy rendering context '%p' on display '%p' and window '%d'", legacyRenderingContext, display, (int)window);
19201 if (!glApi->glXMakeCurrent(display, window, legacyRenderingContext)) {
19202 FPL__ERROR(FPL__MODULE_GLX, "Failed activating GLX legacy rendering context '%p' on display '%p' and window '%d'", legacyRenderingContext, display, (int)window);
19203 goto failed_x11_glx;
19204 } else {
19205 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Successfully activated GLX legacy rendering context '%p' on display '%p' and window '%d'", legacyRenderingContext, display, (int)window);
19206 }
19207
19208 //
19209 // Load extensions
19210 //
19211 glApi->glXCreateContextAttribsARB = (fpl__func_glx_glXCreateContextAttribsARB *)glApi->glXGetProcAddress((const GLubyte *)"glXCreateContextAttribsARB");
19212
19213 // Disable legacy rendering context
19214 glApi->glXMakeCurrent(display, 0, fpl_null);
19215
19216 GLXContext activeRenderingContext;
19217
19218 if ((videoSettings->graphics.opengl.compabilityFlags != fplOpenGLCompabilityFlags_Legacy) && (nativeBackend->fbConfig != fpl_null)) {
19219 // @NOTE(final): This is only available in OpenGL 3.0+
19220 if (!(videoSettings->graphics.opengl.majorVersion >= 3 && videoSettings->graphics.opengl.minorVersion >= 0)) {
19221 FPL__ERROR(FPL__MODULE_GLX, "You have not specified the 'majorVersion' and 'minorVersion' in the VideoSettings");
19222 goto failed_x11_glx;
19223 }
19224
19225 if (glApi->glXCreateContextAttribsARB == fpl_null) {
19226 FPL__ERROR(FPL__MODULE_GLX, "glXCreateContextAttribsARB is not available, modern OpenGL is not available for your video card");
19227 goto failed_x11_glx;
19228 }
19229
19230 int flags = 0;
19231 int profile = 0;
19233 profile = FPL__GLX_CONTEXT_CORE_PROFILE_BIT_ARB;
19235 profile = FPL__GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
19236 } else {
19237 FPL__ERROR(FPL__MODULE_GLX, "No opengl compability profile selected, please specific Core OpenGLCompabilityFlags_Core or OpenGLCompabilityFlags_Compability");
19238 goto failed_x11_glx;
19239 }
19241 flags = FPL__GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
19242 }
19243
19244 int contextAttribIndex = 0;
19245 int contextAttribList[32] = fplZeroInit;
19246 contextAttribList[contextAttribIndex++] = FPL__GLX_CONTEXT_MAJOR_VERSION_ARB;
19247 contextAttribList[contextAttribIndex++] = videoSettings->graphics.opengl.majorVersion;
19248 contextAttribList[contextAttribIndex++] = FPL__GLX_CONTEXT_MINOR_VERSION_ARB;
19249 contextAttribList[contextAttribIndex++] = videoSettings->graphics.opengl.minorVersion;
19250 contextAttribList[contextAttribIndex++] = FPL__GLX_CONTEXT_PROFILE_MASK_ARB;
19251 contextAttribList[contextAttribIndex++] = profile;
19252 if (flags > 0) {
19253 contextAttribList[contextAttribIndex++] = FPL__GLX_CONTEXT_FLAGS_ARB;
19254 contextAttribList[contextAttribIndex++] = flags;
19255 }
19256 contextAttribList[contextAttribIndex] = 0;
19257
19258 GLXContext modernRenderingContext = glApi->glXCreateContextAttribsARB(display, nativeBackend->fbConfig, fpl_null, True, contextAttribList);
19259 if (!modernRenderingContext) {
19260 FPL__ERROR(FPL__MODULE_GLX, "Warning: Failed creating Modern OpenGL Rendering Context for version (%d.%d) and compability flags (%d) -> Fallback to legacy context", videoSettings->graphics.opengl.majorVersion, videoSettings->graphics.opengl.minorVersion, videoSettings->graphics.opengl.compabilityFlags);
19261
19262 // Fallback to legacy rendering context
19263 glApi->glXMakeCurrent(display, window, legacyRenderingContext);
19264 activeRenderingContext = legacyRenderingContext;
19265 } else {
19266 if (!glApi->glXMakeCurrent(display, window, modernRenderingContext)) {
19267 FPL__ERROR(FPL__MODULE_GLX, "Warning: Failed activating Modern OpenGL Rendering Context for version (%d.%d) and compability flags (%d) -> Fallback to legacy context", videoSettings->graphics.opengl.majorVersion, videoSettings->graphics.opengl.minorVersion, videoSettings->graphics.opengl.compabilityFlags);
19268
19269 // Destroy modern rendering context
19270 glApi->glXDestroyContext(display, modernRenderingContext);
19271
19272 // Fallback to legacy rendering context
19273 glApi->glXMakeCurrent(display, window, legacyRenderingContext);
19274 activeRenderingContext = legacyRenderingContext;
19275 } else {
19276 // Destroy legacy rendering context
19277 glApi->glXDestroyContext(display, legacyRenderingContext);
19278 legacyRenderingContext = fpl_null;
19279 activeRenderingContext = modernRenderingContext;
19280 }
19281 }
19282 } else {
19283 // Caller wants legacy context
19284 glApi->glXMakeCurrent(display, window, legacyRenderingContext);
19285 activeRenderingContext = legacyRenderingContext;
19286 }
19287
19288 bool result;
19289
19290 fplAssert(activeRenderingContext != fpl_null);
19291 nativeBackend->context = activeRenderingContext;
19292 nativeBackend->isActiveContext = true;
19293
19294 backend->surface.window.x11.display = display;
19295 backend->surface.window.x11.window = window;
19296 backend->surface.window.x11.visual = nativeWindowState->visual;
19297 backend->surface.window.x11.screen = nativeWindowState->screen;
19298 backend->surface.opengl.renderingContext = (void *)activeRenderingContext;
19299
19300 result = true;
19301
19302 goto done_x11_glx;
19303
19304failed_x11_glx:
19305 result = false;
19306
19307done_x11_glx:
19308 if (nativeBackend->visualInfo != fpl_null) {
19309 // If there is a cached visual info, get rid of it now - regardless of its result
19310 FPL_LOG_DEBUG(FPL__MODULE_GLX, "Destroy visual info '%p'", nativeBackend->visualInfo);
19311 x11Api->XFree(nativeBackend->visualInfo);
19312 nativeBackend->visualInfo = fpl_null;
19313 }
19314
19315 if (!result) {
19316 if (legacyRenderingContext) {
19317 glApi->glXDestroyContext(display, legacyRenderingContext);
19318 }
19319 fpl__VideoBackend_X11OpenGL_Shutdown(appState, windowState, backend);
19320 }
19321
19322 return (result);
19323}
19324
19325fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_X11OpenGL_Unload) {
19326 fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
19327 fpl__UnloadX11OpenGLApi(&nativeBackend->api);
19328 fplClearStruct(nativeBackend);
19329}
19330
19331fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_X11OpenGL_Load) {
19332 fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
19333 const fplVideoSettings *videoSettings = &appState->currentSettings.video;
19334 fplClearStruct(nativeBackend);
19335 nativeBackend->base.magic = FPL__VIDEOBACKEND_MAGIC;
19336 if (!fpl__LoadX11OpenGLApi(&nativeBackend->api, videoSettings->graphics.opengl.libraryFile)) {
19337 return(false);
19338 }
19339 return(true);
19340}
19341
19342fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_X11OpenGL_Present) {
19343 const fpl__VideoBackendX11OpenGL *nativeBackend = (fpl__VideoBackendX11OpenGL *)backend;
19344 const fpl__X11WindowState *x11WinState = &appState->window.x11;
19345 const fpl__X11VideoOpenGLApi *glApi = &nativeBackend->api;
19346 glApi->glXSwapBuffers(x11WinState->display, x11WinState->window);
19347}
19348
19349fpl_internal fpl__VideoContext fpl__VideoBackend_X11OpenGL_Construct() {
19350 fpl__VideoContext result = fpl__StubVideoContext();
19351 result.loadFunc = fpl__VideoBackend_X11OpenGL_Load;
19352 result.unloadFunc = fpl__VideoBackend_X11OpenGL_Unload;
19353 result.getProcedureFunc = fpl__VideoBackend_X11OpenGL_GetProcedure;
19354 result.initializeFunc = fpl__VideoBackend_X11OpenGL_Initialize;
19355 result.shutdownFunc = fpl__VideoBackend_X11OpenGL_Shutdown;
19356 result.prepareWindowFunc = fpl__VideoBackend_X11OpenGL_PrepareWindow;
19357 result.presentFunc = fpl__VideoBackend_X11OpenGL_Present;
19358 return(result);
19359}
19360#endif // FPL__ENABLE_VIDEO_OPENGL && FPL_SUBPLATFORM_X11
19361
19362// ############################################################################
19363//
19364// > VIDEO_BACKEND_SOFTWARE_X11
19365//
19366// ############################################################################
19367#if defined(FPL__ENABLE_VIDEO_SOFTWARE) && defined(FPL_SUBPLATFORM_X11)
19368typedef struct fpl__VideoBackendX11Software {
19369 fpl__VideoBackend base;
19370 GC graphicsContext;
19371 XImage *buffer;
19372} fpl__VideoBackendX11Software;
19373
19374fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_X11Software_Shutdown) {
19375 const fpl__X11SubplatformState *nativeAppState = &appState->x11;
19376 const fpl__X11Api *x11Api = &nativeAppState->api;
19377 const fpl__X11WindowState *nativeWindowState = &windowState->x11;
19378
19379 fpl__VideoBackendX11Software *nativeBackend = (fpl__VideoBackendX11Software *)backend;
19380
19381 if (nativeBackend->buffer != fpl_null) {
19382 // @NOTE(final): Dont use XDestroyImage here, as it points to the backbuffer memory directly - which is released later
19383 nativeBackend->buffer = fpl_null;
19384 }
19385
19386 if (nativeBackend->graphicsContext != fpl_null) {
19387 nativeBackend->graphicsContext = fpl_null;
19388 }
19389}
19390
19391fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_X11Software_Initialize) {
19392 const fpl__X11SubplatformState *nativeAppState = &appState->x11;
19393 const fpl__X11Api *x11Api = &nativeAppState->api;
19394 const fpl__X11WindowState *nativeWindowState = &windowState->x11;
19395
19396 fpl__VideoBackendX11Software *nativeBackend = (fpl__VideoBackendX11Software *)backend;
19397
19398 const fplVideoBackBuffer *backbuffer = &data->backbuffer;
19399
19400 // Based on: https://bbs.archlinux.org/viewtopic.php?id=225741
19401 nativeBackend->graphicsContext = x11Api->XCreateGC(nativeWindowState->display, nativeWindowState->window, 0, 0);
19402 if (nativeBackend->graphicsContext == fpl_null) {
19403 return false;
19404 }
19405
19406 nativeBackend->buffer = x11Api->XCreateImage(nativeWindowState->display, nativeWindowState->visual, 24, ZPixmap, 0, (char *)backbuffer->pixels, backbuffer->width, backbuffer->height, 32, (int)backbuffer->lineWidth);
19407 if (nativeBackend->buffer == fpl_null) {
19408 fpl__VideoBackend_X11Software_Shutdown(appState, windowState, backend);
19409 return false;
19410 }
19411
19412 // Initial draw pixels to the window
19413 x11Api->XPutImage(nativeWindowState->display, nativeWindowState->window, nativeBackend->graphicsContext, nativeBackend->buffer, 0, 0, 0, 0, backbuffer->width, backbuffer->height);
19414 x11Api->XSync(nativeWindowState->display, False);
19415
19416 backend->surface.window.x11.display = nativeWindowState->display;
19417 backend->surface.window.x11.window = nativeWindowState->window;
19418 backend->surface.window.x11.visual = nativeWindowState->visual;
19419 backend->surface.window.x11.screen = nativeWindowState->screen;
19420
19421 return (true);
19422}
19423
19424fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_X11Software_Load) {
19425 fpl__VideoBackendX11Software *nativeBackend = (fpl__VideoBackendX11Software *)backend;
19426 fplClearStruct(nativeBackend);
19427 nativeBackend->base.magic = FPL__VIDEOBACKEND_MAGIC;
19428 return(true);
19429}
19430
19431fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_X11Software_Unload) {
19432 fpl__VideoBackendX11Software *nativeBackend = (fpl__VideoBackendX11Software *)backend;
19433 fplClearStruct(nativeBackend);
19434}
19435
19436fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_X11Software_Present) {
19437 const fpl__VideoBackendX11Software *nativeBackend = (fpl__VideoBackendX11Software *)backend;
19438 const fpl__X11WindowState *x11WinState = &appState->window.x11;
19439 const fpl__X11Api *x11Api = &appState->x11.api;
19440 const fplVideoBackBuffer *backbuffer = &data->backbuffer;
19441 x11Api->XPutImage(x11WinState->display, x11WinState->window, nativeBackend->graphicsContext, nativeBackend->buffer, 0, 0, 0, 0, backbuffer->width, backbuffer->height);
19442 x11Api->XSync(x11WinState->display, False);
19443}
19444
19445fpl_internal fpl__VideoContext fpl__VideoBackend_X11Software_Construct() {
19446 fpl__VideoContext result = fpl__StubVideoContext();
19447 result.loadFunc = fpl__VideoBackend_X11Software_Load;
19448 result.unloadFunc = fpl__VideoBackend_X11Software_Unload;
19449 result.initializeFunc = fpl__VideoBackend_X11Software_Initialize;
19450 result.shutdownFunc = fpl__VideoBackend_X11Software_Shutdown;
19451 result.presentFunc = fpl__VideoBackend_X11Software_Present;
19452 result.recreateOnResize = true;
19453 return(result);
19454}
19455#endif // FPL__ENABLE_VIDEO_SOFTWARE && FPL_SUBPLATFORM_X11
19456
19457// ############################################################################
19458//
19459// > VIDEO_BACKEND_SOFTWARE_WIN32
19460//
19461// ############################################################################
19462#if defined(FPL__ENABLE_VIDEO_SOFTWARE) && defined(FPL_PLATFORM_WINDOWS)
19463typedef struct fpl__VideoBackendWin32Software {
19464 fpl__VideoBackend base;
19465 BITMAPINFO bitmapInfo;
19466} fpl__VideoBackendWin32Software;
19467
19468fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_Win32Software_Shutdown) {
19469 fpl__VideoBackendWin32Software *nativeBackend = (fpl__VideoBackendWin32Software *)backend;
19470 BITMAPINFO *bitmapInfo = &nativeBackend->bitmapInfo;
19471 fplClearStruct(bitmapInfo);
19472}
19473
19474fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_Win32Software_Initialize) {
19475 fpl__VideoBackendWin32Software *nativeBackend = (fpl__VideoBackendWin32Software *)backend;
19476 const fplVideoBackBuffer *backbuffer = &data->backbuffer;
19477 BITMAPINFO *bitmapInfo = &nativeBackend->bitmapInfo;
19478 fplClearStruct(bitmapInfo);
19479 bitmapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
19480 bitmapInfo->bmiHeader.biWidth = (LONG)backbuffer->width;
19481 // @NOTE(final): Top-down bitmap requires a negative height
19482 bitmapInfo->bmiHeader.biHeight = -(LONG)backbuffer->height;
19483 bitmapInfo->bmiHeader.biBitCount = 32;
19484 bitmapInfo->bmiHeader.biCompression = BI_RGB;
19485 bitmapInfo->bmiHeader.biPlanes = 1;
19486 bitmapInfo->bmiHeader.biSizeImage = (DWORD)(backbuffer->height * backbuffer->lineWidth);
19487 return true;
19488}
19489
19490fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_Win32Software_Unload) {
19491 fpl__VideoBackendWin32Software *nativeBackend = (fpl__VideoBackendWin32Software *)backend;
19492 fplClearStruct(nativeBackend);
19493}
19494
19495fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_Win32Software_Load) {
19496 fpl__VideoBackendWin32Software *nativeBackend = (fpl__VideoBackendWin32Software *)backend;
19497 fplClearStruct(nativeBackend);
19498 nativeBackend->base.magic = FPL__VIDEOBACKEND_MAGIC;
19499 return(true);
19500}
19501
19502fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_Win32Software_Present) {
19503 const fpl__Win32AppState *win32AppState = &appState->win32;
19504 const fpl__Win32WindowState *win32WindowState = &appState->window.win32;
19505 const fpl__Win32Api *wapi = &win32AppState->winApi;
19506 const fpl__VideoBackendWin32Software *nativeBackend = (fpl__VideoBackendWin32Software *)backend;
19507 const fplVideoBackBuffer *backbuffer = &data->backbuffer;
19508 fplWindowSize area;
19509 if (fplGetWindowSize(&area)) {
19510 int32_t targetX = 0;
19511 int32_t targetY = 0;
19512 int32_t targetWidth = area.width;
19513 int32_t targetHeight = area.height;
19514 int32_t sourceWidth = backbuffer->width;
19515 int32_t sourceHeight = backbuffer->height;
19516 if (backbuffer->useOutputRect) {
19517 targetX = backbuffer->outputRect.x;
19518 targetY = backbuffer->outputRect.y;
19519 targetWidth = backbuffer->outputRect.width;
19520 targetHeight = backbuffer->outputRect.height;
19521 wapi->gdi.StretchDIBits(win32WindowState->deviceContext, 0, 0, area.width, area.height, 0, 0, 0, 0, fpl_null, fpl_null, DIB_RGB_COLORS, BLACKNESS);
19522 }
19523 wapi->gdi.StretchDIBits(win32WindowState->deviceContext, targetX, targetY, targetWidth, targetHeight, 0, 0, sourceWidth, sourceHeight, backbuffer->pixels, &nativeBackend->bitmapInfo, DIB_RGB_COLORS, SRCCOPY);
19524 }
19525}
19526
19527fpl_internal fpl__VideoContext fpl__VideoBackend_Win32Software_Construct() {
19528 fpl__VideoContext result = fpl__StubVideoContext();
19529 result.loadFunc = fpl__VideoBackend_Win32Software_Load;
19530 result.unloadFunc = fpl__VideoBackend_Win32Software_Unload;
19531 result.initializeFunc = fpl__VideoBackend_Win32Software_Initialize;
19532 result.shutdownFunc = fpl__VideoBackend_Win32Software_Shutdown;
19533 result.presentFunc = fpl__VideoBackend_Win32Software_Present;
19534 result.recreateOnResize = true;
19535 return(result);
19536}
19537#endif // FPL__ENABLE_VIDEO_SOFTWARE && FPL_PLATFORM_WINDOWS
19538
19539// ############################################################################
19540//
19541// > VIDEO_BACKEND_VULKAN (Win32, X11)
19542//
19543// ############################################################################
19544#if defined(FPL__ENABLE_VIDEO_VULKAN)
19545
19546#if !fplHasInclude(<vulkan/vulkan.h>) || defined(FPL_NO_PLATFORM_INCLUDES)
19547
19548#if defined(FPL_PLATFORM_WINDOWS)
19549# define fpl__VKAPI_CALL __stdcall
19550# define fpl__VKAPI_PTR fpl__VKAPI_CALL
19551# define fpl__VKAPI_ATTR
19552#else
19553# define fpl__VKAPI_CALL
19554# define fpl__VKAPI_PTR fpl__VKAPI_CALL
19555# define fpl__VKAPI_ATTR
19556#endif
19557
19558#define FPL__VK_NULL_HANDLE fpl_null
19559
19560#define FPL__VK_MAX_EXTENSION_NAME_SIZE 256
19561#define FPL__VK_MAX_DESCRIPTION_SIZE 256
19562
19563#define FPL__VK_MAKE_VERSION(major, minor, patch) \
19564 ((((uint32_t)(major)) << 22) | (((uint32_t)(minor)) << 12) | ((uint32_t)(patch)))
19565
19566typedef enum fpl__VkResult {
19567 FPL__VK_ERROR_OUT_OF_HOST_MEMORY = -1,
19568 FPL__VK_ERROR_OUT_OF_DEVICE_MEMORY = -2,
19569 FPL__VK_ERROR_INITIALIZATION_FAILED = -3,
19570 FPL__VK_ERROR_DEVICE_LOST = -4,
19571 FPL__VK_ERROR_MEMORY_MAP_FAILED = -5,
19572 FPL__VK_ERROR_LAYER_NOT_PRESENT = -6,
19573 FPL__VK_ERROR_EXTENSION_NOT_PRESENT = -7,
19574 FPL__VK_ERROR_FEATURE_NOT_PRESENT = -8,
19575 FPL__VK_ERROR_INCOMPATIBLE_DRIVER = -9,
19576 FPL__VK_ERROR_TOO_MANY_OBJECTS = -10,
19577 FPL__VK_ERROR_FORMAT_NOT_SUPPORTED = -11,
19578 FPL__VK_ERROR_FRAGMENTED_POOL = -12,
19579 FPL__VK_ERROR_UNKNOWN = -13,
19580
19581 FPL__VK_SUCCESS = 0,
19582
19583 FPL__VK_NOT_READY = 1,
19584 FPL__VK_TIMEOUT = 2,
19585 FPL__VK_EVENT_SET = 3,
19586 FPL__VK_EVENT_RESET = 4,
19587 FPL__VK_INCOMPLETE = 5,
19588
19589 FPL__VK_RESULT_MAX_ENUM = 0x7FFFFFFF
19590} fpl__VkResult;
19591
19592typedef uint32_t fpl__VkFlags;
19593typedef uint32_t fpl__VkBool32;
19594
19595typedef void fpl__VkAllocationCallbacks;
19596
19597typedef void *fpl__VkInstance;
19598typedef void *fpl__VkSurfaceKHR;
19599typedef void *fpl__VkPhysicalDevice;
19600
19601typedef enum fpl__VkStructureType {
19602 FPL__VK_STRUCTURE_TYPE_APPLICATION_INFO = 0,
19603 FPL__VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO = 1,
19604 FPL__VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000,
19605 FPL__VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000,
19606 FPL__VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT = 1000128002,
19607 FPL__VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT = 1000128004,
19608 FPL__VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
19609} fpl__VkStructureType;
19610
19611typedef struct fpl__VkApplicationInfo {
19612 fpl__VkStructureType sType;
19613 const void *pNext;
19614 const char *pApplicationName;
19615 uint32_t applicationVersion;
19616 const char *pEngineName;
19617 uint32_t engineVersion;
19618 uint32_t apiVersion;
19619} fpl__VkApplicationInfo;
19620
19621typedef fpl__VkFlags fpl__VkInstanceCreateFlags;
19622
19623typedef struct fpl__VkInstanceCreateInfo {
19624 fpl__VkStructureType sType;
19625 const void *pNext;
19626 fpl__VkInstanceCreateFlags flags;
19627 const fpl__VkApplicationInfo *pApplicationInfo;
19628 uint32_t enabledLayerCount;
19629 const char *const *ppEnabledLayerNames;
19630 uint32_t enabledExtensionCount;
19631 const char *const *ppEnabledExtensionNames;
19632} fpl__VkInstanceCreateInfo;
19633
19634typedef struct fpl__VkExtensionProperties {
19635 char extensionName[FPL__VK_MAX_EXTENSION_NAME_SIZE];
19636 uint32_t specVersion;
19637} fpl__VkExtensionProperties;
19638
19639typedef struct fpl__VkLayerProperties {
19640 char layerName[FPL__VK_MAX_EXTENSION_NAME_SIZE];
19641 uint32_t specVersion;
19642 uint32_t implementationVersion;
19643 char description[FPL__VK_MAX_DESCRIPTION_SIZE];
19644} fpl__VkLayerProperties;
19645
19646// Core procs (opaque)
19647typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkCreateInstance)(const fpl__VkInstanceCreateInfo *pCreateInfo, const fpl__VkAllocationCallbacks *pAllocator, fpl__VkInstance *pInstance);
19648typedef void (fpl__VKAPI_PTR *fpl__func_vkDestroyInstance)(fpl__VkInstance instance, const fpl__VkAllocationCallbacks *pAllocator);
19649typedef void *(fpl__VKAPI_PTR *fpl__func_vkGetInstanceProcAddr)(fpl__VkInstance instance, const char *pName);
19650typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkEnumerateInstanceExtensionProperties)(const char *pLayerName, uint32_t *pPropertyCount, fpl__VkExtensionProperties *pProperties);
19651typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkEnumerateInstanceLayerProperties)(uint32_t *pPropertyCount, fpl__VkLayerProperties *pProperties);
19652
19653// Surface KHR procs (opaque)
19654typedef void (fpl__VKAPI_PTR *fpl__func_vkDestroySurfaceKHR)(fpl__VkInstance instance, fpl__VkSurfaceKHR surface, const fpl__VkAllocationCallbacks *pAllocator);
19655
19656#if defined(FPL_PLATFORM_WINDOWS)
19657
19658typedef fpl__VkFlags fpl__VkWin32SurfaceCreateFlagsKHR;
19659typedef struct fpl__VkWin32SurfaceCreateInfoKHR {
19660 fpl__VkStructureType sType;
19661 const void *pNext;
19662 fpl__VkWin32SurfaceCreateFlagsKHR flags;
19663 fpl__Win32InstanceHandle hinstance;
19664 fpl__Win32WindowHandle hwnd;
19665} fpl__VkWin32SurfaceCreateInfoKHR;
19666
19667typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkCreateWin32SurfaceKHR)(fpl__VkInstance instance, const fpl__VkWin32SurfaceCreateInfoKHR *pCreateInfo, const fpl__VkAllocationCallbacks *pAllocator, fpl__VkSurfaceKHR *pSurface);
19668typedef fpl__VkBool32(fpl__VKAPI_PTR *fpl__func_vkGetPhysicalDeviceWin32PresentationSupportKHR)(fpl__VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex);
19669
19670#elif defined(FPL_SUBPLATFORM_X11)
19671
19672typedef fpl__VkFlags fpl__VkXlibSurfaceCreateFlagsKHR;
19673typedef struct fpl__VkXlibSurfaceCreateInfoKHR {
19674 fpl__VkStructureType sType;
19675 const void *pNext;
19676 fpl__VkXlibSurfaceCreateFlagsKHR flags;
19677 fpl__X11Display *dpy;
19678 fpl__X11Window window;
19679} fpl__VkXlibSurfaceCreateInfoKHR;
19680
19681typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkCreateXlibSurfaceKHR)(fpl__VkInstance instance, const fpl__VkXlibSurfaceCreateInfoKHR *pCreateInfo, const fpl__VkAllocationCallbacks *pAllocator, fpl__VkSurfaceKHR *pSurface);
19682typedef fpl__VkBool32(fpl__VKAPI_PTR *fpl__func_vkGetPhysicalDeviceXlibPresentationSupportKHR)(fpl__VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex);
19683
19684#endif
19685
19686typedef void *fpl__VkDebugUtilsMessengerEXT;
19687
19688typedef enum fpl__VkDebugUtilsMessageSeverityFlagBitsEXT {
19689 FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT = 0x00000001,
19690 FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT = 0x00000010,
19691 FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT = 0x00000100,
19692 FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT = 0x00001000,
19693 FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF
19694} fpl__VkDebugUtilsMessageSeverityFlagBitsEXT;
19695typedef fpl__VkFlags fpl__VkDebugUtilsMessageSeverityFlagsEXT;
19696
19697typedef enum fpl__VkDebugUtilsMessageTypeFlagBitsEXT {
19698 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT = 0x00000001,
19699 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT = 0x00000002,
19700 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT = 0x00000004,
19701 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF
19702} fpl__VkDebugUtilsMessageTypeFlagBitsEXT;
19703typedef fpl__VkFlags fpl__VkDebugUtilsMessageTypeFlagsEXT;
19704
19705typedef struct fpl__VkDebugUtilsLabelEXT {
19706 fpl__VkStructureType sType;
19707 const void *pNext;
19708 const char *pLabelName;
19709 float color[4];
19710} fpl__VkDebugUtilsLabelEXT;
19711
19712typedef void fpl__VkDebugUtilsObjectNameInfoEXT;
19713typedef fpl__VkFlags fpl__VkDebugUtilsMessengerCallbackDataFlagsEXT;
19714typedef struct fpl__VkDebugUtilsMessengerCallbackDataEXT {
19715 fpl__VkStructureType sType;
19716 const void *pNext;
19717 fpl__VkDebugUtilsMessengerCallbackDataFlagsEXT flags;
19718 const char *pMessageIdName;
19719 int32_t messageIdNumber;
19720 const char *pMessage;
19721 uint32_t queueLabelCount;
19722 const fpl__VkDebugUtilsLabelEXT *pQueueLabels;
19723 uint32_t cmdBufLabelCount;
19724 const fpl__VkDebugUtilsLabelEXT *pCmdBufLabels;
19725 uint32_t objectCount;
19726 const fpl__VkDebugUtilsObjectNameInfoEXT *pObjects;
19727} fpl__VkDebugUtilsMessengerCallbackDataEXT;
19728
19729typedef fpl__VkFlags fpl__VkDebugUtilsMessengerCreateFlagsEXT;
19730
19731typedef fpl__VkBool32(fpl__VKAPI_PTR *fpl__func_vkDebugUtilsMessengerCallbackEXT) (
19732 fpl__VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
19733 fpl__VkDebugUtilsMessageTypeFlagsEXT messageTypes,
19734 const fpl__VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
19735 void *pUserData);
19736
19737typedef struct fpl__VkDebugUtilsMessengerCreateInfoEXT {
19738 fpl__VkStructureType sType;
19739 const void *pNext;
19740 fpl__VkDebugUtilsMessengerCreateFlagsEXT flags;
19741 fpl__VkDebugUtilsMessageSeverityFlagsEXT messageSeverity;
19742 fpl__VkDebugUtilsMessageTypeFlagsEXT messageType;
19743 fpl__func_vkDebugUtilsMessengerCallbackEXT pfnUserCallback;
19744 void *pUserData;
19745} fpl__VkDebugUtilsMessengerCreateInfoEXT;
19746
19747typedef fpl__VkResult(fpl__VKAPI_PTR *fpl__func_vkCreateDebugUtilsMessengerEXT)(fpl__VkInstance instance, const fpl__VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, const fpl__VkAllocationCallbacks *pAllocator, fpl__VkDebugUtilsMessengerEXT *pMessenger);
19748typedef void (fpl__VKAPI_PTR *fpl__func_vkDestroyDebugUtilsMessengerEXT)(fpl__VkInstance instance, fpl__VkDebugUtilsMessengerEXT messenger, const fpl__VkAllocationCallbacks *pAllocator);
19749
19750#else
19751
19752# if defined(FPL_PLATFORM_WINDOWS)
19753# define VK_USE_PLATFORM_WIN32_KHR
19754# elif defined(FPL_SUBPLATFORM_X11)
19755# define VK_USE_PLATFORM_XLIB_KHR
19756# endif
19757
19758# if !defined(FPL_NO_RUNTIME_LINKING)
19759# define VK_NO_PROTOTYPES
19760# endif
19761# include <vulkan/vulkan.h>
19762
19763# define fpl__VKAPI_CALL VKAPI_CALL
19764# define fpl__VKAPI_PTR VKAPI_PTR
19765# define fpl__VKAPI_ATTR VKAPI_ATTR
19766
19767# define FPL__VK_STRUCTURE_TYPE_APPLICATION_INFO VK_STRUCTURE_TYPE_APPLICATION_INFO
19768# define FPL__VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
19769# define FPL__VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR
19770# define FPL__VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR
19771# define FPL__VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT
19772
19773typedef enum fpl__VkResult {
19774 FPL__VK_ERROR_OUT_OF_HOST_MEMORY = VK_ERROR_OUT_OF_HOST_MEMORY,
19775 FPL__VK_ERROR_OUT_OF_DEVICE_MEMORY = VK_ERROR_OUT_OF_DEVICE_MEMORY,
19776 FPL__VK_ERROR_INITIALIZATION_FAILED = VK_ERROR_INITIALIZATION_FAILED,
19777 FPL__VK_ERROR_DEVICE_LOST = VK_ERROR_DEVICE_LOST,
19778 FPL__VK_ERROR_MEMORY_MAP_FAILED = VK_ERROR_MEMORY_MAP_FAILED,
19779 FPL__VK_ERROR_LAYER_NOT_PRESENT = VK_ERROR_LAYER_NOT_PRESENT,
19780 FPL__VK_ERROR_EXTENSION_NOT_PRESENT = VK_ERROR_EXTENSION_NOT_PRESENT,
19781 FPL__VK_ERROR_FEATURE_NOT_PRESENT = VK_ERROR_FEATURE_NOT_PRESENT,
19782 FPL__VK_ERROR_INCOMPATIBLE_DRIVER = VK_ERROR_INCOMPATIBLE_DRIVER,
19783 FPL__VK_ERROR_TOO_MANY_OBJECTS = VK_ERROR_TOO_MANY_OBJECTS,
19784 FPL__VK_ERROR_FORMAT_NOT_SUPPORTED = VK_ERROR_FORMAT_NOT_SUPPORTED,
19785 FPL__VK_ERROR_FRAGMENTED_POOL = VK_ERROR_FRAGMENTED_POOL,
19786 FPL__VK_ERROR_UNKNOWN = VK_ERROR_UNKNOWN,
19787
19788 FPL__VK_SUCCESS = VK_SUCCESS,
19789
19790 FPL__VK_NOT_READY = VK_NOT_READY,
19791 FPL__VK_TIMEOUT = VK_TIMEOUT,
19792 FPL__VK_EVENT_SET = VK_EVENT_SET,
19793 FPL__VK_EVENT_RESET = VK_EVENT_RESET,
19794 FPL__VK_INCOMPLETE = VK_INCOMPLETE,
19795
19796 FPL__VK_RESULT_MAX_ENUM = VK_RESULT_MAX_ENUM
19797} fpl__VkResult;
19798
19799# define FPL__VK_NULL_HANDLE VK_NULL_HANDLE
19800
19801# define FPL__VK_MAKE_VERSION(major, minor, patch) VK_MAKE_VERSION(major, minor, patch)
19802
19803typedef VkFlags fpl__VkFlags;
19804typedef VkBool32 fpl__VkBool32;
19805
19806typedef VkAllocationCallbacks fpl__VkAllocationCallbacks;
19807
19808typedef VkInstance fpl__VkInstance;
19809typedef VkSurfaceKHR fpl__VkSurfaceKHR;
19810typedef VkPhysicalDevice fpl__VkPhysicalDevice;
19811
19812typedef VkApplicationInfo fpl__VkApplicationInfo;
19813typedef VkInstanceCreateInfo fpl__VkInstanceCreateInfo;
19814
19815typedef VkExtensionProperties fpl__VkExtensionProperties;
19816typedef VkLayerProperties fpl__VkLayerProperties;
19817
19818// Core procs
19819typedef PFN_vkCreateInstance fpl__func_vkCreateInstance;
19820typedef PFN_vkDestroyInstance fpl__func_vkDestroyInstance;
19821typedef PFN_vkGetInstanceProcAddr fpl__func_vkGetInstanceProcAddr;
19822typedef PFN_vkEnumerateInstanceExtensionProperties fpl__func_vkEnumerateInstanceExtensionProperties;
19823typedef PFN_vkEnumerateInstanceLayerProperties fpl__func_vkEnumerateInstanceLayerProperties;
19824
19825// Instance procs
19826typedef PFN_vkDestroySurfaceKHR fpl__func_vkDestroySurfaceKHR;
19827#if defined(FPL_PLATFORM_WINDOWS)
19828typedef VkWin32SurfaceCreateInfoKHR fpl__VkWin32SurfaceCreateInfoKHR;
19829typedef PFN_vkCreateWin32SurfaceKHR fpl__func_vkCreateWin32SurfaceKHR;
19830typedef PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR fpl__func_vkGetPhysicalDeviceWin32PresentationSupportKHR;
19831#elif defined(FPL_SUBPLATFORM_X11)
19832typedef VkXlibSurfaceCreateInfoKHR fpl__VkXlibSurfaceCreateInfoKHR;
19833typedef PFN_vkCreateXlibSurfaceKHR fpl__func_vkCreateXlibSurfaceKHR;
19834typedef PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR fpl__func_vkGetPhysicalDeviceXlibPresentationSupportKHR;
19835#endif
19836
19837typedef VkDebugUtilsMessageSeverityFlagBitsEXT fpl__VkDebugUtilsMessageSeverityFlagBitsEXT;
19838typedef VkDebugUtilsMessageSeverityFlagsEXT fpl__VkDebugUtilsMessageSeverityFlagsEXT;
19839# define FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT
19840# define FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT
19841# define FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
19842# define FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT
19843# define FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT
19844typedef VkDebugUtilsMessageTypeFlagBitsEXT fpl__VkDebugUtilsMessageTypeFlagBitsEXT;
19845typedef VkDebugUtilsMessageTypeFlagsEXT fpl__VkDebugUtilsMessageTypeFlagsEXT;
19846# define FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
19847# define FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT
19848# define FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT
19849# define FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT
19850typedef VkDebugUtilsMessengerCallbackDataEXT fpl__VkDebugUtilsMessengerCallbackDataEXT;
19851typedef VkDebugUtilsMessengerEXT fpl__VkDebugUtilsMessengerEXT;
19852typedef PFN_vkDebugUtilsMessengerCallbackEXT fpl__func_vkDebugUtilsMessengerCallbackEXT;
19853typedef VkDebugUtilsMessengerCreateInfoEXT fpl__VkDebugUtilsMessengerCreateInfoEXT;
19854
19855typedef PFN_vkCreateDebugUtilsMessengerEXT fpl__func_vkCreateDebugUtilsMessengerEXT;
19856typedef PFN_vkDestroyDebugUtilsMessengerEXT fpl__func_vkDestroyDebugUtilsMessengerEXT;
19857
19858#endif // !fplHasInclude(<vulkan/vulkan.h>) || defined(FPL_NO_PLATFORM_INCLUDES)
19859
19860typedef struct fpl__VulkanApi {
19861 fplDynamicLibraryHandle libraryHandle;
19862 fpl__func_vkCreateInstance vkCreateInstance;
19863 fpl__func_vkDestroyInstance vkDestroyInstance;
19864 fpl__func_vkGetInstanceProcAddr vkGetInstanceProcAddr;
19865 fpl__func_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties;
19866 fpl__func_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties;
19867} fpl__VulkanApi;
19868
19869fpl_internal void fpl__UnloadVulkanApi(fpl__VulkanApi *api) {
19870 if (api->libraryHandle.isValid) {
19871 fplDynamicLibraryUnload(&api->libraryHandle);
19872 }
19873 fplClearStruct(api);
19874}
19875
19876fpl_internal bool fpl__LoadVulkanApi(fpl__VulkanApi *api, const char *libraryName) {
19877
19878 fplClearStruct(api);
19879
19880#if defined(FPL_NO_RUNTIME_LINKING)
19881 api->vkCreateInstance = vkCreateInstance;
19882 api->vkDestroyInstance = vkDestroyInstance;
19883 api->vkGetInstanceProcAddr = vkGetInstanceProcAddr;
19884 api->vkEnumerateInstanceExtensionProperties = vkEnumerateInstanceExtensionProperties;
19885 api->vkEnumerateInstanceLayerProperties = vkEnumerateInstanceLayerProperties;
19886 return(true);
19887#endif
19888
19889 uint32_t libraryCount = 0;
19890
19891 const char *libraryNames[4];
19892 if (fplGetStringLength(libraryName) > 0) {
19893 libraryNames[libraryCount++] = libraryName;
19894 } else {
19895 // Automatic detection of vulkan library
19896#if defined(FPL_PLATFORM_WINDOWS)
19897 libraryNames[libraryCount++] = "vulkan-1.dll";
19898#elif defined(FPL_SUBPLATFORM_POSIX)
19899 libraryNames[libraryCount++] = "libvulkan.so";
19900 libraryNames[libraryCount++] = "libvulkan.so.1";
19901#else
19902 FPL__WARNING(FPL__MODULE_VIDEO_VULKAN, "Unsupported Platform!");
19903 return(false);
19904#endif
19905 }
19906
19907#define FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libName, target, type, name) \
19908 (target)->name = (type)fplGetDynamicLibraryProc(&libHandle, #name); \
19909 if ((target)->name == fpl_null) { \
19910 FPL__WARNING(FPL__MODULE_VIDEO_VULKAN, "Failed getting procedure address '%s' from library '%s'", #name, libName); \
19911 continue; \
19912 }
19913
19914 bool result = false;
19915 for (uint32_t i = 0; i < libraryCount; ++i) {
19916 const char *libraryName = libraryNames[i];
19917
19918 if (api->libraryHandle.isValid) {
19919 fplDynamicLibraryUnload(&api->libraryHandle);
19920 }
19921 fplClearStruct(api);
19922
19924 if (!fplDynamicLibraryLoad(libraryName, &libHandle)) {
19925 continue;
19926 }
19927 api->libraryHandle = libHandle;
19928
19929 FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libraryName, api, fpl__func_vkCreateInstance, vkCreateInstance);
19930 FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libraryName, api, fpl__func_vkDestroyInstance, vkDestroyInstance);
19931 FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libraryName, api, fpl__func_vkGetInstanceProcAddr, vkGetInstanceProcAddr);
19932 FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libraryName, api, fpl__func_vkEnumerateInstanceExtensionProperties, vkEnumerateInstanceExtensionProperties);
19933 FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE(libHandle, libraryName, api, fpl__func_vkEnumerateInstanceLayerProperties, vkEnumerateInstanceLayerProperties);
19934
19935 result = true;
19936 break;
19937 }
19938
19939 if (!result) {
19940 fpl__UnloadVulkanApi(api);
19941 }
19942
19943#undef FPL__VULKAN_GET_FUNCTION_ADDRESS_CONTINUE
19944
19945 return(result);
19946}
19947
19948typedef struct fpl__VulkanDebugMessengerUserData {
19950 fplVulkanValidationLayerMode validationMode;
19951 void *userData;
19952} fpl__VulkanDebugMessengerUserData;
19953
19954typedef struct fpl__VideoBackendVulkan {
19955 fpl__VideoBackend base;
19956 fpl__VulkanApi api;
19957 fpl__VkInstance instanceHandle;
19958 fpl__VkSurfaceKHR surfaceHandle;
19959 fpl__VkDebugUtilsMessengerEXT debugMessenger;
19960 fpl__VulkanDebugMessengerUserData debugMessengerUserData;
19961 const fpl__VkAllocationCallbacks *allocator;
19962 fpl_b32 isInstanceUserDefined;
19963} fpl__VideoBackendVulkan;
19964
19965fpl_internal const char *fpl__GetVulkanResultString(const fpl__VkResult result) {
19966 switch (result) {
19967 case FPL__VK_ERROR_OUT_OF_HOST_MEMORY:
19968 return "Out of Host-Memory";
19969 case FPL__VK_ERROR_OUT_OF_DEVICE_MEMORY:
19970 return "Out of Device-Memory";
19971 case FPL__VK_ERROR_INITIALIZATION_FAILED:
19972 return "Initialization failed";
19973 case FPL__VK_ERROR_DEVICE_LOST:
19974 return "Device lost";
19975 case FPL__VK_ERROR_MEMORY_MAP_FAILED:
19976 return "Memory map failed";
19977 case FPL__VK_ERROR_LAYER_NOT_PRESENT:
19978 return "Layer not present";
19979 case FPL__VK_ERROR_EXTENSION_NOT_PRESENT:
19980 return "Extension not present";
19981 case FPL__VK_ERROR_FEATURE_NOT_PRESENT:
19982 return "Feature not present";
19983 case FPL__VK_ERROR_INCOMPATIBLE_DRIVER:
19984 return "Incompatible driver";
19985 case FPL__VK_ERROR_TOO_MANY_OBJECTS:
19986 return "Too many objects";
19987 case FPL__VK_ERROR_FORMAT_NOT_SUPPORTED:
19988 return "Format not supported";
19989 case FPL__VK_ERROR_FRAGMENTED_POOL:
19990 return "Fragmented pool";
19991 case FPL__VK_SUCCESS:
19992 return "Success";
19993 case FPL__VK_NOT_READY:
19994 return "Not-Ready";
19995 case FPL__VK_TIMEOUT:
19996 return "Timeout";
19997 case FPL__VK_EVENT_SET:
19998 return "Event-Set";
19999 case FPL__VK_EVENT_RESET:
20000 return "Event-Reset";
20001 case FPL__VK_INCOMPLETE:
20002 return "Incomplete";
20003 default:
20004 return "Unknown";
20005 }
20006}
20007
20008fpl_internal FPL__FUNC_VIDEO_BACKEND_GETPROCEDURE(fpl__VideoBackend_Vulkan_GetProcedure) {
20009 const fpl__VideoBackendVulkan *nativeBackend = (const fpl__VideoBackendVulkan *)backend;
20010 const fpl__VulkanApi *api = &nativeBackend->api;
20011 if (api->libraryHandle.isValid) {
20012 return fplGetDynamicLibraryProc(&api->libraryHandle, procName);
20013 }
20014 return(fpl_null);
20015}
20016
20017fpl_internal FPL__FUNC_VIDEO_BACKEND_GETREQUIREMENTS(fpl__VideoBackend_Vulkan_GetRequirements) {
20018 FPL__CheckArgumentNull(requirements, false);
20019 fplClearStruct(requirements);
20020
20021 fplAssert(requirements->vulkan.instanceExtensionCount < fplArrayCount(requirements->vulkan.instanceExtensions));
20022 requirements->vulkan.instanceExtensions[requirements->vulkan.instanceExtensionCount++] = "VK_KHR_surface";
20023#if defined(FPL_PLATFORM_WINDOWS)
20024 fplAssert(requirements->vulkan.instanceExtensionCount < fplArrayCount(requirements->vulkan.instanceExtensions));
20025 requirements->vulkan.instanceExtensions[requirements->vulkan.instanceExtensionCount++] = "VK_KHR_win32_surface";
20026#elif defined(FPL_SUBPLATFORM_X11)
20027 fplAssert(requirements->vulkan.instanceExtensionCount < fplArrayCount(requirements->vulkan.instanceExtensions));
20028 requirements->vulkan.instanceExtensions[requirements->vulkan.instanceExtensionCount++] = "VK_KHR_xlib_surface";
20029#else
20030 return(false);
20031#endif
20032 return(true);
20033}
20034
20035fpl_internal uint32_t fpl__VersionInfoToVulkanVersion(const fplVersionInfo *versionInfo) {
20036 uint32_t major = fplStringToS32(versionInfo->major);
20037 uint32_t minor = fplStringToS32(versionInfo->minor);
20038 uint32_t patch = fplStringToS32(versionInfo->fix);
20039 uint32_t result = FPL__VK_MAKE_VERSION(major, minor, patch);
20040 return(result);
20041}
20042
20043fpl_internal const char *fpl__GetVulkanMessageSeverityName(const fpl__VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity) {
20044 switch (messageSeverity) {
20045 case FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: return "ERROR";
20046 case FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: return "WARNING";
20047 case FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: return "INFO";
20048 case FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: return "VERBOSE";
20049 default: return "Unknown";
20050 }
20051}
20052
20053fpl_internal fpl__VKAPI_ATTR fpl__VkBool32 fpl__VKAPI_CALL fpl__VulkanDebugCallback(fpl__VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, fpl__VkDebugUtilsMessageTypeFlagsEXT messageType, const fpl__VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) {
20054 fpl__VulkanDebugMessengerUserData *data = (fpl__VulkanDebugMessengerUserData *)pUserData;
20055 const char *message = pCallbackData->pMessage;
20056 if (data->userCallback != fpl_null) {
20057 data->userCallback(data->userData, message, messageSeverity, messageType, pCallbackData);
20058 } else {
20059 if (messageSeverity == FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
20060 FPL_LOG_ERROR(FPL__MODULE_VIDEO_VULKAN, "Validation: %s", message);
20061 else if (messageSeverity == FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
20062 FPL_LOG_WARN(FPL__MODULE_VIDEO_VULKAN, "Validation: %s", message);
20063 else if (messageSeverity == FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT)
20064 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Validation: %s", message);
20065 else if (messageSeverity == FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT)
20066 FPL_LOG_DEBUG(FPL__MODULE_VIDEO_VULKAN, "Validation: %s", message);
20067 else {
20068 FPL_LOG_DEBUG(FPL__MODULE_VIDEO_VULKAN, "Validation: %s", message);
20069 }
20070 }
20071 return 0;
20072}
20073
20074fpl_internal void fpl__VulkanDestroyDebugMessenger(fpl__VideoBackendVulkan *nativeBackend) {
20075 if (nativeBackend->debugMessenger != fpl_null) {
20076 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Destroy Vulkan Debug Messenger '%p'", nativeBackend->debugMessenger);
20077 const fpl__VulkanApi *api = &nativeBackend->api;
20078 fpl__func_vkDestroyDebugUtilsMessengerEXT destroyFunc = (fpl__func_vkDestroyDebugUtilsMessengerEXT)api->vkGetInstanceProcAddr(nativeBackend->instanceHandle, "vkDestroyDebugUtilsMessengerEXT");
20079 fplAssert(destroyFunc != fpl_null);
20080 destroyFunc(nativeBackend->instanceHandle, nativeBackend->debugMessenger, nativeBackend->allocator);
20081 nativeBackend->debugMessenger = fpl_null;
20082 }
20083 fplClearStruct(&nativeBackend->debugMessengerUserData);
20084}
20085
20086fpl_internal bool fpl__VulkanCreateDebugMessenger(const fplVulkanSettings *settings, fpl__VideoBackendVulkan *nativeBackend) {
20087 fpl__VkDebugUtilsMessageSeverityFlagsEXT severities = 0;
20088 switch (settings->validationSeverity) {
20090 severities = FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
20091 break;
20093 severities = FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
20094 break;
20096 severities = FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
20097 break;
20100 severities = FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | FPL__VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
20101 break;
20103 default:
20104 return(false);
20105 }
20106
20107 fpl__VulkanDebugMessengerUserData *userData = &nativeBackend->debugMessengerUserData;
20108 userData->userCallback = settings->validationLayerCallback;
20109 userData->userData = settings->userData;
20110 userData->validationMode = settings->validationLayerMode;
20111
20112 // @TODO(final): Message type filtering in fplVulkanSettings
20113 fpl__VkDebugUtilsMessageTypeFlagsEXT messageTypes =
20114 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
20115 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
20116 FPL__VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
20117
20118 fpl__VkDebugUtilsMessengerCreateInfoEXT createInfo = fplZeroInit;
20119 createInfo.sType = FPL__VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
20120 createInfo.messageSeverity = severities;
20121 createInfo.messageType = messageTypes;
20122 createInfo.pfnUserCallback = fpl__VulkanDebugCallback;
20123 createInfo.pUserData = userData;
20124
20125 const fpl__VulkanApi *api = &nativeBackend->api;
20126
20127 fpl__func_vkCreateDebugUtilsMessengerEXT createFunc = (fpl__func_vkCreateDebugUtilsMessengerEXT)api->vkGetInstanceProcAddr(nativeBackend->instanceHandle, "vkCreateDebugUtilsMessengerEXT");
20128 if (createFunc == fpl_null) {
20129 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Vulkan instance proc 'vkCreateDebugUtilsMessengerEXT' not found! Maybe the instance extension 'VK_EXT_debug_utils' was not set?");
20130 return(false);
20131 }
20132
20133 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Create Vulkan Debug Messenger for Instance '%p' with severity flags of '%lu'", nativeBackend->instanceHandle, severities);
20134 fpl__VkResult creationResult = (fpl__VkResult)createFunc(nativeBackend->instanceHandle, &createInfo, nativeBackend->allocator, &nativeBackend->debugMessenger);
20135 if (creationResult != FPL__VK_SUCCESS) {
20136 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Failed creating Vulkan Debug Messenger for Instance '%p' with severity flags of '%lu' -> (VkResult: %d)!", nativeBackend->instanceHandle, severities, creationResult);
20137 return(false);
20138 }
20139
20140 return(true);
20141}
20142
20143fpl_internal FPL__FUNC_VIDEO_BACKEND_PREPAREWINDOW(fpl__VideoBackend_Vulkan_PrepareWindow) {
20144 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
20145 if (videoSettings->graphics.vulkan.instanceHandle == fpl_null) {
20146
20147 const fpl__VulkanApi *api = &nativeBackend->api;
20148
20149 nativeBackend->allocator = fpl_null;
20150 nativeBackend->instanceHandle = fpl_null;
20151 nativeBackend->isInstanceUserDefined = false;
20152
20153 fplVideoRequirements requirements = fplZeroInit;
20154 if (!fpl__VideoBackend_Vulkan_GetRequirements(&requirements) || requirements.vulkan.instanceExtensionCount == 0) {
20155 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Failed getting required instance extensions for creating an Vulkan instance!");
20156 return(false);
20157 }
20158
20159 // @TODO(final): Validate vulkan video settings
20160
20161 //
20162 // Validate required instance extensions and layers
20163 //
20164 bool supportsDebugUtils = false;
20165
20166 fpl__VkExtensionProperties *supportedInstanceExtensions = fpl_null;
20167 uint32_t supportedInstanceExtensionCount = 0;
20168 api->vkEnumerateInstanceExtensionProperties(fpl_null, &supportedInstanceExtensionCount, fpl_null);
20169 if (supportedInstanceExtensionCount > 0) {
20170 supportedInstanceExtensions = (fpl__VkExtensionProperties *)fpl__AllocateTemporaryMemory(sizeof(fpl__VkExtensionProperties) * supportedInstanceExtensionCount, 16);
20171 api->vkEnumerateInstanceExtensionProperties(fpl_null, &supportedInstanceExtensionCount, supportedInstanceExtensions);
20172 for (uint32_t i = 0; i < supportedInstanceExtensionCount; ++i) {
20173 if (fplIsStringEqual(supportedInstanceExtensions[i].extensionName, "VK_EXT_debug_utils")) {
20174 supportsDebugUtils = true;
20175 }
20176 }
20177 fpl__ReleaseTemporaryMemory(supportedInstanceExtensions);
20178 }
20179
20180 bool supportsValidationLayer = false;
20181
20182 fpl__VkLayerProperties *supportedLayers = fpl_null;
20183 uint32_t supportedLayerCount = 0;
20184 api->vkEnumerateInstanceLayerProperties(&supportedLayerCount, fpl_null);
20185 if (supportedLayerCount > 0) {
20186 supportedLayers = (fpl__VkLayerProperties *)fpl__AllocateTemporaryMemory(sizeof(fpl__VkLayerProperties) * supportedLayerCount, 16);
20187 api->vkEnumerateInstanceLayerProperties(&supportedLayerCount, supportedLayers);
20188 for (uint32_t i = 0; i < supportedLayerCount; ++i) {
20189 if (fplIsStringEqual(supportedLayers[i].layerName, "VK_LAYER_KHRONOS_validation")) {
20190 supportsValidationLayer = true;
20191 }
20192 }
20193 fpl__ReleaseTemporaryMemory(supportedInstanceExtensions);
20194 }
20195
20196 const char *enabledValidationLayers[4] = fplZeroInit;
20197 const char *enabledInstanceExtensions[8] = fplZeroInit;
20198 uint32_t enabledValidationLayerCount = 0;
20199 uint32_t enabledInstanceExtensionCount = 0;
20202 if (!supportsDebugUtils) {
20203 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "VK_EXT_debug_utils instance extension is not supported!");
20204 return(false);
20205 }
20206 if (!supportsValidationLayer) {
20207 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "VK_LAYER_KHRONOS_validation instance layer is not supported!");
20208 return(false);
20209 }
20210 }
20211 if (supportsDebugUtils && supportsValidationLayer) {
20212 enabledValidationLayers[enabledValidationLayerCount++] = "VK_LAYER_KHRONOS_validation";
20213 enabledInstanceExtensions[enabledInstanceExtensionCount++] = "VK_EXT_debug_utils";
20214 }
20215 }
20216 for (uint32_t i = 0; i < requirements.vulkan.instanceExtensionCount; ++i) {
20217 fplAssert(enabledInstanceExtensionCount < fplArrayCount(enabledInstanceExtensions));
20218 enabledInstanceExtensions[enabledInstanceExtensionCount++] = requirements.vulkan.instanceExtensions[i];
20219 }
20220
20221 fpl__VkApplicationInfo applicationInfo = fplZeroInit;
20222 applicationInfo.sType = FPL__VK_STRUCTURE_TYPE_APPLICATION_INFO;
20223 applicationInfo.pApplicationName = videoSettings->graphics.vulkan.appName;
20224 applicationInfo.pEngineName = videoSettings->graphics.vulkan.engineName;
20225 applicationInfo.apiVersion = fpl__VersionInfoToVulkanVersion(&videoSettings->graphics.vulkan.apiVersion);
20226 applicationInfo.engineVersion = fpl__VersionInfoToVulkanVersion(&videoSettings->graphics.vulkan.engineVersion);
20227 applicationInfo.applicationVersion = fpl__VersionInfoToVulkanVersion(&videoSettings->graphics.vulkan.appVersion);
20228
20229 fpl__VkInstanceCreateInfo instanceCreateInfo = fplZeroInit;
20230 instanceCreateInfo.sType = FPL__VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
20231 instanceCreateInfo.pApplicationInfo = &applicationInfo;
20232 instanceCreateInfo.enabledExtensionCount = enabledInstanceExtensionCount;
20233 instanceCreateInfo.ppEnabledExtensionNames = enabledInstanceExtensions;
20234 instanceCreateInfo.enabledLayerCount = enabledValidationLayerCount;
20235 instanceCreateInfo.ppEnabledLayerNames = enabledValidationLayers;
20236
20237 const fpl__VkAllocationCallbacks *allocator = (const fpl__VkAllocationCallbacks *)videoSettings->graphics.vulkan.allocator;
20238
20239 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Create Vulkan Instance with %lu extensions and %lu layers", instanceCreateInfo.enabledExtensionCount, instanceCreateInfo.enabledLayerCount);
20240 fpl__VkInstance instance = fpl_null;
20241 fpl__VkResult creationResult = (fpl__VkResult)api->vkCreateInstance(&instanceCreateInfo, allocator, &instance);
20242 if (creationResult != FPL__VK_SUCCESS) {
20243 const char *creationError = fpl__GetVulkanResultString(creationResult);
20244 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Failed creating Vulkan Instance with %lu extensions and %lu layers -> (VkResult: %d, Error: %s)!", instanceCreateInfo.enabledExtensionCount, instanceCreateInfo.enabledLayerCount, creationResult, creationError);
20245 return(false);
20246 }
20247
20248 nativeBackend->allocator = allocator;
20249 nativeBackend->instanceHandle = instance;
20250 nativeBackend->isInstanceUserDefined = false;
20251
20252 // Debug utils
20254 if (!fpl__VulkanCreateDebugMessenger(&videoSettings->graphics.vulkan, nativeBackend)) {
20256 FPL__WARNING(FPL__MODULE_VIDEO_VULKAN, "The debug messenger could not be created, no validation message are printed");
20257 } else {
20258 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "The debug messenger could not be created, stop vulkan initialization!");
20259 return(false);
20260 }
20261 }
20262 }
20263
20264 return(true);
20265 } else {
20266 // Instance passed by user
20267 nativeBackend->allocator = (const fpl__VkAllocationCallbacks *)videoSettings->graphics.vulkan.allocator;
20268 nativeBackend->instanceHandle = (fpl__VkInstance)videoSettings->graphics.vulkan.instanceHandle;
20269 nativeBackend->isInstanceUserDefined = true;
20270 return(true);
20271 }
20272}
20273
20274fpl_internal FPL__FUNC_VIDEO_BACKEND_FINALIZEWINDOW(fpl__VideoBackend_Vulkan_FinalizeWindow) {
20275 return(true);
20276}
20277
20278fpl_internal FPL__FUNC_VIDEO_BACKEND_SHUTDOWN(fpl__VideoBackend_Vulkan_Shutdown) {
20279 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
20280
20281 const fpl__VulkanApi *api = &nativeBackend->api;
20282
20283 if (nativeBackend->surfaceHandle != FPL__VK_NULL_HANDLE) {
20284 fpl__func_vkDestroySurfaceKHR destroyProc = (fpl__func_vkDestroySurfaceKHR)api->vkGetInstanceProcAddr(nativeBackend->instanceHandle, "vkDestroySurfaceKHR");
20285 fplAssert(destroyProc != fpl_null);
20286
20287 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Destroy Vulkan Surface '%p'", nativeBackend->surfaceHandle);
20288 destroyProc(nativeBackend->instanceHandle, nativeBackend->surfaceHandle, nativeBackend->allocator);
20289
20290 nativeBackend->surfaceHandle = FPL__VK_NULL_HANDLE;
20291 }
20292}
20293
20294fpl_internal FPL__FUNC_VIDEO_BACKEND_INITIALIZE(fpl__VideoBackend_Vulkan_Initialize) {
20295 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
20296
20297 const fpl__VulkanApi *api = &nativeBackend->api;
20298
20299 if (nativeBackend->instanceHandle == FPL__VK_NULL_HANDLE) {
20300 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Cannot create a Vulkan surface without a Vulkan instance!");
20301 return(false);
20302 }
20303
20304 fpl__VkSurfaceKHR surfaceHandle = FPL__VK_NULL_HANDLE;
20305
20306#if defined(FPL_PLATFORM_WINDOWS)
20307 FPL_LOG_DEBUG(FPL__MODULE_VIDEO_VULKAN, "Query Vulkan Instance Proc 'vkCreateWin32SurfaceKHR' for instance '%p'", nativeBackend->instanceHandle);
20308 fpl__func_vkCreateWin32SurfaceKHR createProc = (fpl__func_vkCreateWin32SurfaceKHR)api->vkGetInstanceProcAddr(nativeBackend->instanceHandle, "vkCreateWin32SurfaceKHR");
20309 if (createProc == fpl_null) {
20310 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Vulkan instance proc 'vkCreateWin32SurfaceKHR' not found! Maybe the instance extension 'VK_KHR_win32_surface' was not set?");
20311 return(false);
20312 }
20313
20314 fpl__VkWin32SurfaceCreateInfoKHR creationInfo = fplZeroInit;
20315 creationInfo.sType = FPL__VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
20316 creationInfo.hinstance = fpl__global__InitState.win32.appInstance;
20317 creationInfo.hwnd = windowState->win32.windowHandle;
20318
20319 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Create Vulkan Win32 Surface for hwnd '%p', hinstance '%p' and Vulkan instance '%p'", creationInfo.hwnd, creationInfo.hinstance, nativeBackend->instanceHandle);
20320 fpl__VkResult creationResult = (fpl__VkResult)createProc(nativeBackend->instanceHandle, &creationInfo, nativeBackend->allocator, &surfaceHandle);
20321 if (creationResult != FPL__VK_SUCCESS) {
20322 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Failed creating vulkan surface KHR for Win32 -> (VkResult: %d)!", creationResult);
20323 return(false);
20324 }
20325#elif defined(FPL_SUBPLATFORM_X11)
20326 FPL_LOG_DEBUG(FPL__MODULE_VIDEO_VULKAN, "Query Vulkan Instance Proc 'vkCreateXlibSurfaceKHR' for instance '%p'", nativeBackend->instanceHandle);
20327 fpl__func_vkCreateXlibSurfaceKHR createProc = (fpl__func_vkCreateXlibSurfaceKHR)api->vkGetInstanceProcAddr(nativeBackend->instanceHandle, "vkCreateXlibSurfaceKHR");
20328 if (createProc == fpl_null) {
20329 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Vulkan instance proc 'vkCreateXlibSurfaceKHR' not found! Maybe the instance extension 'VK_KHR_xlib_surface' was not set?");
20330 return(false);
20331 }
20332
20333 fpl__VkXlibSurfaceCreateInfoKHR creationInfo = fplZeroInit;
20334 creationInfo.sType = FPL__VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
20335 creationInfo.dpy = (fpl__X11Display *)windowState->x11.display;
20336 creationInfo.window = windowState->x11.window;
20337
20338 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Create Vulkan X11 Surface for display '%p', window '%d' and Vulkan instance '%p'", creationInfo.dpy, creationInfo.window, nativeBackend->instanceHandle);
20339 fpl__VkResult creationResult = createProc(nativeBackend->instanceHandle, &creationInfo, nativeBackend->allocator, &surfaceHandle);
20340 if (creationResult != FPL__VK_SUCCESS) {
20341 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Failed creating vulkan surface KHR for X11 -> (VkResult: %d)!", creationResult);
20342 return(false);
20343 }
20344#else
20345 FPL__ERROR(FPL__MODULE_VIDEO_VULKAN, "Unsupported Platform!");
20346 return(false);
20347#endif
20348
20349 fplAssert(surfaceHandle != FPL__VK_NULL_HANDLE);
20350 nativeBackend->surfaceHandle = surfaceHandle;
20351
20352 backend->surface.vulkan.instance = nativeBackend->instanceHandle;
20353 backend->surface.vulkan.surfaceKHR = nativeBackend->surfaceHandle;
20354
20355#if defined(FPL_PLATFORM_WINDOWS)
20356 backend->surface.window.win32.windowHandle = windowState->win32.windowHandle;
20357 backend->surface.window.win32.deviceContext = windowState->win32.deviceContext;
20358#elif defined(FPL_SUBPLATFORM_X11)
20359 backend->surface.window.x11.display = windowState->x11.display;
20360 backend->surface.window.x11.window = windowState->x11.window;
20361 backend->surface.window.x11.screen = windowState->x11.screen;
20362 backend->surface.window.x11.visual = windowState->x11.visual;
20363#endif
20364
20365 return(true);
20366}
20367
20368fpl_internal FPL__FUNC_VIDEO_BACKEND_DESTROYEDWINDOW(fpl__VideoBackend_Vulkan_DestroyedWindow) {
20369 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
20370
20371 // Destroy Vulkan Debug Messenger
20372 if (!nativeBackend->isInstanceUserDefined && nativeBackend->instanceHandle != fpl_null && nativeBackend->debugMessenger != fpl_null) {
20373 fpl__VulkanDestroyDebugMessenger(nativeBackend);
20374 }
20375
20376 // Destroy Vulkan instance
20377 const fpl__VulkanApi *api = &nativeBackend->api;
20378 if (!nativeBackend->isInstanceUserDefined && nativeBackend->instanceHandle != fpl_null) {
20379 FPL_LOG_INFO(FPL__MODULE_VIDEO_VULKAN, "Destroy Vulkan Instance '%p'", nativeBackend->instanceHandle);
20380 api->vkDestroyInstance(nativeBackend->instanceHandle, nativeBackend->allocator);
20381 nativeBackend->instanceHandle = fpl_null;
20382 }
20383}
20384
20385fpl_internal FPL__FUNC_VIDEO_BACKEND_UNLOAD(fpl__VideoBackend_Vulkan_Unload) {
20386 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
20387
20388 // Unload core api
20389 fpl__UnloadVulkanApi(&nativeBackend->api);
20390
20391 // Clear everything
20392 fplClearStruct(nativeBackend);
20393}
20394
20395fpl_internal FPL__FUNC_VIDEO_BACKEND_LOAD(fpl__VideoBackend_Vulkan_Load) {
20396 fpl__VideoBackendVulkan *nativeBackend = (fpl__VideoBackendVulkan *)backend;
20397 const fplVideoSettings *videoSettings = &appState->currentSettings.video;
20398
20399 // Clear and set magic id
20400 fplClearStruct(nativeBackend);
20401 nativeBackend->base.magic = FPL__VIDEOBACKEND_MAGIC;
20402
20403 // Load core api
20404 if (!fpl__LoadVulkanApi(&nativeBackend->api, videoSettings->graphics.vulkan.libraryFile)) {
20405 return(false);
20406 }
20407
20408 return(true);
20409}
20410
20411fpl_internal FPL__FUNC_VIDEO_BACKEND_PRESENT(fpl__VideoBackend_Vulkan_Present) {
20412 const fpl__VideoBackendVulkan *nativeBackend = (const fpl__VideoBackendVulkan *)backend;
20413}
20414
20415fpl_internal fpl__VideoContext fpl__VideoBackend_Vulkan_Construct() {
20416 fpl__VideoContext result = fpl__StubVideoContext();
20417 result.loadFunc = fpl__VideoBackend_Vulkan_Load;
20418 result.unloadFunc = fpl__VideoBackend_Vulkan_Unload;
20419 result.getProcedureFunc = fpl__VideoBackend_Vulkan_GetProcedure;
20420 result.initializeFunc = fpl__VideoBackend_Vulkan_Initialize;
20421 result.shutdownFunc = fpl__VideoBackend_Vulkan_Shutdown;
20422 result.prepareWindowFunc = fpl__VideoBackend_Vulkan_PrepareWindow;
20423 result.finalizeWindowFunc = fpl__VideoBackend_Vulkan_FinalizeWindow;
20424 result.destroyedWindowFunc = fpl__VideoBackend_Vulkan_DestroyedWindow;
20425 result.presentFunc = fpl__VideoBackend_Vulkan_Present;
20426 result.getRequirementsFunc = fpl__VideoBackend_Vulkan_GetRequirements;
20427 return(result);
20428}
20429
20430#endif // FPL__ENABLE_VIDEO_VULKAN
20431
20432
20433#endif // FPL__VIDEO_BACKENDS_IMPLEMENTED
20434
20435// ****************************************************************************
20436//
20437// > AUDIO_BACKENDS
20438//
20439// ****************************************************************************
20440#if !defined(FPL__AUDIO_BACKENDS_IMPLEMENTED) && defined(FPL__ENABLE_AUDIO)
20441# define FPL__AUDIO_BACKENDS_IMPLEMENTED
20442
20443typedef enum fpl__AudioDeviceState {
20444 fpl__AudioDeviceState_Uninitialized = 0,
20445 fpl__AudioDeviceState_Stopped,
20446 fpl__AudioDeviceState_Started,
20447 fpl__AudioDeviceState_Starting,
20448 fpl__AudioDeviceState_Stopping,
20449} fpl__AudioDeviceState;
20450
20451typedef struct fpl__CommonAudioState {
20452 fplAudioDeviceFormat internalFormat;
20453 fpl_audio_client_read_callback *clientReadCallback;
20454 void *clientUserData;
20455 volatile fpl__AudioDeviceState state;
20456} fpl__CommonAudioState;
20457
20458fpl_internal uint32_t fpl__ReadAudioFramesFromClient(const fpl__CommonAudioState *commonAudio, uint32_t frameCount, void *pSamples) {
20459 uint32_t framesRead = 0;
20460 if (commonAudio->clientReadCallback != fpl_null) {
20461 framesRead = commonAudio->clientReadCallback(&commonAudio->internalFormat, frameCount, pSamples, commonAudio->clientUserData);
20462 }
20463 uint32_t channels = commonAudio->internalFormat.channels;
20464 uint32_t samplesRead = framesRead * channels;
20465 uint32_t sampleSize = fplGetAudioSampleSizeInBytes(commonAudio->internalFormat.type);
20466 uint32_t consumedBytes = samplesRead * sampleSize;
20467 uint32_t remainingBytes = ((frameCount * channels) - samplesRead) * sampleSize;
20468 if (remainingBytes > 0) {
20469 fplMemoryClear((uint8_t *)pSamples + consumedBytes, remainingBytes);
20470 }
20471 return(samplesRead);
20472}
20473
20474// Global Audio GUIDs
20475#if defined(FPL_PLATFORM_WINDOWS)
20476static GUID FPL__GUID_KSDATAFORMAT_SUBTYPE_PCM = { 0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} };
20477static GUID FPL__GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { 0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} };
20478#endif
20479
20480// Forward declarations
20481fpl_internal fpl__AudioDeviceState fpl__AudioGetDeviceState(fpl__CommonAudioState *audioState);
20482fpl_internal bool fpl__IsAudioDeviceInitialized(fpl__CommonAudioState *audioState);
20483fpl_internal bool fpl__IsAudioDeviceStarted(fpl__CommonAudioState *audioState);
20484
20485// ############################################################################
20486//
20487// > AUDIO_BACKEND_DIRECTSOUND
20488//
20489// ############################################################################
20490#if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
20491# include <mmreg.h>
20492# include <mmsystem.h>
20493# include <dsound.h>
20494
20495#define FPL__FUNC_DSOUND_DirectSoundCreate(name) HRESULT WINAPI name(const GUID* pcGuidDevice, LPDIRECTSOUND *ppDS8, LPUNKNOWN pUnkOuter)
20496typedef FPL__FUNC_DSOUND_DirectSoundCreate(func_DirectSoundCreate);
20497#define FPL__FUNC_DSOUND_DirectSoundEnumerateW(name) HRESULT WINAPI name(LPDSENUMCALLBACKW pDSEnumCallback, LPVOID pContext)
20498typedef FPL__FUNC_DSOUND_DirectSoundEnumerateW(func_DirectSoundEnumerateW);
20499static GUID FPL__IID_IDirectSoundNotify = { 0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16} };
20500#define FPL__DIRECTSOUND_MAX_PERIODS 4
20501
20502typedef struct fpl__DirectSoundApi {
20503 HMODULE dsoundLibrary;
20504 func_DirectSoundCreate *DirectSoundCreate;
20505 func_DirectSoundEnumerateW *DirectSoundEnumerateW;
20506} fpl__DirectSoundApi;
20507
20508fpl_internal void fpl__UnloadDirectSoundApi(fpl__DirectSoundApi *dsoundApi) {
20509 fplAssert(dsoundApi != fpl_null);
20510 if (dsoundApi->dsoundLibrary != fpl_null) {
20511 FreeLibrary(dsoundApi->dsoundLibrary);
20512 }
20513 fplClearStruct(dsoundApi);
20514}
20515
20516fpl_internal bool fpl__LoadDirectSoundApi(fpl__DirectSoundApi *dsoundApi) {
20517 fplAssert(dsoundApi != fpl_null);
20518 bool result = false;
20519 const char *dsoundLibraryName = "dsound.dll";
20520 fplClearStruct(dsoundApi);
20521 do {
20522 HMODULE dsoundLibrary = fpl_null;
20523 FPL__WIN32_LOAD_LIBRARY(FPL__MODULE_AUDIO_DIRECTSOUND, dsoundLibrary, dsoundLibraryName);
20524 dsoundApi->dsoundLibrary = dsoundLibrary;
20525 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_DIRECTSOUND, dsoundLibrary, dsoundLibraryName, dsoundApi, func_DirectSoundCreate, DirectSoundCreate);
20526 FPL__WIN32_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_DIRECTSOUND, dsoundLibrary, dsoundLibraryName, dsoundApi, func_DirectSoundEnumerateW, DirectSoundEnumerateW);
20527 result = true;
20528 } while (0);
20529 if (!result) {
20530 fpl__UnloadDirectSoundApi(dsoundApi);
20531 }
20532 return(result);
20533}
20534
20535typedef struct fpl__DirectSoundAudioState {
20536 fpl__DirectSoundApi api;
20537 LPDIRECTSOUND directSound;
20538 LPDIRECTSOUNDBUFFER primaryBuffer;
20539 LPDIRECTSOUNDBUFFER secondaryBuffer;
20540 LPDIRECTSOUNDNOTIFY notify;
20541 HANDLE notifyEvents[FPL__DIRECTSOUND_MAX_PERIODS];
20542 HANDLE stopEvent;
20543 uint32_t lastProcessedFrame;
20544 bool breakMainLoop;
20545} fpl__DirectSoundAudioState;
20546
20547typedef struct fpl__DirectSoundDeviceInfos {
20548 fplAudioDeviceInfo *deviceInfos;
20549 uint32_t foundDeviceCount;
20550 uint32_t maxDeviceCount;
20551 uint32_t capacityOverflow;
20552} fpl__DirectSoundDeviceInfos;
20553
20554fpl_internal BOOL CALLBACK fpl__GetDeviceCallbackDirectSound(LPGUID lpGuid, LPCWSTR lpwstrDescription, LPCWSTR lpwstrModule, LPVOID lpContext) {
20555 fpl__DirectSoundDeviceInfos *infos = (fpl__DirectSoundDeviceInfos *)lpContext;
20556 fplAssert(infos != fpl_null);
20557 if (infos->deviceInfos != fpl_null) {
20558 uint32_t index = infos->foundDeviceCount++;
20559 if (index < infos->maxDeviceCount) {
20560 fplAudioDeviceInfo *deviceInfo = infos->deviceInfos + index;
20561 fplClearStruct(deviceInfo);
20562 fplWideStringToUTF8String(lpwstrDescription, lstrlenW(lpwstrDescription), deviceInfo->name, fplArrayCount(deviceInfo->name));
20563 if (lpGuid != fpl_null) {
20564 fplMemoryCopy(lpGuid, sizeof(deviceInfo->id.dshow), &deviceInfo->id.dshow);
20565 }
20566 } else {
20567 infos->capacityOverflow++;
20568 }
20569 } else {
20570 infos->foundDeviceCount++;
20571 }
20572 return TRUE;
20573}
20574
20575fpl_internal uint32_t fpl__GetAudioDevicesDirectSound(fpl__DirectSoundAudioState *dsoundState, fplAudioDeviceInfo *deviceInfos, uint32_t maxDeviceCount) {
20576 uint32_t result = 0;
20577 const fpl__DirectSoundApi *dsoundApi = &dsoundState->api;
20578 fpl__DirectSoundDeviceInfos infos = fplZeroInit;
20579 infos.maxDeviceCount = maxDeviceCount;
20580 infos.deviceInfos = deviceInfos;
20581 infos.capacityOverflow = 0;
20582 dsoundApi->DirectSoundEnumerateW(fpl__GetDeviceCallbackDirectSound, &infos);
20583 result = infos.foundDeviceCount;
20584 if (infos.capacityOverflow > 0) {
20585 FPL__ERROR(FPL__MODULE_AUDIO_DIRECTSOUND, "Capacity of '%lu' for audio device infos has been reached. '%lu' audio devices are not included in the result", maxDeviceCount, infos.capacityOverflow);
20586 }
20587 return(result);
20588}
20589
20590fpl_internal bool fpl__AudioReleaseDirectSound(const fpl__CommonAudioState *commonAudio, fpl__DirectSoundAudioState *dsoundState) {
20591 if (dsoundState->stopEvent != fpl_null) {
20592 CloseHandle(dsoundState->stopEvent);
20593 }
20594
20595 for (uint32_t i = 0; i < commonAudio->internalFormat.periods; ++i) {
20596 if (dsoundState->notifyEvents[i]) {
20597 CloseHandle(dsoundState->notifyEvents[i]);
20598 }
20599 }
20600
20601 if (dsoundState->notify != fpl_null) {
20602 IDirectSoundNotify_Release(dsoundState->notify);
20603 }
20604
20605 if (dsoundState->secondaryBuffer != fpl_null) {
20606 IDirectSoundBuffer_Release(dsoundState->secondaryBuffer);
20607 }
20608
20609 if (dsoundState->primaryBuffer != fpl_null) {
20610 IDirectSoundBuffer_Release(dsoundState->primaryBuffer);
20611 }
20612
20613 if (dsoundState->directSound != fpl_null) {
20614 IDirectSound_Release(dsoundState->directSound);
20615 }
20616
20617 fpl__UnloadDirectSoundApi(&dsoundState->api);
20618
20619 fplClearStruct(dsoundState);
20620
20621 return true;
20622}
20623
20624fpl_internal fplAudioResultType fpl__AudioInitDirectSound(const fplAudioSettings *audioSettings, const fplAudioDeviceFormat *targetFormat, fpl__CommonAudioState *commonAudio, fpl__DirectSoundAudioState *dsoundState) {
20625#ifdef __cplusplus
20626 GUID guid_IID_IDirectSoundNotify = FPL__IID_IDirectSoundNotify;
20627#else
20628 GUID *guid_IID_IDirectSoundNotify = &FPL__IID_IDirectSoundNotify;
20629#endif
20630
20631#define FPL__DSOUND_INIT_ERROR(ret, format, ...) do { \
20632 FPL__ERROR(FPL__MODULE_AUDIO_DIRECTSOUND, format, ## __VA_ARGS__); \
20633 fpl__AudioReleaseDirectSound(commonAudio, dsoundState); \
20634 return ret; \
20635} while (0)
20636
20637 fplAssert(fpl__global__AppState != fpl_null);
20638 fpl__PlatformAppState *appState = fpl__global__AppState;
20639 fpl__Win32AppState *win32AppState = &appState->win32;
20640 const fpl__Win32Api *apiFuncs = &win32AppState->winApi;
20641
20642 // Load direct sound library
20643 fpl__DirectSoundApi *dsoundApi = &dsoundState->api;
20644 if (!fpl__LoadDirectSoundApi(dsoundApi)) {
20645 FPL__DSOUND_INIT_ERROR(fplAudioResultType_ApiFailed, "API could not be loaded!");
20646 }
20647
20648 // Load direct sound object
20649 const fplAudioDeviceInfo *targetDevice = &audioSettings->targetDevice;
20650 const GUID *deviceId = fpl_null;
20651 if (fplGetStringLength(targetDevice->name) > 0) {
20652 fplAssert(sizeof(GUID) == sizeof(targetDevice->id.dshow));
20653 deviceId = (const GUID *)&targetDevice->id.dshow;
20654 }
20655 if (!SUCCEEDED(dsoundApi->DirectSoundCreate(deviceId, &dsoundState->directSound, fpl_null))) {
20656 char idString[64];
20657 fpl__Win32FormatGuidString(idString, fplArrayCount(idString), deviceId);
20658 FPL__DSOUND_INIT_ERROR(fplAudioResultType_NoDeviceFound, "Audio device by id '%s' could not be created!", idString);
20659 }
20660
20661 // Setup wave format ex
20662 WAVEFORMATEXTENSIBLE waveFormat = fplZeroInit;
20663 waveFormat.Format.cbSize = sizeof(waveFormat);
20664 waveFormat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
20665 waveFormat.Format.nChannels = (WORD)targetFormat->channels;
20666 waveFormat.Format.nSamplesPerSec = (DWORD)targetFormat->sampleRate;
20667 waveFormat.Format.wBitsPerSample = (WORD)fplGetAudioSampleSizeInBytes(targetFormat->type) * 8;
20668 waveFormat.Format.nBlockAlign = (waveFormat.Format.nChannels * waveFormat.Format.wBitsPerSample) / 8;
20669 waveFormat.Format.nAvgBytesPerSec = waveFormat.Format.nBlockAlign * waveFormat.Format.nSamplesPerSec;
20670 waveFormat.Samples.wValidBitsPerSample = waveFormat.Format.wBitsPerSample;
20671 if ((targetFormat->type == fplAudioFormatType_F32) || (targetFormat->type == fplAudioFormatType_F64)) {
20672 waveFormat.SubFormat = FPL__GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
20673 } else {
20674 waveFormat.SubFormat = FPL__GUID_KSDATAFORMAT_SUBTYPE_PCM;
20675 }
20676
20677 // Get either local window handle or desktop handle
20678 HWND windowHandle = fpl_null;
20679# if defined(FPL__ENABLE_WINDOW)
20680 if (appState->initFlags & fplInitFlags_Window) {
20681 windowHandle = appState->window.win32.windowHandle;
20682 }
20683# endif
20684 if (windowHandle == fpl_null) {
20685 windowHandle = apiFuncs->user.GetDesktopWindow();
20686 }
20687
20688 // The cooperative level must be set before doing anything else
20689 if (FAILED(IDirectSound_SetCooperativeLevel(dsoundState->directSound, windowHandle, (targetFormat->preferExclusiveMode) ? DSSCL_EXCLUSIVE : DSSCL_PRIORITY))) {
20690 FPL__DSOUND_INIT_ERROR(fplAudioResultType_Failed, "Failed setting DirectSound Cooperative Level to '%s' mode!", (targetFormat->preferExclusiveMode ? "Exclusive" : "Priority"));
20691 }
20692
20693 // Create primary buffer
20694 DSBUFFERDESC descDSPrimary = fplZeroInit;
20695 descDSPrimary.dwSize = sizeof(DSBUFFERDESC);
20696 descDSPrimary.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
20697 if (FAILED(IDirectSound_CreateSoundBuffer(dsoundState->directSound, &descDSPrimary, &dsoundState->primaryBuffer, fpl_null))) {
20698 FPL__DSOUND_INIT_ERROR(fplAudioResultType_Failed, "Failed creating primary DirectSound sound buffer!");
20699 }
20700
20701 // Set format
20702 if (FAILED(IDirectSoundBuffer_SetFormat(dsoundState->primaryBuffer, (WAVEFORMATEX *)&waveFormat))) {
20703 char subformatString[64];
20704 fpl__Win32FormatGuidString(subformatString, fplArrayCount(subformatString), &waveFormat.SubFormat);
20705 FPL__DSOUND_INIT_ERROR(fplAudioResultType_Failed, "Failed setting audio format for primary sound buffer to (nChannels: %d, nSamplesPerSec: %u, wBitsPerSample: %d, subformat: '%s')!", waveFormat.Format.nChannels, waveFormat.Format.nSamplesPerSec, waveFormat.Format.wBitsPerSample, subformatString);
20706 }
20707
20708 // Get the required size in bytes
20709 DWORD requiredSize;
20710 if (FAILED(IDirectSoundBuffer_GetFormat(dsoundState->primaryBuffer, fpl_null, 0, &requiredSize))) {
20711 FPL__DSOUND_INIT_ERROR(fplAudioResultType_Failed, "Failed getting format size for primary sound buffer!");
20712 }
20713
20714 // Get actual format
20715 char actualFormatData[1024];
20716 WAVEFORMATEXTENSIBLE *actualFormat = (WAVEFORMATEXTENSIBLE *)actualFormatData;
20717 if (FAILED(IDirectSoundBuffer_GetFormat(dsoundState->primaryBuffer, (WAVEFORMATEX *)actualFormat, requiredSize, fpl_null))) {
20718 FPL__DSOUND_INIT_ERROR(fplAudioResultType_Failed, "Failed getting actual wave format from size '{%u}' for primary sound buffer!", requiredSize);
20719 }
20720
20721 // Set internal format
20722 fplAudioDeviceFormat internalFormat = fplZeroInit;
20724 if (fpl__Win32IsEqualGuid(actualFormat->SubFormat, FPL__GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
20725 if (actualFormat->Format.wBitsPerSample == 64) {
20726 internalFormat.type = fplAudioFormatType_F64;
20727 } else {
20728 internalFormat.type = fplAudioFormatType_F32;
20729 }
20730 } else {
20731 switch (actualFormat->Format.wBitsPerSample) {
20732 case 8:
20733 internalFormat.type = fplAudioFormatType_U8;
20734 break;
20735 case 16:
20736 internalFormat.type = fplAudioFormatType_S16;
20737 break;
20738 case 24:
20739 internalFormat.type = fplAudioFormatType_S24;
20740 break;
20741 case 32:
20742 internalFormat.type = fplAudioFormatType_S32;
20743 break;
20744 case 64:
20745 internalFormat.type = fplAudioFormatType_S64;
20746 break;
20747 }
20748 }
20749 internalFormat.channels = actualFormat->Format.nChannels;
20750 internalFormat.sampleRate = actualFormat->Format.nSamplesPerSec;
20751
20752 // @NOTE(final): We divide up our playback buffer into this number of periods and let directsound notify us when one of it needs to play.
20753 internalFormat.periods = fplMax(2, fplMin(targetFormat->periods, 4));
20754 internalFormat.bufferSizeInFrames = targetFormat->bufferSizeInFrames;
20755 uint32_t bufferSizeInBytes = fplGetAudioBufferSizeInBytes(internalFormat.type, internalFormat.channels, internalFormat.bufferSizeInFrames);
20756
20757 commonAudio->internalFormat = internalFormat;
20758
20759 const char *internalFormatTypeName = fplGetAudioFormatName(internalFormat.type);
20760 FPL_LOG(fplLogLevel_Info, FPL__MODULE_AUDIO_DIRECTSOUND,
20761 "Using internal format (Channels: %u, Samplerate: %u, Type: %s, Periods: %u, Buffer size frames/bytes: %u/%u)",
20762 internalFormat.channels,
20763 internalFormat.sampleRate,
20764 internalFormatTypeName,
20765 internalFormat.periods,
20766 internalFormat.bufferSizeInFrames,
20767 bufferSizeInBytes);
20768
20769// Create secondary buffer
20770 DSBUFFERDESC descDS = fplZeroInit;
20771 descDS.dwSize = sizeof(DSBUFFERDESC);
20772 descDS.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
20773 descDS.dwBufferBytes = (DWORD)bufferSizeInBytes;
20774 descDS.lpwfxFormat = (WAVEFORMATEX *)&waveFormat;
20775 if (FAILED(IDirectSound_CreateSoundBuffer(dsoundState->directSound, &descDS, &dsoundState->secondaryBuffer, fpl_null))) {
20776 FPL__DSOUND_INIT_ERROR(fplAudioResultType_Failed, "Failed creating secondary sound buffer with buffer size of '%u' bytes", bufferSizeInBytes);
20777 }
20778
20779 // Notifications are set up via a DIRECTSOUNDNOTIFY object which is retrieved from the buffer.
20780 if (FAILED(IDirectSoundBuffer_QueryInterface(dsoundState->secondaryBuffer, guid_IID_IDirectSoundNotify, (void **)&dsoundState->notify))) {
20781 FPL__DSOUND_INIT_ERROR(fplAudioResultType_Failed, "Failed query direct sound notify interface");
20782 }
20783
20784 // Setup notifications
20785 DWORD periodSizeInBytes = bufferSizeInBytes / internalFormat.periods;
20786 DSBPOSITIONNOTIFY notifyPoints[FPL__DIRECTSOUND_MAX_PERIODS];
20787 for (uint32_t i = 0; i < internalFormat.periods; ++i) {
20788 dsoundState->notifyEvents[i] = CreateEventA(fpl_null, false, false, fpl_null);
20789 if (dsoundState->notifyEvents[i] == fpl_null) {
20790 fpl__AudioReleaseDirectSound(commonAudio, dsoundState);
20792 }
20793
20794 // The notification offset is in bytes.
20795 notifyPoints[i].dwOffset = i * periodSizeInBytes;
20796 notifyPoints[i].hEventNotify = dsoundState->notifyEvents[i];
20797 }
20798 if (FAILED(IDirectSoundNotify_SetNotificationPositions(dsoundState->notify, internalFormat.periods, notifyPoints))) {
20799 FPL__DSOUND_INIT_ERROR(fplAudioResultType_Failed, "Failed setting notification position for %u periods", internalFormat.periods);
20800 }
20801
20802 // Create stop event
20803 dsoundState->stopEvent = CreateEventA(fpl_null, false, false, fpl_null);
20804 if (dsoundState->stopEvent == fpl_null) {
20805 FPL__DSOUND_INIT_ERROR(fplAudioResultType_Failed, "Failed creating stop event");
20806 }
20807
20809}
20810
20811fpl_internal void fpl__AudioStopMainLoopDirectSound(fpl__DirectSoundAudioState *dsoundState) {
20812 dsoundState->breakMainLoop = true;
20813 SetEvent(dsoundState->stopEvent);
20814}
20815
20816fpl_internal bool fpl__GetCurrentFrameDirectSound(const fpl__CommonAudioState *commonAudio, fpl__DirectSoundAudioState *dsoundState, uint32_t *pCurrentPos) {
20817 fplAssert(pCurrentPos != fpl_null);
20818 *pCurrentPos = 0;
20819
20820 fplAssert(dsoundState->secondaryBuffer != fpl_null);
20821 DWORD dwCurrentPosition;
20822 if (FAILED(IDirectSoundBuffer_GetCurrentPosition(dsoundState->secondaryBuffer, fpl_null, &dwCurrentPosition))) {
20823 return false;
20824 }
20825
20826 fplAssert(commonAudio->internalFormat.channels > 0);
20827 *pCurrentPos = (uint32_t)dwCurrentPosition / fplGetAudioSampleSizeInBytes(commonAudio->internalFormat.type) / commonAudio->internalFormat.channels;
20828 return true;
20829}
20830
20831fpl_internal uint32_t fpl__GetAvailableFramesDirectSound(const fpl__CommonAudioState *commonAudio, fpl__DirectSoundAudioState *dsoundState) {
20832 // Get current frame from current play position
20833 uint32_t currentFrame;
20834 if (!fpl__GetCurrentFrameDirectSound(commonAudio, dsoundState, &currentFrame)) {
20835 return 0;
20836 }
20837
20838 // In a playback device the last processed frame should always be ahead of the current frame. The space between
20839 // the last processed and current frame (moving forward, starting from the last processed frame) is the amount
20840 // of space available to write.
20841 uint32_t totalFrameCount = commonAudio->internalFormat.bufferSizeInFrames;
20842 uint32_t committedBeg = currentFrame;
20843 uint32_t committedEnd;
20844 committedEnd = dsoundState->lastProcessedFrame;
20845 if (committedEnd <= committedBeg) {
20846 committedEnd += totalFrameCount;
20847 }
20848
20849 uint32_t committedSize = (committedEnd - committedBeg);
20850 fplAssert(committedSize <= totalFrameCount);
20851
20852 return totalFrameCount - committedSize;
20853}
20854
20855fpl_internal uint32_t fpl__WaitForFramesDirectSound(const fpl__CommonAudioState *commonAudio, fpl__DirectSoundAudioState *dsoundState) {
20856 fplAssert(commonAudio->internalFormat.sampleRate > 0);
20857 fplAssert(commonAudio->internalFormat.periods > 0);
20858
20859 // The timeout to use for putting the thread to sleep is based on the size of the buffer and the period count.
20860 DWORD timeoutInMilliseconds = (commonAudio->internalFormat.bufferSizeInFrames / (commonAudio->internalFormat.sampleRate / 1000)) / commonAudio->internalFormat.periods;
20861 if (timeoutInMilliseconds < 1) {
20862 timeoutInMilliseconds = 1;
20863 }
20864
20865 // Copy event handles so we can wait for each one
20866 unsigned int eventCount = commonAudio->internalFormat.periods + 1;
20867 HANDLE pEvents[FPL__DIRECTSOUND_MAX_PERIODS + 1]; // +1 for the stop event.
20868 fplMemoryCopy(dsoundState->notifyEvents, sizeof(HANDLE) * commonAudio->internalFormat.periods, pEvents);
20869 pEvents[eventCount - 1] = dsoundState->stopEvent;
20870
20871 while (!dsoundState->breakMainLoop) {
20872 // Get available frames from directsound
20873 uint32_t framesAvailable = fpl__GetAvailableFramesDirectSound(commonAudio, dsoundState);
20874 if (framesAvailable > 0) {
20875 return framesAvailable;
20876 }
20877
20878 // If we get here it means we weren't able to find any frames. We'll just wait here for a bit.
20879 WaitForMultipleObjects(eventCount, pEvents, FALSE, timeoutInMilliseconds);
20880 }
20881
20882 // We'll get here if the loop was terminated. Just return whatever's available.
20883 return fpl__GetAvailableFramesDirectSound(commonAudio, dsoundState);
20884}
20885
20886fpl_internal bool fpl__AudioStopDirectSound(fpl__DirectSoundAudioState *dsoundState) {
20887 fplAssert(dsoundState->secondaryBuffer != fpl_null);
20888 if (FAILED(IDirectSoundBuffer_Stop(dsoundState->secondaryBuffer))) {
20889 return false;
20890 }
20891 IDirectSoundBuffer_SetCurrentPosition(dsoundState->secondaryBuffer, 0);
20892 return true;
20893}
20894
20895fpl_internal fplAudioResultType fpl__AudioStartDirectSound(const fpl__CommonAudioState *commonAudio, fpl__DirectSoundAudioState *dsoundState) {
20896 fplAssert(commonAudio->internalFormat.channels > 0);
20897 fplAssert(commonAudio->internalFormat.periods > 0);
20898 uint32_t audioSampleSizeBytes = fplGetAudioSampleSizeInBytes(commonAudio->internalFormat.type);
20899 fplAssert(audioSampleSizeBytes > 0);
20900
20901 // Before playing anything we need to grab an initial group of samples from the client.
20902 uint32_t framesToRead = commonAudio->internalFormat.bufferSizeInFrames / commonAudio->internalFormat.periods;
20903 uint32_t desiredLockSize = framesToRead * commonAudio->internalFormat.channels * audioSampleSizeBytes;
20904
20905 void *pLockPtr;
20906 DWORD actualLockSize;
20907 void *pLockPtr2;
20908 DWORD actualLockSize2;
20909
20910 if (SUCCEEDED(IDirectSoundBuffer_Lock(dsoundState->secondaryBuffer, 0, desiredLockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) {
20911 framesToRead = actualLockSize / audioSampleSizeBytes / commonAudio->internalFormat.channels;
20912 fpl__ReadAudioFramesFromClient(commonAudio, framesToRead, pLockPtr);
20913 IDirectSoundBuffer_Unlock(dsoundState->secondaryBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2);
20914 dsoundState->lastProcessedFrame = framesToRead;
20915 if (FAILED(IDirectSoundBuffer_Play(dsoundState->secondaryBuffer, 0, 0, DSBPLAY_LOOPING))) {
20917 }
20918 } else {
20920 }
20922}
20923
20924fpl_internal void fpl__AudioRunMainLoopDirectSound(const fpl__CommonAudioState *commonAudio, fpl__DirectSoundAudioState *dsoundState) {
20925 fplAssert(commonAudio->internalFormat.channels > 0);
20926 uint32_t audioSampleSizeBytes = fplGetAudioSampleSizeInBytes(commonAudio->internalFormat.type);
20927 fplAssert(audioSampleSizeBytes > 0);
20928
20929 // Make sure the stop event is not signaled to ensure we don't end up immediately returning from WaitForMultipleObjects().
20930 ResetEvent(dsoundState->stopEvent);
20931
20932 // Main loop
20933 dsoundState->breakMainLoop = false;
20934 while (!dsoundState->breakMainLoop) {
20935 // Wait until we get available frames from directsound
20936 uint32_t framesAvailable = fpl__WaitForFramesDirectSound(commonAudio, dsoundState);
20937 if (framesAvailable == 0) {
20938 continue;
20939 }
20940
20941 // Don't bother grabbing more data if the device is being stopped.
20942 if (dsoundState->breakMainLoop) {
20943 break;
20944 }
20945
20946 // Lock playback buffer
20947 DWORD lockOffset = dsoundState->lastProcessedFrame * commonAudio->internalFormat.channels * audioSampleSizeBytes;
20948 DWORD lockSize = framesAvailable * commonAudio->internalFormat.channels * audioSampleSizeBytes;
20949 {
20950 void *pLockPtr;
20951 DWORD actualLockSize;
20952 void *pLockPtr2;
20953 DWORD actualLockSize2;
20954 if (FAILED(IDirectSoundBuffer_Lock(dsoundState->secondaryBuffer, lockOffset, lockSize, &pLockPtr, &actualLockSize, &pLockPtr2, &actualLockSize2, 0))) {
20955 FPL__ERROR(FPL__MODULE_AUDIO_DIRECTSOUND, "Failed to lock directsound secondary buffer '%p' for offset/size (%lu / %lu)", dsoundState->secondaryBuffer, lockOffset, lockSize);
20956 break;
20957 }
20958
20959 // Read actual frames from user
20960 uint32_t frameCount = actualLockSize / audioSampleSizeBytes / commonAudio->internalFormat.channels;
20961 fpl__ReadAudioFramesFromClient(commonAudio, frameCount, pLockPtr);
20962 dsoundState->lastProcessedFrame = (dsoundState->lastProcessedFrame + frameCount) % commonAudio->internalFormat.bufferSizeInFrames;
20963
20964 // Unlock playback buffer
20965 IDirectSoundBuffer_Unlock(dsoundState->secondaryBuffer, pLockPtr, actualLockSize, pLockPtr2, actualLockSize2);
20966 }
20967 }
20968}
20969#endif // FPL__ENABLE_AUDIO_DIRECTSOUND
20970
20971// ############################################################################
20972//
20973// > AUDIO_BACKEND_ALSA
20974//
20975// ############################################################################
20976#if defined(FPL__ENABLE_AUDIO_ALSA)
20977
20978// NOTE(final): ALSA on Raspberry, due to high latency requires large audio buffers, so below we have a table mapped from device names to a scaling factor.
20979typedef struct fpl__AlsaBufferScale {
20980 const char *deviceName;
20981 float scale;
20982} fpl__AlsaBufferScale;
20983
20984fpl_globalvar fpl__AlsaBufferScale fpl__globalAlsaBufferScales[] = {
20985 fplStructInit(fpl__AlsaBufferScale, "*bcm2835*", 2.0f),
20986};
20987
20988fpl_internal float fpl__AlsaGetBufferScale(const char *deviceName) {
20989 if (fplGetStringLength(deviceName) > 0) {
20990 for (int i = 0; i < fplArrayCount(fpl__globalAlsaBufferScales); ++i) {
20991 const char *testDeviceName = fpl__globalAlsaBufferScales[i].deviceName;
20992 if (fplIsStringMatchWildcard(deviceName, testDeviceName)) {
20993 float scale = fpl__globalAlsaBufferScales[i].scale;
20994 return(scale);
20995 }
20996 }
20997 }
20998 return(1.0f);
20999}
21000
21001fpl_internal uint32_t fpl__AlsaScaleBufferSize(const uint32_t bufferSize, const float scale) {
21002 uint32_t result = fplMax(1, (uint32_t)(bufferSize * scale));
21003 return(result);
21004}
21005
21006#if defined(FPL__ANONYMOUS_ALSA_HEADERS)
21007typedef void snd_pcm_t;
21008typedef void snd_pcm_format_mask_t;
21009typedef void snd_pcm_hw_params_t;
21010typedef void snd_pcm_sw_params_t;
21011
21012typedef uint64_t snd_pcm_uframes_t;
21013typedef int64_t snd_pcm_sframes_t;
21014
21015typedef struct snd_pcm_chmap_t {
21016 unsigned int channels;
21017 unsigned int pos[0];
21018} snd_pcm_chmap_t;
21019
21020typedef enum snd_pcm_stream_t {
21021 SND_PCM_STREAM_PLAYBACK = 0,
21022 SND_PCM_STREAM_CAPTURE,
21023} snd_pcm_stream_t;
21024
21025typedef struct snd_pcm_channel_area_t {
21026 void *addr;
21027 unsigned int first;
21028 unsigned int step;
21029} snd_pcm_channel_area_t;
21030
21031typedef enum snd_pcm_format_t {
21032 SND_PCM_FORMAT_UNKNOWN = -1,
21033 SND_PCM_FORMAT_S8 = 0,
21034 SND_PCM_FORMAT_U8,
21035 SND_PCM_FORMAT_S16_LE,
21036 SND_PCM_FORMAT_S16_BE,
21037 SND_PCM_FORMAT_U16_LE,
21038 SND_PCM_FORMAT_U16_BE,
21039 SND_PCM_FORMAT_S24_LE,
21040 SND_PCM_FORMAT_S24_BE,
21041 SND_PCM_FORMAT_U24_LE,
21042 SND_PCM_FORMAT_U24_BE,
21043 SND_PCM_FORMAT_S32_LE,
21044 SND_PCM_FORMAT_S32_BE,
21045 SND_PCM_FORMAT_U32_LE,
21046 SND_PCM_FORMAT_U32_BE,
21047 SND_PCM_FORMAT_FLOAT_LE,
21048 SND_PCM_FORMAT_FLOAT_BE,
21049 SND_PCM_FORMAT_FLOAT64_LE,
21050 SND_PCM_FORMAT_FLOAT64_BE,
21051 SND_PCM_FORMAT_IEC958_SUBFRAME_LE,
21052 SND_PCM_FORMAT_IEC958_SUBFRAME_BE,
21053 SND_PCM_FORMAT_MU_LAW,
21054 SND_PCM_FORMAT_A_LAW,
21055 SND_PCM_FORMAT_IMA_ADPCM,
21056 SND_PCM_FORMAT_MPEG,
21057 SND_PCM_FORMAT_GSM,
21058 SND_PCM_FORMAT_S20_LE,
21059 SND_PCM_FORMAT_S20_BE,
21060 SND_PCM_FORMAT_U20_LE,
21061 SND_PCM_FORMAT_U20_BE,
21062 SND_PCM_FORMAT_SPECIAL,
21063 SND_PCM_FORMAT_S24_3LE,
21064 SND_PCM_FORMAT_S24_3BE,
21065 SND_PCM_FORMAT_U24_3LE,
21066 SND_PCM_FORMAT_U24_3BE,
21067 SND_PCM_FORMAT_S20_3LE,
21068 SND_PCM_FORMAT_S20_3BE,
21069 SND_PCM_FORMAT_U20_3LE,
21070 SND_PCM_FORMAT_U20_3BE,
21071 SND_PCM_FORMAT_S18_3LE,
21072 SND_PCM_FORMAT_S18_3BE,
21073 SND_PCM_FORMAT_U18_3LE,
21074 SND_PCM_FORMAT_U18_3BE,
21075 SND_PCM_FORMAT_S16,
21076 SND_PCM_FORMAT_U16,
21077 SND_PCM_FORMAT_S24,
21078 SND_PCM_FORMAT_U24,
21079 SND_PCM_FORMAT_S32,
21080 SND_PCM_FORMAT_U32,
21081 SND_PCM_FORMAT_FLOAT,
21082 SND_PCM_FORMAT_FLOAT64,
21083 SND_PCM_FORMAT_IEC958_SUBFRAME,
21084 SND_PCM_FORMAT_S20,
21085 SND_PCM_FORMAT_U20
21086} snd_pcm_format_t;
21087
21088typedef enum snd_pcm_access_t {
21089 SND_PCM_ACCESS_MMAP_INTERLEAVED = 0,
21090 SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
21091 SND_PCM_ACCESS_MMAP_COMPLEX,
21092 SND_PCM_ACCESS_RW_INTERLEAVED,
21093 SND_PCM_ACCESS_RW_NONINTERLEAVED
21094} snd_pcm_access_t;
21095
21096#define SND_PCM_NO_AUTO_RESAMPLE 0x00010000
21097#define SND_PCM_NO_AUTO_CHANNELS 0x00020000
21098#define SND_PCM_NO_AUTO_FORMAT 0x00040000
21099#else
21100// @TODO(final/ALSA): Remove ALSA include when runtime linking is enabled
21101# include <alsa/asoundlib.h>
21102#endif // FPL__ANONYMOUS_ALSA_HEADERS
21103
21104#define FPL__ALSA_FUNC_snd_pcm_open(name) int name(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)
21105typedef FPL__ALSA_FUNC_snd_pcm_open(fpl__alsa_func_snd_pcm_open);
21106#define FPL__ALSA_FUNC_snd_pcm_close(name) int name(snd_pcm_t *pcm)
21107typedef FPL__ALSA_FUNC_snd_pcm_close(fpl__alsa_func_snd_pcm_close);
21108#define FPL__ALSA_FUNC_snd_pcm_hw_params_sizeof(name) size_t name(void)
21109typedef FPL__ALSA_FUNC_snd_pcm_hw_params_sizeof(fpl__alsa_func_snd_pcm_hw_params_sizeof);
21110#define FPL__ALSA_FUNC_snd_pcm_hw_params(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
21111typedef FPL__ALSA_FUNC_snd_pcm_hw_params(fpl__alsa_func_snd_pcm_hw_params);
21112#define FPL__ALSA_FUNC_snd_pcm_hw_params_any(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
21113typedef FPL__ALSA_FUNC_snd_pcm_hw_params_any(fpl__alsa_func_snd_pcm_hw_params_any);
21114#define FPL__ALSA_FUNC_snd_pcm_hw_params_set_format(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val)
21115typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_format(fpl__alsa_func_snd_pcm_hw_params_set_format);
21116#define FPL__ALSA_FUNC_snd_pcm_hw_params_set_format_first(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t *format)
21117typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_format_first(fpl__alsa_func_snd_pcm_hw_params_set_format_first);
21118#define FPL__ALSA_FUNC_snd_pcm_hw_params_get_format_mask(name) void name(snd_pcm_hw_params_t *params, snd_pcm_format_mask_t *mask)
21119typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_format_mask(fpl__alsa_func_snd_pcm_hw_params_get_format_mask);
21120#define FPL__ALSA_FUNC_snd_pcm_hw_params_set_channels_near(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val)
21121typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_channels_near(fpl__alsa_func_snd_pcm_hw_params_set_channels_near);
21122#define FPL__ALSA_FUNC_snd_pcm_hw_params_set_rate_resample(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val)
21123typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_rate_resample(fpl__alsa_func_snd_pcm_hw_params_set_rate_resample);
21124#define FPL__ALSA_FUNC_snd_pcm_hw_params_set_rate_near(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
21125typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_rate_near(fpl__alsa_func_snd_pcm_hw_params_set_rate_near);
21126#define FPL__ALSA_FUNC_snd_pcm_hw_params_set_buffer_size_near(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)
21127typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_buffer_size_near(fpl__alsa_func_snd_pcm_hw_params_set_buffer_size_near);
21128#define FPL__ALSA_FUNC_snd_pcm_hw_params_set_periods_near(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
21129typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_periods_near(fpl__alsa_func_snd_pcm_hw_params_set_periods_near);
21130#define FPL__ALSA_FUNC_snd_pcm_hw_params_set_access(name) int name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access)
21131typedef FPL__ALSA_FUNC_snd_pcm_hw_params_set_access(fpl__alsa_func_snd_pcm_hw_params_set_access);
21132#define FPL__ALSA_FUNC_snd_pcm_hw_params_get_format(name) int name(const snd_pcm_hw_params_t *params, snd_pcm_format_t *val)
21133typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_format(fpl__alsa_func_snd_pcm_hw_params_get_format);
21134#define FPL__ALSA_FUNC_snd_pcm_hw_params_get_channels(name) int name(const snd_pcm_hw_params_t *params, unsigned int *val)
21135typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_channels(fpl__alsa_func_snd_pcm_hw_params_get_channels);
21136#define FPL__ALSA_FUNC_snd_pcm_hw_params_get_rate(name) int name(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
21137typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_rate(fpl__alsa_func_snd_pcm_hw_params_get_rate);
21138#define FPL__ALSA_FUNC_snd_pcm_hw_params_get_buffer_size(name) int name(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)
21139typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_buffer_size(fpl__alsa_func_snd_pcm_hw_params_get_buffer_size);
21140#define FPL__ALSA_FUNC_snd_pcm_hw_params_get_periods(name) int name(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
21141typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_periods(fpl__alsa_func_snd_pcm_hw_params_get_periods);
21142#define FPL__ALSA_FUNC_snd_pcm_hw_params_get_access(name) int name(const snd_pcm_hw_params_t *params, snd_pcm_access_t *_access)
21143typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_access(fpl__alsa_func_snd_pcm_hw_params_get_access);
21144#define FPL__ALSA_FUNC_snd_pcm_hw_params_get_sbits(name) int name(const snd_pcm_hw_params_t *params)
21145typedef FPL__ALSA_FUNC_snd_pcm_hw_params_get_sbits(fpl__alsa_func_snd_pcm_hw_params_get_sbits);
21146#define FPL__ALSA_FUNC_snd_pcm_sw_params_sizeof(name) size_t name(void)
21147typedef FPL__ALSA_FUNC_snd_pcm_sw_params_sizeof(fpl__alsa_func_snd_pcm_sw_params_sizeof);
21148#define FPL__ALSA_FUNC_snd_pcm_sw_params_current(name) int name(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
21149typedef FPL__ALSA_FUNC_snd_pcm_sw_params_current(fpl__alsa_func_snd_pcm_sw_params_current);
21150#define FPL__ALSA_FUNC_snd_pcm_sw_params_set_avail_min(name) int name(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val)
21151typedef FPL__ALSA_FUNC_snd_pcm_sw_params_set_avail_min(fpl__alsa_func_snd_pcm_sw_params_set_avail_min);
21152#define FPL__ALSA_FUNC_snd_pcm_sw_params_set_start_threshold(name) int name(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val)
21153typedef FPL__ALSA_FUNC_snd_pcm_sw_params_set_start_threshold(fpl__alsa_func_snd_pcm_sw_params_set_start_threshold);
21154#define FPL__ALSA_FUNC_snd_pcm_sw_params(name) int name(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
21155typedef FPL__ALSA_FUNC_snd_pcm_sw_params(fpl__alsa_func_snd_pcm_sw_params);
21156#define FPL__ALSA_FUNC_snd_pcm_format_mask_sizeof(name) size_t name(void)
21157typedef FPL__ALSA_FUNC_snd_pcm_format_mask_sizeof(fpl__alsa_func_snd_pcm_format_mask_sizeof);
21158#define FPL__ALSA_FUNC_snd_pcm_format_mask_test(name) int name(const snd_pcm_format_mask_t *mask, snd_pcm_format_t val)
21159typedef FPL__ALSA_FUNC_snd_pcm_format_mask_test(fpl__alsa_func_snd_pcm_format_mask_test);
21160#define FPL__ALSA_FUNC_snd_pcm_get_chmap(name) snd_pcm_chmap_t *name(snd_pcm_t *pcm)
21161typedef FPL__ALSA_FUNC_snd_pcm_get_chmap(fpl__alsa_func_snd_pcm_get_chmap);
21162#define FPL__ALSA_FUNC_snd_pcm_prepare(name) int name(snd_pcm_t *pcm)
21163typedef FPL__ALSA_FUNC_snd_pcm_prepare(fpl__alsa_func_snd_pcm_prepare);
21164#define FPL__ALSA_FUNC_snd_pcm_start(name) int name(snd_pcm_t *pcm)
21165typedef FPL__ALSA_FUNC_snd_pcm_start(fpl__alsa_func_snd_pcm_start);
21166#define FPL__ALSA_FUNC_snd_pcm_drop(name) int name(snd_pcm_t *pcm)
21167typedef FPL__ALSA_FUNC_snd_pcm_drop(fpl__alsa_func_snd_pcm_drop);
21168#define FPL__ALSA_FUNC_snd_device_name_hint(name) int name(int card, const char *iface, void ***hints)
21169typedef FPL__ALSA_FUNC_snd_device_name_hint(fpl__alsa_func_snd_device_name_hint);
21170#define FPL__ALSA_FUNC_snd_device_name_get_hint(name) char *name(const void *hint, const char *id)
21171typedef FPL__ALSA_FUNC_snd_device_name_get_hint(fpl__alsa_func_snd_device_name_get_hint);
21172#define FPL__ALSA_FUNC_snd_device_name_free_hint(name) int name(void **hints)
21173typedef FPL__ALSA_FUNC_snd_device_name_free_hint(fpl__alsa_func_snd_device_name_free_hint);
21174#define FPL__ALSA_FUNC_snd_card_get_index(name) int name(const char *name)
21175typedef FPL__ALSA_FUNC_snd_card_get_index(fpl__alsa_func_snd_card_get_index);
21176#define FPL__ALSA_FUNC_snd_pcm_mmap_begin(name) int name(snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames)
21177typedef FPL__ALSA_FUNC_snd_pcm_mmap_begin(fpl__alsa_func_snd_pcm_mmap_begin);
21178#define FPL__ALSA_FUNC_snd_pcm_mmap_commit(name) snd_pcm_sframes_t name(snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames)
21179typedef FPL__ALSA_FUNC_snd_pcm_mmap_commit(fpl__alsa_func_snd_pcm_mmap_commit);
21180#define FPL__ALSA_FUNC_snd_pcm_recover(name) int name(snd_pcm_t *pcm, int err, int silent)
21181typedef FPL__ALSA_FUNC_snd_pcm_recover(fpl__alsa_func_snd_pcm_recover);
21182#define FPL__ALSA_FUNC_snd_pcm_writei(name) snd_pcm_sframes_t name(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
21183typedef FPL__ALSA_FUNC_snd_pcm_writei(fpl__alsa_func_snd_pcm_writei);
21184#define FPL__ALSA_FUNC_snd_pcm_avail(name) snd_pcm_sframes_t name(snd_pcm_t *pcm)
21185typedef FPL__ALSA_FUNC_snd_pcm_avail(fpl__alsa_func_snd_pcm_avail);
21186#define FPL__ALSA_FUNC_snd_pcm_avail_update(name) snd_pcm_sframes_t name(snd_pcm_t *pcm)
21187typedef FPL__ALSA_FUNC_snd_pcm_avail_update(fpl__alsa_func_snd_pcm_avail_update);
21188#define FPL__ALSA_FUNC_snd_pcm_wait(name) int name(snd_pcm_t *pcm, int timeout)
21189typedef FPL__ALSA_FUNC_snd_pcm_wait(fpl__alsa_func_snd_pcm_wait);
21190#define FPL__ALSA_FUNC_snd_pcm_info_sizeof(name) size_t name(void)
21191typedef FPL__ALSA_FUNC_snd_pcm_info_sizeof(fpl__alsa_func_snd_pcm_info_sizeof);
21192#define FPL__ALSA_FUNC_snd_pcm_info(name) int name(snd_pcm_t *handle, snd_pcm_info_t *info)
21193typedef FPL__ALSA_FUNC_snd_pcm_info(fpl__alsa_func_snd_pcm_info);
21194#define FPL__ALSA_FUNC_snd_pcm_info_get_name(name) const char* name(const snd_pcm_info_t *obj)
21195typedef FPL__ALSA_FUNC_snd_pcm_info_get_name(fpl__alsa_func_snd_pcm_info_get_name);
21196
21197typedef struct fpl__AlsaAudioApi {
21198 void *libHandle;
21199 fpl__alsa_func_snd_pcm_open *snd_pcm_open;
21200 fpl__alsa_func_snd_pcm_close *snd_pcm_close;
21201 fpl__alsa_func_snd_pcm_hw_params_sizeof *snd_pcm_hw_params_sizeof;
21202 fpl__alsa_func_snd_pcm_hw_params *snd_pcm_hw_params;
21203 fpl__alsa_func_snd_pcm_hw_params_any *snd_pcm_hw_params_any;
21204 fpl__alsa_func_snd_pcm_hw_params_set_format *snd_pcm_hw_params_set_format;
21205 fpl__alsa_func_snd_pcm_hw_params_set_format_first *snd_pcm_hw_params_set_format_first;
21206 fpl__alsa_func_snd_pcm_hw_params_get_format_mask *snd_pcm_hw_params_get_format_mask;
21207 fpl__alsa_func_snd_pcm_hw_params_set_channels_near *snd_pcm_hw_params_set_channels_near;
21208 fpl__alsa_func_snd_pcm_hw_params_set_rate_resample *snd_pcm_hw_params_set_rate_resample;
21209 fpl__alsa_func_snd_pcm_hw_params_set_rate_near *snd_pcm_hw_params_set_rate_near;
21210 fpl__alsa_func_snd_pcm_hw_params_set_buffer_size_near *snd_pcm_hw_params_set_buffer_size_near;
21211 fpl__alsa_func_snd_pcm_hw_params_set_periods_near *snd_pcm_hw_params_set_periods_near;
21212 fpl__alsa_func_snd_pcm_hw_params_set_access *snd_pcm_hw_params_set_access;
21213 fpl__alsa_func_snd_pcm_hw_params_get_format *snd_pcm_hw_params_get_format;
21214 fpl__alsa_func_snd_pcm_hw_params_get_channels *snd_pcm_hw_params_get_channels;
21215 fpl__alsa_func_snd_pcm_hw_params_get_rate *snd_pcm_hw_params_get_rate;
21216 fpl__alsa_func_snd_pcm_hw_params_get_buffer_size *snd_pcm_hw_params_get_buffer_size;
21217 fpl__alsa_func_snd_pcm_hw_params_get_periods *snd_pcm_hw_params_get_periods;
21218 fpl__alsa_func_snd_pcm_hw_params_get_access *snd_pcm_hw_params_get_access;
21219 fpl__alsa_func_snd_pcm_hw_params_get_sbits *snd_pcm_hw_params_get_sbits;
21220 fpl__alsa_func_snd_pcm_sw_params_sizeof *snd_pcm_sw_params_sizeof;
21221 fpl__alsa_func_snd_pcm_sw_params_current *snd_pcm_sw_params_current;
21222 fpl__alsa_func_snd_pcm_sw_params_set_avail_min *snd_pcm_sw_params_set_avail_min;
21223 fpl__alsa_func_snd_pcm_sw_params_set_start_threshold *snd_pcm_sw_params_set_start_threshold;
21224 fpl__alsa_func_snd_pcm_sw_params *snd_pcm_sw_params;
21225 fpl__alsa_func_snd_pcm_format_mask_sizeof *snd_pcm_format_mask_sizeof;
21226 fpl__alsa_func_snd_pcm_format_mask_test *snd_pcm_format_mask_test;
21227 fpl__alsa_func_snd_pcm_get_chmap *snd_pcm_get_chmap;
21228 fpl__alsa_func_snd_pcm_prepare *snd_pcm_prepare;
21229 fpl__alsa_func_snd_pcm_start *snd_pcm_start;
21230 fpl__alsa_func_snd_pcm_drop *snd_pcm_drop;
21231 fpl__alsa_func_snd_device_name_hint *snd_device_name_hint;
21232 fpl__alsa_func_snd_device_name_get_hint *snd_device_name_get_hint;
21233 fpl__alsa_func_snd_device_name_free_hint *snd_device_name_free_hint;
21234 fpl__alsa_func_snd_card_get_index *snd_card_get_index;
21235 fpl__alsa_func_snd_pcm_mmap_begin *snd_pcm_mmap_begin;
21236 fpl__alsa_func_snd_pcm_mmap_commit *snd_pcm_mmap_commit;
21237 fpl__alsa_func_snd_pcm_recover *snd_pcm_recover;
21238 fpl__alsa_func_snd_pcm_writei *snd_pcm_writei;
21239 fpl__alsa_func_snd_pcm_avail *snd_pcm_avail;
21240 fpl__alsa_func_snd_pcm_avail_update *snd_pcm_avail_update;
21241 fpl__alsa_func_snd_pcm_wait *snd_pcm_wait;
21242 fpl__alsa_func_snd_pcm_info_sizeof *snd_pcm_info_sizeof;
21243 fpl__alsa_func_snd_pcm_info *snd_pcm_info;
21244 fpl__alsa_func_snd_pcm_info_get_name *snd_pcm_info_get_name;
21245} fpl__AlsaAudioApi;
21246
21247typedef struct fpl__AlsaAudioState {
21248 fpl__AlsaAudioApi api;
21249 snd_pcm_t *pcmDevice;
21250 void *intermediaryBuffer;
21251 bool isUsingMMap;
21252 bool breakMainLoop;
21253} fpl__AlsaAudioState;
21254
21255fpl_internal void fpl__UnloadAlsaApi(fpl__AlsaAudioApi *alsaApi) {
21256 fplAssert(alsaApi != fpl_null);
21257 if (alsaApi->libHandle != fpl_null) {
21258 dlclose(alsaApi->libHandle);
21259 }
21260 fplClearStruct(alsaApi);
21261}
21262
21263fpl_internal bool fpl__LoadAlsaApi(fpl__AlsaAudioApi *alsaApi) {
21264 fplAssert(alsaApi != fpl_null);
21265 const char *libraryNames[] = {
21266 "libasound.so",
21267 };
21268 bool result = false;
21269 for (uint32_t index = 0; index < fplArrayCount(libraryNames); ++index) {
21270 const char *libName = libraryNames[index];
21271 fplClearStruct(alsaApi);
21272 do {
21273 void *libHandle = fpl_null;
21274 FPL__POSIX_LOAD_LIBRARY(FPL__MODULE_AUDIO_ALSA, libHandle, libName);
21275 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_open, snd_pcm_open);
21276 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_close, snd_pcm_close);
21277 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_sizeof, snd_pcm_hw_params_sizeof);
21278 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params, snd_pcm_hw_params);
21279 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_any, snd_pcm_hw_params_any);
21280 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_set_format, snd_pcm_hw_params_set_format);
21281 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_set_format_first, snd_pcm_hw_params_set_format_first);
21282 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_get_format_mask, snd_pcm_hw_params_get_format_mask);
21283 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_set_channels_near, snd_pcm_hw_params_set_channels_near);
21284 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_set_rate_resample, snd_pcm_hw_params_set_rate_resample);
21285 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_set_rate_near, snd_pcm_hw_params_set_rate_near);
21286 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_set_buffer_size_near, snd_pcm_hw_params_set_buffer_size_near);
21287 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_set_periods_near, snd_pcm_hw_params_set_periods_near);
21288 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_set_access, snd_pcm_hw_params_set_access);
21289 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_get_format, snd_pcm_hw_params_get_format);
21290 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_get_channels, snd_pcm_hw_params_get_channels);
21291 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_get_rate, snd_pcm_hw_params_get_rate);
21292 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_get_buffer_size, snd_pcm_hw_params_get_buffer_size);
21293 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_get_periods, snd_pcm_hw_params_get_periods);
21294 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_get_access, snd_pcm_hw_params_get_access);
21295 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_hw_params_get_sbits, snd_pcm_hw_params_get_sbits);
21296 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_sw_params_sizeof, snd_pcm_sw_params_sizeof);
21297 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_sw_params_current, snd_pcm_sw_params_current);
21298 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_sw_params_set_avail_min, snd_pcm_sw_params_set_avail_min);
21299 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_sw_params_set_start_threshold, snd_pcm_sw_params_set_start_threshold);
21300 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_sw_params, snd_pcm_sw_params);
21301 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_format_mask_sizeof, snd_pcm_format_mask_sizeof);
21302 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_format_mask_test, snd_pcm_format_mask_test);
21303 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_get_chmap, snd_pcm_get_chmap);
21304 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_prepare, snd_pcm_prepare);
21305 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_start, snd_pcm_start);
21306 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_drop, snd_pcm_drop);
21307 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_device_name_hint, snd_device_name_hint);
21308 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_device_name_get_hint, snd_device_name_get_hint);
21309 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_device_name_free_hint, snd_device_name_free_hint);
21310 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_card_get_index, snd_card_get_index);
21311 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_mmap_begin, snd_pcm_mmap_begin);
21312 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_mmap_commit, snd_pcm_mmap_commit);
21313 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_recover, snd_pcm_recover);
21314 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_writei, snd_pcm_writei);
21315 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_avail, snd_pcm_avail);
21316 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_avail_update, snd_pcm_avail_update);
21317 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_wait, snd_pcm_wait);
21318 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_info_sizeof, snd_pcm_info_sizeof);
21319 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_info, snd_pcm_info);
21320 FPL__POSIX_GET_FUNCTION_ADDRESS(FPL__MODULE_AUDIO_ALSA, libHandle, libName, alsaApi, fpl__alsa_func_snd_pcm_info_get_name, snd_pcm_info_get_name);
21321 alsaApi->libHandle = libHandle;
21322 result = true;
21323 } while (0);
21324 if (result) {
21325 break;
21326 }
21327 fpl__UnloadAlsaApi(alsaApi);
21328 }
21329 return(result);
21330}
21331
21332fpl_internal uint32_t fpl__AudioWaitForFramesAlsa(const fplAudioDeviceFormat *deviceFormat, fpl__AlsaAudioState *alsaState, bool *requiresRestart) {
21333 fplAssert(deviceFormat != fpl_null);
21334 if (requiresRestart != fpl_null) {
21335 *requiresRestart = false;
21336 }
21337 const fpl__AlsaAudioApi *alsaApi = &alsaState->api;
21338 uint32_t periodSizeInFrames = deviceFormat->bufferSizeInFrames / deviceFormat->periods;
21339 while (!alsaState->breakMainLoop) {
21340 snd_pcm_sframes_t framesAvailable = alsaApi->snd_pcm_avail_update(alsaState->pcmDevice);
21341 if (framesAvailable < 0) {
21342 if (framesAvailable == -EPIPE) {
21343 if (alsaApi->snd_pcm_recover(alsaState->pcmDevice, framesAvailable, 1) < 0) {
21344 return 0;
21345 }
21346 if (requiresRestart != fpl_null) {
21347 *requiresRestart = true;
21348 }
21349 framesAvailable = alsaApi->snd_pcm_avail_update(alsaState->pcmDevice);
21350 if (framesAvailable < 0) {
21351 return 0;
21352 }
21353 }
21354 }
21355
21356 // Keep the returned number of samples consistent and based on the period size.
21357 if (framesAvailable >= periodSizeInFrames) {
21358 return periodSizeInFrames;
21359 }
21360
21361 if (framesAvailable < periodSizeInFrames) {
21362 // Less than a whole period is available so keep waiting.
21363 int waitResult = alsaApi->snd_pcm_wait(alsaState->pcmDevice, -1);
21364 if (waitResult < 0) {
21365 if (waitResult == -EPIPE) {
21366 if (alsaApi->snd_pcm_recover(alsaState->pcmDevice, waitResult, 1) < 0) {
21367 return 0;
21368 }
21369 if (requiresRestart != fpl_null) {
21370 *requiresRestart = true;
21371 }
21372 }
21373 }
21374 }
21375 }
21376
21377 // We'll get here if the loop was terminated. Just return whatever's available.
21378 snd_pcm_sframes_t framesAvailable = alsaApi->snd_pcm_avail_update(alsaState->pcmDevice);
21379 if (framesAvailable < 0) {
21380 return 0;
21381 }
21382 return framesAvailable;
21383}
21384
21385fpl_internal bool fpl__GetAudioFramesFromClientAlsa(fpl__CommonAudioState *commonAudio, fpl__AlsaAudioState *alsaState) {
21386 fplAssert(commonAudio != fpl_null && alsaState != fpl_null);
21387 const fpl__AlsaAudioApi *alsaApi = &alsaState->api;
21388
21389 if (!fpl__IsAudioDeviceStarted(commonAudio) && fpl__AudioGetDeviceState(commonAudio) != fpl__AudioDeviceState_Starting) {
21390 return false;
21391 }
21392 if (alsaState->breakMainLoop) {
21393 return false;
21394 }
21395
21396 if (alsaState->isUsingMMap) {
21397 // mmap path
21398 bool requiresRestart;
21399 uint32_t framesAvailable = fpl__AudioWaitForFramesAlsa(&commonAudio->internalFormat, alsaState, &requiresRestart);
21400 if (framesAvailable == 0) {
21401 return false;
21402 }
21403 if (alsaState->breakMainLoop) {
21404 return false;
21405 }
21406
21407 const snd_pcm_channel_area_t *channelAreas;
21408 snd_pcm_uframes_t mappedOffset;
21409 snd_pcm_uframes_t mappedFrames = framesAvailable;
21410 while (framesAvailable > 0) {
21411 int result = alsaApi->snd_pcm_mmap_begin(alsaState->pcmDevice, &channelAreas, &mappedOffset, &mappedFrames);
21412 if (result < 0) {
21413 return false;
21414 }
21415 if (mappedFrames > 0) {
21416 void *bufferPtr = (uint8_t *)channelAreas[0].addr + ((channelAreas[0].first + (mappedOffset * channelAreas[0].step)) / 8);
21417 fpl__ReadAudioFramesFromClient(commonAudio, mappedFrames, bufferPtr);
21418 }
21419 result = alsaApi->snd_pcm_mmap_commit(alsaState->pcmDevice, mappedOffset, mappedFrames);
21420 if (result < 0 || (snd_pcm_uframes_t)result != mappedFrames) {
21421 alsaApi->snd_pcm_recover(alsaState->pcmDevice, result, 1);
21422 return false;
21423 }
21424 if (requiresRestart) {
21425 if (alsaApi->snd_pcm_start(alsaState->pcmDevice) < 0) {
21426 return false;
21427 }
21428 }
21429 if (framesAvailable >= mappedFrames) {
21430 framesAvailable -= mappedFrames;
21431 } else {
21432 framesAvailable = 0;
21433 }
21434
21435 }
21436 } else {
21437 // readi/writei path
21438 while (!alsaState->breakMainLoop) {
21439 uint32_t framesAvailable = fpl__AudioWaitForFramesAlsa(&commonAudio->internalFormat, alsaState, fpl_null);
21440 if (framesAvailable == 0) {
21441 continue;
21442 }
21443 if (alsaState->breakMainLoop) {
21444 return false;
21445 }
21446 fpl__ReadAudioFramesFromClient(commonAudio, framesAvailable, alsaState->intermediaryBuffer);
21447 snd_pcm_sframes_t framesWritten = alsaApi->snd_pcm_writei(alsaState->pcmDevice, alsaState->intermediaryBuffer, framesAvailable);
21448 if (framesWritten < 0) {
21449 if (framesWritten == -EAGAIN) {
21450 // Keep trying
21451 continue;
21452 } else if (framesWritten == -EPIPE) {
21453 // Underrun -> Recover and try again
21454 if (alsaApi->snd_pcm_recover(alsaState->pcmDevice, framesWritten, 1) < 0) {
21455 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to recover device after underrun!");
21456 return false;
21457 }
21458 framesWritten = alsaApi->snd_pcm_writei(alsaState->pcmDevice, alsaState->intermediaryBuffer, framesAvailable);
21459 if (framesWritten < 0) {
21460 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to write data to the PCM device!");
21461 return false;
21462 }
21463 // Success
21464 break;
21465 } else {
21466 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to write audio frames from client, error code: %d!", framesWritten);
21467 return false;
21468 }
21469 } else {
21470 // Success
21471 break;
21472 }
21473 }
21474 }
21475 return true;
21476}
21477
21478fpl_internal void fpl__AudioStopMainLoopAlsa(fpl__AlsaAudioState *alsaState) {
21479 fplAssert(alsaState != fpl_null);
21480 alsaState->breakMainLoop = true;
21481}
21482
21483fpl_internal bool fpl__AudioReleaseAlsa(const fpl__CommonAudioState *commonAudio, fpl__AlsaAudioState *alsaState) {
21484 fplAssert(commonAudio != fpl_null && alsaState != fpl_null);
21485 fpl__AlsaAudioApi *alsaApi = &alsaState->api;
21486 if (alsaState->pcmDevice != fpl_null) {
21487 alsaApi->snd_pcm_close(alsaState->pcmDevice);
21488 alsaState->pcmDevice = fpl_null;
21489 if (alsaState->intermediaryBuffer != fpl_null) {
21490 fpl__ReleaseDynamicMemory(alsaState->intermediaryBuffer);
21491 alsaState->intermediaryBuffer = fpl_null;
21492 }
21493 }
21494 fpl__UnloadAlsaApi(alsaApi);
21495 fplClearStruct(alsaState);
21496 return true;
21497}
21498
21499fpl_internal fplAudioResultType fpl__AudioStartAlsa(fpl__CommonAudioState *commonAudio, fpl__AlsaAudioState *alsaState) {
21500 fplAssert(commonAudio != fpl_null && alsaState != fpl_null);
21501 const fpl__AlsaAudioApi *alsaApi = &alsaState->api;
21502
21503 // Prepare the device
21504 if (alsaApi->snd_pcm_prepare(alsaState->pcmDevice) < 0) {
21505 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to prepare PCM device '%p'!", alsaState->pcmDevice);
21507 }
21508
21509 // Get initial frames to fill from the client
21510 if (!fpl__GetAudioFramesFromClientAlsa(commonAudio, alsaState)) {
21511 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to get initial audio frames from client!");
21513 }
21514
21515 if (alsaState->isUsingMMap) {
21516 if (alsaApi->snd_pcm_start(alsaState->pcmDevice) < 0) {
21517 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to start PCM device '%p'!", alsaState->pcmDevice);
21519 }
21520 }
21521
21523}
21524
21525fpl_internal bool fpl__AudioStopAlsa(fpl__AlsaAudioState *alsaState) {
21526 fplAssert(alsaState != fpl_null);
21527 const fpl__AlsaAudioApi *alsaApi = &alsaState->api;
21528 if (alsaApi->snd_pcm_drop(alsaState->pcmDevice)) {
21529 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Failed to drop the PCM device '%p'!", alsaState->pcmDevice);
21530 return false;
21531 }
21532 return true;
21533}
21534
21535fpl_internal snd_pcm_format_t fpl__MapAudioFormatToAlsaFormat(fplAudioFormatType format) {
21536 // @TODO(final): [ALSA] Mapping table from fplAudioFormatType to snd_pcm_format_t here!
21537 bool isBigEndian = fplIsBigEndian();
21538 if (isBigEndian) {
21539 switch (format) {
21541 return SND_PCM_FORMAT_U8;
21543 return SND_PCM_FORMAT_S16_BE;
21545 return SND_PCM_FORMAT_S24_3BE;
21547 return SND_PCM_FORMAT_S32_BE;
21549 return SND_PCM_FORMAT_FLOAT_BE;
21550 default:
21551 return SND_PCM_FORMAT_UNKNOWN;
21552 }
21553 } else {
21554 switch (format) {
21556 return SND_PCM_FORMAT_U8;
21558 return SND_PCM_FORMAT_S16_LE;
21560 return SND_PCM_FORMAT_S24_3LE;
21562 return SND_PCM_FORMAT_S32_LE;
21564 return SND_PCM_FORMAT_FLOAT_LE;
21565 default:
21566 return SND_PCM_FORMAT_UNKNOWN;
21567 }
21568 }
21569}
21570
21571fpl_internal void fpl__AudioRunMainLoopAlsa(fpl__CommonAudioState *commonAudio, fpl__AlsaAudioState *alsaState) {
21572 fplAssert(alsaState != fpl_null);
21573 alsaState->breakMainLoop = false;
21574 while (!alsaState->breakMainLoop && fpl__GetAudioFramesFromClientAlsa(commonAudio, alsaState)) {
21575 }
21576}
21577
21578fpl_internal fplAudioFormatType fpl__MapAlsaFormatToAudioFormat(snd_pcm_format_t format) {
21579 // @TODO(final): [ALSA] Mapping table from snd_pcm_format_t to fplAudioFormatType here!
21580 switch (format) {
21581 case SND_PCM_FORMAT_U8:
21582 return fplAudioFormatType_U8;
21583 case SND_PCM_FORMAT_S16_BE:
21584 case SND_PCM_FORMAT_S16_LE:
21586 case SND_PCM_FORMAT_S24_3BE:
21587 case SND_PCM_FORMAT_S24_3LE:
21589 case SND_PCM_FORMAT_S32_BE:
21590 case SND_PCM_FORMAT_S32_LE:
21592 case SND_PCM_FORMAT_FLOAT_BE:
21593 case SND_PCM_FORMAT_FLOAT_LE:
21595 default:
21597 }
21598}
21599
21600fpl_internal fplAudioResultType fpl__AudioInitAlsa(const fplAudioSettings *audioSettings, const fplAudioDeviceFormat *targetFormat, fpl__CommonAudioState *commonAudio, fpl__AlsaAudioState *alsaState) {
21601 snd_pcm_hw_params_t *hardwareParams = fpl_null;
21602 snd_pcm_sw_params_t *softwareParams = fpl_null;
21603
21604# define FPL__ALSA_INIT_ERROR(ret, format, ...) do { \
21605 if (softwareParams != fpl_null) fpl__ReleaseTemporaryMemory(softwareParams); \
21606 if (hardwareParams != fpl_null) fpl__ReleaseTemporaryMemory(hardwareParams); \
21607 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, format, ## __VA_ARGS__); \
21608 fpl__AudioReleaseAlsa(commonAudio, alsaState); \
21609 return fplAudioResultType_Failed; \
21610 } while (0)
21611
21612 // Load ALSA library
21613 fpl__AlsaAudioApi *alsaApi = &alsaState->api;
21614 if (!fpl__LoadAlsaApi(alsaApi)) {
21615 FPL__ALSA_INIT_ERROR(fplAudioResultType_ApiFailed, "Failed loading ALSA api!");
21616 }
21617
21618 //
21619 // Open PCM Device
21620 //
21621 fplAudioDeviceInfo deviceInfo = audioSettings->targetDevice;
21622 char deviceName[256] = fplZeroInit;
21623 snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
21624 int openMode = SND_PCM_NO_AUTO_RESAMPLE | SND_PCM_NO_AUTO_CHANNELS | SND_PCM_NO_AUTO_FORMAT;
21625 if (fplGetStringLength(deviceInfo.id.alsa) == 0) {
21626 const char *defaultDeviceNames[16] = fplZeroInit;
21627 int defaultDeviceCount = 0;
21628 defaultDeviceNames[defaultDeviceCount++] = "default";
21629 if (!targetFormat->preferExclusiveMode) {
21630 defaultDeviceNames[defaultDeviceCount++] = "dmix";
21631 defaultDeviceNames[defaultDeviceCount++] = "dmix:0";
21632 defaultDeviceNames[defaultDeviceCount++] = "dmix:0,0";
21633 }
21634 defaultDeviceNames[defaultDeviceCount++] = "hw";
21635 defaultDeviceNames[defaultDeviceCount++] = "hw:0";
21636 defaultDeviceNames[defaultDeviceCount++] = "hw:0,0";
21637
21638 bool isDeviceOpen = false;
21639 for (size_t defaultDeviceIndex = 0; defaultDeviceIndex < defaultDeviceCount; ++defaultDeviceIndex) {
21640 const char *defaultDeviceName = defaultDeviceNames[defaultDeviceIndex];
21641 FPL_LOG_DEBUG("ALSA", "Opening PCM audio device '%s'", defaultDeviceName);
21642 if (alsaApi->snd_pcm_open(&alsaState->pcmDevice, defaultDeviceName, stream, openMode) == 0) {
21643 FPL_LOG_DEBUG("ALSA", "Successfully opened PCM audio device '%s'", defaultDeviceName);
21644 isDeviceOpen = true;
21645 fplCopyString(defaultDeviceName, deviceName, fplArrayCount(deviceName));
21646 break;
21647 } else {
21648 FPL_LOG_ERROR("ALSA", "Failed opening PCM audio device '%s'!", defaultDeviceName);
21649 }
21650 }
21651 if (!isDeviceOpen) {
21652 FPL__ALSA_INIT_ERROR(fplAudioResultType_NoDeviceFound, "No PCM audio device found!");
21653 }
21654 } else {
21655 const char *forcedDeviceId = audioSettings->targetDevice.id.alsa;
21656 // @TODO(final/ALSA): Do we want to allow device ids to be :%d,%d so we can probe "dmix" and "hw" ?
21657 if (alsaApi->snd_pcm_open(&alsaState->pcmDevice, forcedDeviceId, stream, openMode) < 0) {
21658 FPL__ALSA_INIT_ERROR(fplAudioResultType_NoDeviceFound, "PCM audio device by id '%s' not found!", forcedDeviceId);
21659 }
21660 fplCopyString(forcedDeviceId, deviceName, fplArrayCount(deviceName));
21661 }
21662
21663 //
21664 // Buffer sizes
21665 //
21666 // Some audio devices have high latency, so using the default buffer size will not work.
21667 // We have to scale the buffer sizes for special devices, such as broadcom audio (Raspberry Pi)
21668 // See fpl__AlsaGetBufferScale for details
21669 // Idea comes from miniaudio, which does the same thing - so the code is almost identically here
21670 //
21671 float bufferSizeScaleFactor = 1.0f;
21673 size_t pcmInfoSize = alsaApi->snd_pcm_info_sizeof();
21674 snd_pcm_info_t *pcmInfo = (snd_pcm_info_t *)fpl__AllocateTemporaryMemory(pcmInfoSize, 8);
21675 if (pcmInfo == fpl_null) {
21676 FPL__ALSA_INIT_ERROR(fplAudioResultType_OutOfMemory, "Out of stack memory for snd_pcm_info_t!");
21677 }
21678
21679 // Query device name
21680 if (alsaApi->snd_pcm_info(alsaState->pcmDevice, pcmInfo) == 0) {
21681 const char *deviceName = alsaApi->snd_pcm_info_get_name(pcmInfo);
21682 if (deviceName != fpl_null) {
21683 if (fplIsStringEqual("default", deviceName)) {
21684 // The device name "default" is useless for buffer-scaling, so we search for the real device name in the hint-table
21685 char **ppDeviceHints;
21686 if (alsaApi->snd_device_name_hint(-1, "pcm", (void ***)&ppDeviceHints) == 0) {
21687 char **ppNextDeviceHint = ppDeviceHints;
21688
21689 while (*ppNextDeviceHint != fpl_null) {
21690 char *hintName = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "NAME");
21691 char *hintDesc = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "DESC");
21692 char *hintIOID = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "IOID");
21693
21694 bool foundDevice = false;
21695 if (hintIOID == fpl_null || fplIsStringEqual(hintIOID, "Output")) {
21696 if (fplIsStringEqual(hintName, deviceName)) {
21697 // We found the default device and can now get the scale for the description
21698 bufferSizeScaleFactor = fpl__AlsaGetBufferScale(hintDesc);
21699 foundDevice = true;
21700 }
21701 }
21702
21703 // Unfortunatly the hint strings are malloced, so we have to free it :(
21704 free(hintName);
21705 free(hintDesc);
21706 free(hintIOID);
21707
21708 ++ppNextDeviceHint;
21709
21710 if (foundDevice) {
21711 break;
21712 }
21713 }
21714
21715 alsaApi->snd_device_name_free_hint((void **)ppDeviceHints);
21716 }
21717 } else {
21718 bufferSizeScaleFactor = fpl__AlsaGetBufferScale(deviceName);
21719 }
21720 }
21721 }
21722 fpl__ReleaseTemporaryMemory(pcmInfo);
21723 }
21724
21725 //
21726 // Get hardware parameters
21727 //
21728 fplAssert(alsaState->pcmDevice != fpl_null);
21729 fplAssert(fplGetStringLength(deviceName) > 0);
21730
21731 FPL_LOG_DEBUG("ALSA", "Get hardware parameters from device '%s'", deviceName);
21732 size_t hardwareParamsSize = alsaApi->snd_pcm_hw_params_sizeof();
21733 hardwareParams = (snd_pcm_hw_params_t *)fpl__AllocateTemporaryMemory(hardwareParamsSize, 8);
21734 fplMemoryClear(hardwareParams, hardwareParamsSize);
21735 if (alsaApi->snd_pcm_hw_params_any(alsaState->pcmDevice, hardwareParams) < 0) {
21736 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed getting hardware parameters from device '%s'!", deviceName);
21737 }
21738 FPL_LOG_DEBUG("ALSA", "Successfullyy got hardware parameters from device '%s'", deviceName);
21739
21740 //
21741 // Access mode (Interleaved MMap or Standard readi/writei)
21742 //
21743 alsaState->isUsingMMap = false;
21744 if (!audioSettings->specific.alsa.noMMap) {
21745 if (alsaApi->snd_pcm_hw_params_set_access(alsaState->pcmDevice, hardwareParams, SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) {
21746 alsaState->isUsingMMap = true;
21747 } else {
21748 FPL_LOG_ERROR("ALSA", "Failed setting MMap access mode for device '%s', trying fallback to standard mode!", deviceName);
21749 }
21750 }
21751 if (!alsaState->isUsingMMap) {
21752 if (alsaApi->snd_pcm_hw_params_set_access(alsaState->pcmDevice, hardwareParams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
21753 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed setting default access mode for device '%s'!", deviceName);
21754 }
21755 }
21756
21757 fplAudioDeviceFormat internalFormat = fplZeroInit;
21758 internalFormat.backend = fplAudioBackendType_Alsa;
21759
21760 //
21761 // Format
21762 //
21763
21764 // Get all supported formats
21765 size_t formatMaskSize = alsaApi->snd_pcm_format_mask_sizeof();
21766 snd_pcm_format_mask_t *formatMask = (snd_pcm_format_mask_t *)fpl__AllocateTemporaryMemory(formatMaskSize, 8);
21767 fplMemoryClear(formatMask, formatMaskSize);
21768 alsaApi->snd_pcm_hw_params_get_format_mask(hardwareParams, formatMask);
21769
21770 snd_pcm_format_t foundFormat;
21771 snd_pcm_format_t preferredFormat = fpl__MapAudioFormatToAlsaFormat(targetFormat->type);
21772 if (!alsaApi->snd_pcm_format_mask_test(formatMask, preferredFormat)) {
21773 // The required format is not supported. Try a list of default formats.
21774 bool isBigEndian = fplIsBigEndian();
21775 snd_pcm_format_t defaultFormats[] = {
21776 isBigEndian ? SND_PCM_FORMAT_S16_BE : SND_PCM_FORMAT_S16_LE,
21777 isBigEndian ? SND_PCM_FORMAT_FLOAT_BE : SND_PCM_FORMAT_FLOAT_LE,
21778 isBigEndian ? SND_PCM_FORMAT_S32_BE : SND_PCM_FORMAT_S32_LE,
21779 isBigEndian ? SND_PCM_FORMAT_S24_3BE : SND_PCM_FORMAT_S24_3LE,
21780 SND_PCM_FORMAT_U8,
21781 };
21782 foundFormat = SND_PCM_FORMAT_UNKNOWN;
21783 for (size_t defaultFormatIndex = 0; defaultFormatIndex < fplArrayCount(defaultFormats); ++defaultFormatIndex) {
21784 snd_pcm_format_t defaultFormat = defaultFormats[defaultFormatIndex];
21785 if (alsaApi->snd_pcm_format_mask_test(formatMask, defaultFormat)) {
21786 foundFormat = defaultFormat;
21787 break;
21788 }
21789 }
21790 } else {
21791 foundFormat = preferredFormat;
21792 }
21793
21794 fpl__ReleaseTemporaryMemory(formatMask);
21795
21796 if (foundFormat == SND_PCM_FORMAT_UNKNOWN) {
21797 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "No supported audio format for device '%s' found!", deviceName);
21798 }
21799
21800 if (alsaApi->snd_pcm_hw_params_set_format(alsaState->pcmDevice, hardwareParams, foundFormat) < 0) {
21801 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed setting PCM format '%s' for device '%s'!", fplGetAudioFormatName(fpl__MapAlsaFormatToAudioFormat(foundFormat)), deviceName);
21802 }
21803 internalFormat.type = fpl__MapAlsaFormatToAudioFormat(foundFormat);
21804
21805 //
21806 // Channels
21807 //
21808 unsigned int internalChannels = targetFormat->channels;
21809 if (alsaApi->snd_pcm_hw_params_set_channels_near(alsaState->pcmDevice, hardwareParams, &internalChannels) < 0) {
21810 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed setting PCM channels '%lu' for device '%s'!", internalChannels, deviceName);
21811 }
21812 internalFormat.channels = internalChannels;
21813
21814 //
21815 // Sample rate
21816 //
21817
21818 // @NOTE(final): The caller is responsible to convert to the sample rate FPL expects, so we disable any resampling
21819 alsaApi->snd_pcm_hw_params_set_rate_resample(alsaState->pcmDevice, hardwareParams, 0);
21820 unsigned int actualSampleRate = targetFormat->sampleRate;
21821 fplAssert(actualSampleRate > 0);
21822 if (alsaApi->snd_pcm_hw_params_set_rate_near(alsaState->pcmDevice, hardwareParams, &actualSampleRate, 0) < 0) {
21823 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed setting PCM sample rate '%lu' for device '%s'!", actualSampleRate, deviceName);
21824 }
21825 internalFormat.sampleRate = actualSampleRate;
21826
21827 //
21828 // Buffer size + Scaling
21829 //
21830 snd_pcm_uframes_t actualBufferSize;
21832 actualBufferSize = fpl__AlsaScaleBufferSize(targetFormat->bufferSizeInFrames, bufferSizeScaleFactor);
21833 } else {
21834 actualBufferSize = targetFormat->bufferSizeInFrames;
21835 }
21836 fplAssert(actualBufferSize > 0);
21837 if (alsaApi->snd_pcm_hw_params_set_buffer_size_near(alsaState->pcmDevice, hardwareParams, &actualBufferSize) < 0) {
21838 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed setting PCM buffer size '%lu' for device '%s'!", actualBufferSize, deviceName);
21839 }
21840 internalFormat.bufferSizeInFrames = actualBufferSize;
21841
21842 uint32_t bufferSizeInBytes = fplGetAudioBufferSizeInBytes(internalFormat.type, internalFormat.channels, internalFormat.bufferSizeInFrames);
21843
21844 //
21845 // Periods
21846 //
21847 uint32_t internalPeriods = targetFormat->periods;
21848 int periodsDir = 0;
21849 if (alsaApi->snd_pcm_hw_params_set_periods_near(alsaState->pcmDevice, hardwareParams, &internalPeriods, &periodsDir) < 0) {
21850 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed setting PCM periods '%lu' for device '%s'!", internalPeriods, deviceName);
21851 }
21852 internalFormat.periods = internalPeriods;
21853
21854 //
21855 // Hardware parameters
21856 //
21857 if (alsaApi->snd_pcm_hw_params(alsaState->pcmDevice, hardwareParams) < 0) {
21858 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed to install PCM hardware parameters for device '%s'!", deviceName);
21859 }
21860
21861 // Save internal format
21862 commonAudio->internalFormat = internalFormat;
21863
21864 //
21865 // Software parameters
21866 //
21867 size_t softwareParamsSize = alsaApi->snd_pcm_sw_params_sizeof();
21868 softwareParams = (snd_pcm_sw_params_t *)fpl__AllocateTemporaryMemory(softwareParamsSize, 8);
21869 fplMemoryClear(softwareParams, softwareParamsSize);
21870 if (alsaApi->snd_pcm_sw_params_current(alsaState->pcmDevice, softwareParams) < 0) {
21871 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed to get software parameters for device '%s'!", deviceName);
21872 }
21873 snd_pcm_uframes_t minAvailableFrames = fpl__PrevPowerOfTwo(internalFormat.bufferSizeInFrames / internalFormat.periods);
21874 if (alsaApi->snd_pcm_sw_params_set_avail_min(alsaState->pcmDevice, softwareParams, minAvailableFrames) < 0) {
21875 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed to set software available min frames of '%lu' for device '%s'!", minAvailableFrames, deviceName);
21876 }
21877 if (!alsaState->isUsingMMap) {
21878 snd_pcm_uframes_t threshold = internalFormat.bufferSizeInFrames / internalFormat.periods;
21879 if (alsaApi->snd_pcm_sw_params_set_start_threshold(alsaState->pcmDevice, softwareParams, threshold) < 0) {
21880 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed to set start threshold of '%lu' for device '%s'!", threshold, deviceName);
21881 }
21882 }
21883 if (alsaApi->snd_pcm_sw_params(alsaState->pcmDevice, softwareParams) < 0) {
21884 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed to install PCM software parameters for device '%s'!", deviceName);
21885 }
21886
21887 if (!alsaState->isUsingMMap) {
21888 fplAssert(bufferSizeInBytes > 0);
21889 alsaState->intermediaryBuffer = fpl__AllocateDynamicMemory(bufferSizeInBytes, 16);
21890 if (alsaState->intermediaryBuffer == fpl_null) {
21891 FPL__ALSA_INIT_ERROR(fplAudioResultType_Failed, "Failed allocating intermediary buffer of size '%lu' for device '%s'!", bufferSizeInBytes, deviceName);
21892 }
21893 }
21894
21895 // @NOTE(final): We do not support channel mapping right know, so we limit it to mono or stereo
21896 fplAssert(internalFormat.channels <= 2);
21897
21898#undef FPL__ALSA_INIT_ERROR
21899
21900 fpl__ReleaseTemporaryMemory(softwareParams);
21901 fpl__ReleaseTemporaryMemory(hardwareParams);
21902
21904}
21905
21906fpl_internal uint32_t fpl__GetAudioDevicesAlsa(fpl__AlsaAudioState *alsaState, fplAudioDeviceInfo *deviceInfos, uint32_t maxDeviceCount) {
21907 fplAssert(alsaState != fpl_null);
21908 const fpl__AlsaAudioApi *alsaApi = &alsaState->api;
21909 char **ppDeviceHints;
21910 if (alsaApi->snd_device_name_hint(-1, "pcm", (void ***)&ppDeviceHints) < 0) {
21911 return 0;
21912 }
21913 uint32_t capacityOverflow = 0;
21914 uint32_t result = 0;
21915 char **ppNextDeviceHint = ppDeviceHints;
21916 while (*ppNextDeviceHint != fpl_null) {
21917 char *name = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "NAME");
21918 char *ioid = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "IOID");
21919
21920 // Only allow output or default devices
21921 if (name != fpl_null && (fplIsStringEqual(name, "default") || fplIsStringEqual(name, "pulse") || fplIsStringEqual(ioid, "Output"))) {
21922 if (deviceInfos != fpl_null) {
21923 if (result >= maxDeviceCount) {
21924 ++capacityOverflow;
21925 } else {
21926 fplAudioDeviceInfo *outDeviceInfo = deviceInfos + result;
21927 fplClearStruct(outDeviceInfo);
21928 fplCopyString(name, outDeviceInfo->id.alsa, fplArrayCount(outDeviceInfo->id.alsa));
21929 char *desc = alsaApi->snd_device_name_get_hint(*ppNextDeviceHint, "DESC");
21930 if (desc != fpl_null) {
21931 fplCopyString(desc, outDeviceInfo->name, fplArrayCount(outDeviceInfo->name));
21932 free(desc);
21933 } else {
21934 fplCopyString(name, outDeviceInfo->name, fplArrayCount(outDeviceInfo->name));
21935 }
21936 ++result;
21937 }
21938 } else {
21939 ++result;
21940 }
21941 }
21942 if (ioid != fpl_null) {
21943 free(ioid);
21944 }
21945 if (name != fpl_null) {
21946 free(name);
21947 }
21948 ++ppNextDeviceHint;
21949 }
21950 alsaApi->snd_device_name_free_hint((void **)ppDeviceHints);
21951 if (capacityOverflow > 0) {
21952 FPL__ERROR(FPL__MODULE_AUDIO_ALSA, "Capacity of '%lu' for audio device infos has been reached. '%lu' audio devices are not included in the result", maxDeviceCount, capacityOverflow);
21953 }
21954 return(result);
21955}
21956
21957#endif // FPL__ENABLE_AUDIO_ALSA
21958
21959#endif // FPL__AUDIO_BACKENDS_IMPLEMENTED
21960
21961// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
21962//
21963// > SYSTEM_AUDIO_L1 (Audio System, Private Implementation)
21964//
21965// The audio system is based on a stripped down version of "mini_al.h" by David Reid.
21966// See: https://github.com/dr-soft/mini_al
21967//
21968// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
21969#if defined(FPL__ENABLE_AUDIO)
21970
21971#define FPL__AUDIO_RESULT_TYPE_COUNT FPL__ENUM_COUNT(fplAudioResultType_First, fplAudioResultType_Last)
21972fpl_globalvar const char *fpl__global_audioResultTypeNameTable[] = {
21973 "None", // fplAudioResultType_None = 0,
21974 "Success", // fplAudioResultType_Success,
21975 "Audio-Device not initialized",// fplAudioResultType_DeviceNotInitialized,
21976 "Audio-Device already stopped",// fplAudioResultType_DeviceAlreadyStopped,
21977 "Audio-Device already started",// fplAudioResultType_DeviceAlreadyStarted,
21978 "Audio-Device is busy", // fplAudioResultType_DeviceBusy,
21979 "No Audio-Device found", // fplAudioResultType_NoDeviceFound,
21980 "Api failure", // fplAudioResultType_ApiFailed,
21981 "Platform not initialized", // fplAudioResultType_PlatformNotInitialized,
21982 "Backend already initialized", // fplAudioResultType_BackendAlreadyInitialized,
21983 "Audio format was not set", // fplAudioResultType_UnsetAudioFormat,
21984 "Number of audio channels was not set", // fplAudioResultType_UnsetAudioChannels,
21985 "Audio sample rate was not set", // fplAudioResultType_UnsetAudioSampleRate,
21986 "Audio buffer sizes was not set", // fplAudioResultType_UnsetAudioBufferSize,
21987 "Unknown audio failure", // fplAudioResultType_Failed,
21988};
21989fplStaticAssert(fplArrayCount(fpl__global_audioResultTypeNameTable) == FPL__AUDIO_RESULT_TYPE_COUNT);
21990
21991fpl_common_api const char *fplGetAudioResultName(const fplAudioResultType type) {
21992 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(type, fplAudioResultType_First, fplAudioResultType_Last);
21993 const char *result = fpl__global_audioResultTypeNameTable[index];
21994 return(result);
21995}
21996
21997typedef struct fpl__AudioEvent {
21998 fplMutexHandle mutex;
22000 volatile int32_t signaled;
22001} fpl__AudioEvent;
22002
22003fpl_internal bool fpl__InitAudioEvent(fpl__AudioEvent *ev) {
22004 fplClearStruct(ev);
22005 if (!fplMutexInit(&ev->mutex)) {
22006 return(false);
22007 }
22008 if (!fplConditionInit(&ev->cond)) {
22009 return(false);
22010 }
22011 ev->signaled = 0;
22012 return(true);
22013}
22014
22015fpl_internal void fpl__ReleaseAudioEvent(fpl__AudioEvent *ev) {
22016 fplConditionDestroy(&ev->cond);
22017 fplMutexDestroy(&ev->mutex);
22018}
22019
22020fpl_internal void fpl__WaitForAudioEvent(fpl__AudioEvent *ev) {
22021 fplMutexLock(&ev->mutex);
22022 while (!ev->signaled) {
22023 fplConditionWait(&ev->cond, &ev->mutex, FPL_TIMEOUT_INFINITE);
22024 }
22025 ev->signaled = 0;
22026 fplMutexUnlock(&ev->mutex);
22027}
22028
22029fpl_internal void fpl__SetAudioEvent(fpl__AudioEvent *ev) {
22030 fplMutexLock(&ev->mutex);
22031 ev->signaled = 1;
22032 fplConditionSignal(&ev->cond);
22033 fplMutexUnlock(&ev->mutex);
22034}
22035
22036typedef struct fpl__AudioState {
22037 fpl__CommonAudioState common;
22038
22039 fplMutexHandle lock;
22040 fplThreadHandle *workerThread;
22041 fpl__AudioEvent startEvent;
22042 fpl__AudioEvent stopEvent;
22043 fpl__AudioEvent wakeupEvent;
22044 volatile fplAudioResultType workResult;
22045
22046 fplAudioBackendType backendType;
22047 bool isAsyncBackend;
22048
22049 union {
22050# if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
22051 fpl__DirectSoundAudioState dsound;
22052# endif
22053# if defined(FPL__ENABLE_AUDIO_ALSA)
22054 fpl__AlsaAudioState alsa;
22055# endif
22056 };
22057} fpl__AudioState;
22058
22059fpl_internal fpl__AudioState *fpl__GetAudioState(fpl__PlatformAppState *appState) {
22060 fplAssert(appState != fpl_null);
22061 fpl__AudioState *result = fpl_null;
22062 if (appState->audio.mem != fpl_null) {
22063 result = (fpl__AudioState *)appState->audio.mem;
22064 }
22065 return(result);
22066}
22067
22068fpl_internal void fpl__StopAudioDeviceMainLoop(fpl__AudioState *audioState) {
22069 fplAssert(audioState->backendType > fplAudioBackendType_Auto);
22070 switch (audioState->backendType) {
22071
22072# if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
22074 {
22075 fpl__AudioStopMainLoopDirectSound(&audioState->dsound);
22076 } break;
22077# endif
22078
22079# if defined(FPL__ENABLE_AUDIO_ALSA)
22081 {
22082 fpl__AudioStopMainLoopAlsa(&audioState->alsa);
22083 } break;
22084# endif
22085
22086 default:
22087 break;
22088 }
22089}
22090
22091fpl_internal bool fpl__ReleaseAudioDevice(fpl__AudioState *audioState) {
22092 fplAssert(audioState->backendType > fplAudioBackendType_Auto);
22093 bool result = false;
22094 switch (audioState->backendType) {
22095
22096# if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
22098 {
22099 result = fpl__AudioReleaseDirectSound(&audioState->common, &audioState->dsound);
22100 } break;
22101# endif
22102
22103# if defined(FPL__ENABLE_AUDIO_ALSA)
22105 {
22106 result = fpl__AudioReleaseAlsa(&audioState->common, &audioState->alsa);
22107 } break;
22108# endif
22109
22110 default:
22111 break;
22112 }
22113 return (result);
22114}
22115
22116fpl_internal bool fpl__StopAudioDevice(fpl__AudioState *audioState) {
22117 fplAssert(audioState->backendType > fplAudioBackendType_Auto);
22118 bool result = false;
22119 switch (audioState->backendType) {
22120
22121# if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
22123 {
22124 result = fpl__AudioStopDirectSound(&audioState->dsound);
22125 } break;
22126# endif
22127
22128# if defined(FPL__ENABLE_AUDIO_ALSA)
22130 {
22131 result = fpl__AudioStopAlsa(&audioState->alsa);
22132 } break;
22133# endif
22134
22135 default:
22136 break;
22137 }
22138 return (result);
22139}
22140
22141fpl_internal fplAudioResultType fpl__StartAudioDevice(fpl__AudioState *audioState) {
22142 fplAssert(audioState->backendType > fplAudioBackendType_Auto);
22144 switch (audioState->backendType) {
22145
22146# if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
22148 {
22149 result = fpl__AudioStartDirectSound(&audioState->common, &audioState->dsound);
22150 } break;
22151# endif
22152
22153# if defined(FPL__ENABLE_AUDIO_ALSA)
22155 {
22156 result = fpl__AudioStartAlsa(&audioState->common, &audioState->alsa);
22157 } break;
22158# endif
22159
22160 default:
22161 break;
22162 }
22163 return (result);
22164}
22165
22166fpl_internal void fpl__RunAudioDeviceMainLoop(fpl__AudioState *audioState) {
22167 fplAssert(audioState->backendType > fplAudioBackendType_Auto);
22168 switch (audioState->backendType) {
22169
22170# if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
22172 {
22173 fpl__AudioRunMainLoopDirectSound(&audioState->common, &audioState->dsound);
22174 } break;
22175# endif
22176
22177# if defined(FPL__ENABLE_AUDIO_ALSA)
22179 {
22180 fpl__AudioRunMainLoopAlsa(&audioState->common, &audioState->alsa);
22181 } break;
22182# endif
22183
22184 default:
22185 break;
22186 }
22187}
22188
22189fpl_internal bool fpl__IsAudioBackendAsync(const fplAudioBackendType backendType) {
22190 switch (backendType) {
22193 return false;
22194 default:
22195 return false;
22196 }
22197}
22198
22199fpl_internal void fpl__AudioSetDeviceState(fpl__CommonAudioState *audioState, fpl__AudioDeviceState newState) {
22200 fplAtomicStoreU32((volatile uint32_t *)&audioState->state, (uint32_t)newState);
22201}
22202
22203fpl_internal fpl__AudioDeviceState fpl__AudioGetDeviceState(fpl__CommonAudioState *audioState) {
22204 fpl__AudioDeviceState result = (fpl__AudioDeviceState)fplAtomicLoadU32((volatile uint32_t *)&audioState->state);
22205 return(result);
22206}
22207
22208fpl_internal bool fpl__IsAudioDeviceInitialized(fpl__CommonAudioState *audioState) {
22209 if (audioState == fpl_null) {
22210 return false;
22211 }
22212 fpl__AudioDeviceState state = fpl__AudioGetDeviceState(audioState);
22213 return(state != fpl__AudioDeviceState_Uninitialized);
22214}
22215
22216fpl_internal bool fpl__IsAudioDeviceStarted(fpl__CommonAudioState *audioState) {
22217 if (audioState == fpl_null) {
22218 return false;
22219 }
22220 fpl__AudioDeviceState state = fpl__AudioGetDeviceState(audioState);
22221 return(state == fpl__AudioDeviceState_Started);
22222}
22223
22224fpl_internal void fpl__AudioWorkerThread(const fplThreadHandle *thread, void *data) {
22225#if defined(FPL_PLATFORM_WINDOWS)
22226 fplAssert(fpl__global__AppState != fpl_null);
22227 const fpl__Win32Api *wapi = &fpl__global__AppState->win32.winApi;
22228#endif
22229
22230 fpl__AudioState *audioState = (fpl__AudioState *)data;
22231 fpl__CommonAudioState *commonAudioState = &audioState->common;
22232 fplAssert(audioState != fpl_null);
22233 fplAssert(audioState->backendType != fplAudioBackendType_None);
22234
22235#if defined(FPL_PLATFORM_WINDOWS)
22236 wapi->ole.CoInitializeEx(fpl_null, 0);
22237#endif
22238
22239 for (;;) {
22240 // Stop the device at the start of the iteration always
22241 fpl__StopAudioDevice(audioState);
22242
22243 // Let the other threads know that the device has been stopped.
22244 fpl__AudioSetDeviceState(commonAudioState, fpl__AudioDeviceState_Stopped);
22245 fpl__SetAudioEvent(&audioState->stopEvent);
22246
22247 // We wait until the audio device gets wake up
22248 fpl__WaitForAudioEvent(&audioState->wakeupEvent);
22249
22250 // Default result code.
22251 audioState->workResult = fplAudioResultType_Success;
22252
22253 // Just break if we're terminating.
22254 if (fpl__AudioGetDeviceState(commonAudioState) == fpl__AudioDeviceState_Uninitialized) {
22255 break;
22256 }
22257
22258 // Expect that the device is currently be started by the client
22259 fplAssert(fpl__AudioGetDeviceState(commonAudioState) == fpl__AudioDeviceState_Starting);
22260
22261 // Start audio device
22262 audioState->workResult = fpl__StartAudioDevice(audioState);
22263 if (audioState->workResult != fplAudioResultType_Success) {
22264 fpl__SetAudioEvent(&audioState->startEvent);
22265 continue;
22266 }
22267
22268 // The audio device is started, mark it as such
22269 fpl__AudioSetDeviceState(commonAudioState, fpl__AudioDeviceState_Started);
22270 fpl__SetAudioEvent(&audioState->startEvent);
22271
22272 // Enter audio device main loop
22273 fpl__RunAudioDeviceMainLoop(audioState);
22274 }
22275
22276 // Signal to stop any audio threads, in case there are some waiting
22277 fpl__SetAudioEvent(&audioState->stopEvent);
22278
22279#if defined(FPL_PLATFORM_WINDOWS)
22280 wapi->ole.CoUninitialize();
22281#endif
22282}
22283
22284fpl_internal void fpl__ReleaseAudio(fpl__AudioState *audioState) {
22285 fplAssert(audioState != fpl_null);
22286
22287#if defined(FPL_PLATFORM_WINDOWS)
22288 fplAssert(fpl__global__AppState != fpl_null);
22289 const fpl__Win32Api *wapi = &fpl__global__AppState->win32.winApi;
22290#endif
22291
22292 fpl__CommonAudioState *commonAudioState = &audioState->common;
22293
22294 if (fpl__IsAudioDeviceInitialized(commonAudioState)) {
22295
22296 // Wait until the audio device is stopped
22297 if (fpl__IsAudioDeviceStarted(commonAudioState)) {
22299 fplThreadSleep(1);
22300 }
22301 }
22302
22303 // Putting the device into an uninitialized state will make the worker thread return.
22304 fpl__AudioSetDeviceState(commonAudioState, fpl__AudioDeviceState_Uninitialized);
22305
22306 // Wake up the worker thread and wait for it to properly terminate.
22307 fpl__SetAudioEvent(&audioState->wakeupEvent);
22308
22309 fplThreadWaitForOne(audioState->workerThread, FPL_TIMEOUT_INFINITE);
22310 fplThreadTerminate(audioState->workerThread);
22311
22312 // Release signals and thread
22313 fpl__ReleaseAudioEvent(&audioState->stopEvent);
22314 fpl__ReleaseAudioEvent(&audioState->startEvent);
22315 fpl__ReleaseAudioEvent(&audioState->wakeupEvent);
22316 fplMutexDestroy(&audioState->lock);
22317
22318 // Release audio device
22319 fpl__ReleaseAudioDevice(audioState);
22320
22321 // Clear audio state
22322 fplClearStruct(audioState);
22323 }
22324
22325#if defined(FPL_PLATFORM_WINDOWS)
22326 wapi->ole.CoUninitialize();
22327#endif
22328}
22329
22330fpl_internal fplAudioResultType fpl__InitAudio(const fplAudioSettings *audioSettings, fpl__AudioState *audioState) {
22331 fplAssert(audioState != fpl_null);
22332
22333#if defined(FPL_PLATFORM_WINDOWS)
22334 fplAssert(fpl__global__AppState != fpl_null);
22335 const fpl__Win32Api *wapi = &fpl__global__AppState->win32.winApi;
22336#endif
22337
22338 if (audioState->backendType != fplAudioBackendType_None) {
22339 fpl__ReleaseAudio(audioState);
22341 }
22342
22343 fplAudioDeviceFormat actualTargetFormat = fplZeroInit;
22344 fplConvertAudioTargetFormatToDeviceFormat(&audioSettings->targetFormat, &actualTargetFormat);
22345
22346 audioState->common.clientReadCallback = audioSettings->clientReadCallback;
22347 audioState->common.clientUserData = audioSettings->userData;
22348
22349#if defined(FPL_PLATFORM_WINDOWS)
22350 wapi->ole.CoInitializeEx(fpl_null, 0);
22351#endif
22352
22353 // Create mutex and signals
22354 if (!fplMutexInit(&audioState->lock)) {
22355 fpl__ReleaseAudio(audioState);
22357 }
22358 if (!fpl__InitAudioEvent(&audioState->wakeupEvent)) {
22359 fpl__ReleaseAudio(audioState);
22361 }
22362 if (!fpl__InitAudioEvent(&audioState->startEvent)) {
22363 fpl__ReleaseAudio(audioState);
22365 }
22366 if (!fpl__InitAudioEvent(&audioState->stopEvent)) {
22367 fpl__ReleaseAudio(audioState);
22369 }
22370
22371 // Prope backends
22372 fplAudioBackendType propeBackendTypes[16];
22373 uint32_t backendCount = 0;
22374 if (audioSettings->backend == fplAudioBackendType_Auto) {
22375 // @NOTE(final): Add all audio backends here, regardless of the platform.
22376 propeBackendTypes[backendCount++] = fplAudioBackendType_DirectSound;
22377 propeBackendTypes[backendCount++] = fplAudioBackendType_Alsa;
22378 } else {
22379 // @NOTE(final): Forced audio backend
22380 propeBackendTypes[backendCount++] = audioSettings->backend;
22381 }
22383 for (uint32_t backendIndex = 0; backendIndex < backendCount; ++backendIndex) {
22384 fplAudioBackendType propeBackendType = propeBackendTypes[backendIndex];
22385
22386 initResult = fplAudioResultType_Failed;
22387 switch (propeBackendType) {
22388# if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
22390 {
22391 initResult = fpl__AudioInitDirectSound(audioSettings, &actualTargetFormat, &audioState->common, &audioState->dsound);
22392 if (initResult != fplAudioResultType_Success) {
22393 fpl__AudioReleaseDirectSound(&audioState->common, &audioState->dsound);
22394 }
22395 } break;
22396# endif
22397
22398# if defined(FPL__ENABLE_AUDIO_ALSA)
22400 {
22401 initResult = fpl__AudioInitAlsa(audioSettings, &actualTargetFormat, &audioState->common, &audioState->alsa);
22402 if (initResult != fplAudioResultType_Success) {
22403 fpl__AudioReleaseAlsa(&audioState->common, &audioState->alsa);
22404 }
22405 } break;
22406# endif
22407
22408 default:
22409 break;
22410 }
22411 if (initResult == fplAudioResultType_Success) {
22412 audioState->backendType = propeBackendType;
22413 audioState->isAsyncBackend = fpl__IsAudioBackendAsync(propeBackendType);
22414 break;
22415 }
22416 }
22417
22418 if (initResult != fplAudioResultType_Success) {
22419 fpl__ReleaseAudio(audioState);
22420 return initResult;
22421 }
22422
22423 if (!audioState->isAsyncBackend) {
22424 // Create and start worker thread
22425 fplThreadParameters audioThreadParams = fplZeroInit;
22426 audioThreadParams.priority = fplThreadPriority_RealTime;
22427 audioThreadParams.runFunc = fpl__AudioWorkerThread;
22428 audioThreadParams.userData = audioState;
22429 audioState->workerThread = fplThreadCreateWithParameters(&audioThreadParams);
22430 if (audioState->workerThread == fpl_null) {
22431 fpl__ReleaseAudio(audioState);
22433 }
22434 // Change to realtime thread
22435 fplSetThreadPriority(audioState->workerThread, fplThreadPriority_RealTime);
22436 // Wait for the worker thread to put the device into the stopped state.
22437 fpl__WaitForAudioEvent(&audioState->stopEvent);
22438 } else {
22439 fpl__AudioSetDeviceState(&audioState->common, fpl__AudioDeviceState_Stopped);
22440 }
22441
22442 fplAssert(fpl__AudioGetDeviceState(&audioState->common) == fpl__AudioDeviceState_Stopped);
22443
22445}
22446#endif // FPL__ENABLE_AUDIO
22447
22448// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22449//
22450// > SYSTEM_VIDEO_L1 (Video System, Private Implementation)
22451//
22452// The audio system is based on a stripped down version of "mini_al.h" by David Reid.
22453// See: https://github.com/dr-soft/mini_al
22454//
22455// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22456#if defined(FPL__ENABLE_VIDEO)
22457
22458typedef union fpl__ActiveVideoBackend {
22459 fpl__VideoBackend base;
22460
22461#if defined(FPL_PLATFORM_WINDOWS)
22462# if defined(FPL__ENABLE_VIDEO_OPENGL)
22463 fpl__VideoBackendWin32OpenGL win32_opengl;
22464# endif
22465# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
22466 fpl__VideoBackendWin32Software win32_software;
22467# endif
22468#endif
22469
22470#if defined(FPL_SUBPLATFORM_X11)
22471# if defined(FPL__ENABLE_VIDEO_OPENGL)
22472 fpl__VideoBackendX11OpenGL x11_opengl;
22473# endif
22474# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
22475 fpl__VideoBackendX11Software x11_software;
22476# endif
22477#endif
22478
22479#if defined(FPL__ENABLE_VIDEO_VULKAN)
22480 fpl__VideoBackendVulkan vulkan;
22481#endif
22482
22483} fpl__ActiveVideoBackend;
22484
22485typedef struct fpl__VideoState {
22486 fpl__VideoContext context;
22487 fpl__VideoData data;
22488 fplVideoBackendType backendType;
22489 fpl__ActiveVideoBackend activeBackend;
22490} fpl__VideoState;
22491
22492fpl_internal fpl__VideoState *fpl__GetVideoState(fpl__PlatformAppState *appState) {
22493 fplAssert(appState != fpl_null);
22494 fpl__VideoState *result = fpl_null;
22495 if (appState->video.mem != fpl_null) {
22496 result = (fpl__VideoState *)appState->video.mem;
22497 }
22498 return(result);
22499}
22500
22501fpl_internal void fpl__DestroySurfaceBackend(fpl__PlatformAppState *appState, fpl__VideoState *videoState) {
22502 const fpl__VideoContext *ctx = &videoState->context;
22503 fplAssert(ctx->destroyedWindowFunc != fpl_null);
22504 ctx->destroyedWindowFunc(appState, &videoState->activeBackend.base);
22505}
22506
22507fpl_internal void fpl__UnloadVideoBackend(fpl__PlatformAppState *appState, fpl__VideoState *videoState) {
22508 const fpl__VideoContext *ctx = &videoState->context;
22509 fplAssert(ctx->unloadFunc != fpl_null);
22510 ctx->unloadFunc(appState, &videoState->activeBackend.base);
22511 fplClearStruct(videoState);
22512}
22513
22514fpl_internal bool fpl__LoadVideoBackend(fpl__PlatformAppState *appState, fpl__VideoState *videoState) {
22515 const fpl__VideoContext *ctx = &videoState->context;
22516 fplAssert(ctx->loadFunc != fpl_null);
22517 bool result = ctx->loadFunc(appState, &videoState->activeBackend.base);
22518 return(result);
22519}
22520
22521fpl_internal void fpl__ShutdownVideoBackend(fpl__PlatformAppState *appState, fpl__VideoState *videoState) {
22522 fplAssert(appState != fpl_null);
22523 if (videoState != fpl_null) {
22524 const fpl__VideoContext *ctx = &videoState->context;
22525 fplAssert(ctx->shutdownFunc != fpl_null);
22526 ctx->shutdownFunc(appState, &appState->window, &videoState->activeBackend.base);
22527
22528# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
22529 fplVideoBackBuffer *backbuffer = &videoState->data.backbuffer;
22530 if (backbuffer->pixels != fpl_null) {
22531 fpl__ReleaseDynamicMemory(backbuffer->pixels);
22532 }
22533 fplClearStruct(backbuffer);
22534# endif
22535 }
22536}
22537
22538fpl_internal bool fpl__InitializeVideoBackend(const fplVideoBackendType backendType, const fplVideoSettings *videoSettings, const uint32_t windowWidth, const uint32_t windowHeight, fpl__PlatformAppState *appState, fpl__VideoState *videoState) {
22539 // @NOTE(final): video backends are platform independent, so we cannot have to same system as audio.
22540 fplAssert(appState != fpl_null);
22541 fplAssert(videoState != fpl_null);
22542
22543 const fpl__VideoContext *ctx = &videoState->context;
22544
22545 // Allocate backbuffer context if needed
22546# if defined(FPL__ENABLE_VIDEO_SOFTWARE)
22547 if (backendType == fplVideoBackendType_Software) {
22548 fplVideoBackBuffer *backbuffer = &videoState->data.backbuffer;
22549 backbuffer->width = windowWidth;
22550 backbuffer->height = windowHeight;
22551 backbuffer->pixelStride = sizeof(uint32_t);
22552 backbuffer->lineWidth = backbuffer->width * backbuffer->pixelStride;
22553 size_t size = backbuffer->lineWidth * backbuffer->height;
22554 backbuffer->pixels = (uint32_t *)fpl__AllocateDynamicMemory(size, 4);
22555 if (backbuffer->pixels == fpl_null) {
22556 FPL__ERROR(FPL__MODULE_VIDEO_SOFTWARE, "Failed allocating video software backbuffer of size %xu bytes", size);
22557 fpl__ShutdownVideoBackend(appState, videoState);
22558 return false;
22559 }
22560
22561 // Clear to black by default
22562 // @NOTE(final): Bitmap is top-down, 0xAABBGGRR
22563 uint32_t *p = backbuffer->pixels;
22564 uint32_t color = appState->initSettings.window.background.value == 0 ? 0xFF000000 : appState->initSettings.window.background.value;
22565 for (uint32_t y = 0; y < backbuffer->height; ++y) {
22566 for (uint32_t x = 0; x < backbuffer->width; ++x) {
22567 *p++ = color;
22568 }
22569 }
22570 }
22571# endif // FPL__ENABLE_VIDEO_SOFTWARE
22572
22573 fplAssert(ctx->initializeFunc != fpl_null);
22574 bool videoInitResult = ctx->initializeFunc(appState, &appState->window, videoSettings, &videoState->data, &videoState->activeBackend.base);
22575 if (!videoInitResult) {
22577 fpl__ShutdownVideoBackend(appState, videoState);
22578 return false;
22579 }
22580
22581 return true;
22582}
22583
22584fpl_internal fpl__VideoContext fpl__ConstructVideoContext(const fplVideoBackendType backendType) {
22585 switch (backendType) {
22586#if defined(FPL__ENABLE_VIDEO_OPENGL)
22588 {
22589# if defined(FPL_PLATFORM_WINDOWS)
22590 return fpl__VideoBackend_Win32OpenGL_Construct();
22591# elif defined(FPL_SUBPLATFORM_X11)
22592 return fpl__VideoBackend_X11OpenGL_Construct();
22593# endif
22594 } break;
22595#endif
22596
22597#if defined(FPL__ENABLE_VIDEO_VULKAN)
22599 {
22600 return fpl__VideoBackend_Vulkan_Construct();
22601 } break;
22602#endif
22603
22604#if defined(FPL__ENABLE_VIDEO_SOFTWARE)
22606 {
22607# if defined(FPL_PLATFORM_WINDOWS)
22608 return fpl__VideoBackend_Win32Software_Construct();
22609# elif defined(FPL_SUBPLATFORM_X11)
22610 return fpl__VideoBackend_X11Software_Construct();
22611# endif
22612 } break;
22613#endif
22614
22615 default:
22616 {
22617 // No backend found, just return a stub
22618 FPL__ERROR(FPL__MODULE_VIDEO, "The video backend '%s' is not supported for this platform", fplGetVideoBackendName(backendType));
22619 return(fpl__StubVideoContext());
22620 } break;
22621 }
22622}
22623#endif // FPL__ENABLE_VIDEO
22624
22625// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22626//
22627// > SYSTEM_WINDOW (Window System, Private Implementation)
22628//
22629// - Init window
22630// - Release window
22631//
22632// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22633#if defined(FPL__ENABLE_WINDOW)
22634fpl_internal FPL__FUNC_PREPARE_VIDEO_WINDOW(fpl__PrepareVideoWindowDefault) {
22635 fplAssert(appState != fpl_null);
22636
22637# if defined(FPL__ENABLE_VIDEO)
22638 if (initFlags & fplInitFlags_Video) {
22639 fpl__VideoState *videoState = fpl__GetVideoState(appState);
22640 if (videoState->context.prepareWindowFunc != fpl_null) {
22641 bool result = videoState->context.prepareWindowFunc(appState, &initSettings->video, &appState->window, &videoState->activeBackend.base);
22642 return(result);
22643 }
22644 }
22645# endif // FPL__ENABLE_VIDEO
22646
22647 return(true);
22648}
22649
22650fpl_internal FPL__FUNC_FINALIZE_VIDEO_WINDOW(fpl__FinalizeVideoWindowDefault) {
22651 fplAssert(appState != fpl_null);
22652
22653#if defined(FPL__ENABLE_VIDEO)
22654 if (initFlags & fplInitFlags_Video) {
22655 fpl__VideoState *videoState = fpl__GetVideoState(appState);
22656 if (videoState->context.finalizeWindowFunc != fpl_null) {
22657 bool result = videoState->context.finalizeWindowFunc(appState, &initSettings->video, &appState->window, &videoState->activeBackend.base);
22658 return(result);
22659 }
22660 }
22661#endif // FPL__ENABLE_VIDEO
22662
22663 return true;
22664}
22665
22666fpl_internal void fpl__ReleaseWindow(const fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
22667 if (appState != fpl_null) {
22668# if defined(FPL_PLATFORM_WINDOWS)
22669 fpl__Win32ReleaseWindow(&initState->win32, &appState->win32, &appState->window.win32);
22670# elif defined(FPL_SUBPLATFORM_X11)
22671 fpl__X11ReleaseWindow(&appState->x11, &appState->window.x11);
22672# endif
22673 }
22674}
22675
22676fpl_internal bool fpl__InitWindow(const fplSettings *initSettings, fplWindowSettings *currentWindowSettings, fpl__PlatformAppState *appState, const fpl__SetupWindowCallbacks *setupCallbacks) {
22677 bool result = false;
22678 if (appState != fpl_null) {
22679# if defined(FPL_PLATFORM_WINDOWS)
22680 result = fpl__Win32InitWindow(initSettings, currentWindowSettings, appState, &appState->win32, &appState->window.win32, setupCallbacks);
22681# elif defined(FPL_SUBPLATFORM_X11)
22682 result = fpl__X11InitWindow(initSettings, currentWindowSettings, appState, &appState->x11, &appState->window.x11, setupCallbacks);
22683# endif
22684 }
22685 return (result);
22686}
22687#endif // FPL__ENABLE_WINDOW
22688
22689// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22690//
22691// > SYSTEM_AUDIO_L2 (Audio System, Public Implementation)
22692//
22693// - Stop audio
22694// - Play audio
22695//
22696// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22697#if defined(FPL__ENABLE_AUDIO)
22698
22699#define FPL__AUDIOFORMATTYPE_COUNT FPL__ENUM_COUNT(fplAudioFormatType_First, fplAudioFormatType_Last)
22700
22701fpl_globalvar uint32_t fpl__globalAudioFormatSampleSizeTable[] = {
22702 0, // No audio format
22703 1, // Unsigned 8-bit integer PCM
22704 2, // Signed 16-bit integer PCM
22705 3, // Signed 24-bit integer PCM
22706 4, // Signed 32-bit integer PCM
22707 8, // Signed 64-bit integer PCM
22708 4, // 32-bit IEEE_FLOAT
22709 8, // 64-bit IEEE_FLOAT
22710};
22711fplStaticAssert(fplArrayCount(fpl__globalAudioFormatSampleSizeTable) == FPL__AUDIOFORMATTYPE_COUNT);
22712
22713fpl_globalvar const char *fpl__globalAudioFormatNameTable[] = {
22714 "None", // 0 = No audio format
22715 "U8", // = Unsigned 8-bit integer PCM
22716 "S16", // = Signed 16-bit integer PCM
22717 "S24", // = Signed 24-bit integer PCM
22718 "S32", // = Signed 32-bit integer PCM
22719 "S64", // = Signed 64-bit integer PCM
22720 "F32", // = 32-bit IEEE_FLOAT
22721 "F64", // = 64-bit IEEE_FLOAT
22722};
22723fplStaticAssert(fplArrayCount(fpl__globalAudioFormatNameTable) == FPL__AUDIOFORMATTYPE_COUNT);
22724
22726 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(format, fplAudioFormatType_First, fplAudioFormatType_Last);
22727 uint32_t result = fpl__globalAudioFormatSampleSizeTable[index];
22728 return(result);
22729}
22730
22732 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(format, fplAudioFormatType_First, fplAudioFormatType_Last);
22733 const char *result = fpl__globalAudioFormatNameTable[index];
22734 return(result);
22735}
22736
22737#define FPL__AUDIOBACKENDTYPE_COUNT FPL__ENUM_COUNT(fplAudioBackendType_First, fplAudioBackendType_Last)
22738fpl_globalvar const char *fpl__globalAudioBackendNameTable[] = {
22739 "None", // No audio backend
22740 "Automatic", // Automatic backend detection
22741 "DirectSound", // DirectSound
22742 "ALSA", // Alsa
22743};
22744fplStaticAssert(fplArrayCount(fpl__globalAudioBackendNameTable) == FPL__AUDIOBACKENDTYPE_COUNT);
22745
22747 FPL__CheckPlatform(fplAudioBackendType_None);
22748 const fpl__PlatformAppState *appState = fpl__global__AppState;
22749 fplAudioBackendType result = appState->currentSettings.audio.backend;
22750 return(result);
22751}
22752
22754 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(backendType, fplAudioBackendType_First, fplAudioBackendType_Last);
22755 const char *result = fpl__globalAudioBackendNameTable[index];
22756 return(result);
22757}
22758
22759fpl_common_api uint32_t fplGetAudioBufferSizeInFrames(uint32_t sampleRate, uint32_t bufferSizeInMilliSeconds) {
22760 if (sampleRate == 0 || bufferSizeInMilliSeconds == 0) return(0);
22761 uint32_t result = bufferSizeInMilliSeconds * sampleRate / 1000UL;
22762 return(result);
22763}
22764
22765fpl_common_api uint32_t fplGetAudioBufferSizeInMilliseconds(uint32_t sampleRate, uint32_t frameCount) {
22766 if (sampleRate == 0 || frameCount == 0) return(0);
22767 uint32_t result = frameCount * 1000UL / sampleRate;
22768 return(result);
22769}
22770
22771fpl_common_api uint32_t fplGetAudioFrameSizeInBytes(const fplAudioFormatType format, const uint32_t channelCount) {
22772 if (channelCount == 0) return(0);
22773 uint32_t result = fplGetAudioSampleSizeInBytes(format) * channelCount;
22774 return(result);
22775}
22776
22777fpl_common_api uint32_t fplGetAudioBufferSizeInBytes(const fplAudioFormatType format, const uint32_t channelCount, const uint32_t frameCount) {
22778 if (channelCount == 0 || frameCount == 0) return(0);
22779 uint32_t frameSize = fplGetAudioFrameSizeInBytes(format, channelCount);
22780 uint32_t result = frameSize * frameCount;
22781 return(result);
22782}
22783
22785 FPL__CheckArgumentNullNoRet(inFormat);
22786 FPL__CheckArgumentNullNoRet(outFormat);
22787
22788 fplClearStruct(outFormat);
22789
22790 // Channels
22791 if (inFormat->channels > 0) {
22792 outFormat->channels = inFormat->channels;
22793 } else {
22794 outFormat->channels = FPL__DEFAULT_AUDIO_CHANNELS;
22796 }
22797
22798 // Sample rate
22799 if (inFormat->sampleRate > 0) {
22800 outFormat->sampleRate = inFormat->sampleRate;
22801 } else {
22802 outFormat->sampleRate = FPL__DEFAULT_AUDIO_SAMPLERATE;
22804 }
22805
22806 // Format
22807 if (outFormat->type != fplAudioFormatType_None) {
22808 outFormat->type = inFormat->type;
22809 } else {
22810 outFormat->type = FPL__DEFAULT_AUDIO_FORMAT;
22812 }
22813
22814 // Periods
22815 if (outFormat->periods > 0) {
22816 outFormat->periods = inFormat->periods;
22817 } else {
22818 outFormat->periods = FPL__DEFAULT_AUDIO_PERIODS;
22820 }
22821
22822 // Buffer size
22823 if (inFormat->bufferSizeInFrames > 0) {
22824 outFormat->bufferSizeInFrames = inFormat->bufferSizeInFrames;
22825 } else if (inFormat->bufferSizeInMilliseconds > 0) {
22827 } else {
22828 uint32_t bufferSizeInMilliseconds = (inFormat->latencyMode == fplAudioLatencyMode_Conservative) ? FPL__DEFAULT_AUDIO_BUFFERSIZE_CONSERVATIVE_IN_MSECS : FPL__DEFAULT_AUDIO_BUFFERSIZE_LOWLATENCY_IN_MSECS;
22829 outFormat->bufferSizeInFrames = fplGetAudioBufferSizeInFrames(inFormat->sampleRate, bufferSizeInMilliseconds);
22831 }
22832
22833 // Exclusive mode
22834 outFormat->preferExclusiveMode = inFormat->preferExclusiveMode;
22835}
22836
22839 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
22840 if (audioState == fpl_null) {
22842 }
22843
22844 fpl__CommonAudioState *commonAudioState = &audioState->common;
22845
22846 if (!fpl__IsAudioDeviceInitialized(commonAudioState)) {
22848 }
22849
22850 fpl__AudioDeviceState firstDeviceState = fpl__AudioGetDeviceState(commonAudioState);
22851 if (firstDeviceState == fpl__AudioDeviceState_Stopped) {
22853 }
22854
22856 fplMutexLock(&audioState->lock);
22857 {
22858 // Check if the device is already stopped
22859 if (fpl__AudioGetDeviceState(commonAudioState) == fpl__AudioDeviceState_Stopping) {
22860 fplMutexUnlock(&audioState->lock);
22862 }
22863 if (fpl__AudioGetDeviceState(commonAudioState) == fpl__AudioDeviceState_Stopped) {
22864 fplMutexUnlock(&audioState->lock);
22866 }
22867
22868 // The device needs to be in a started state. If it's not, we just let the caller know the device is busy.
22869 if (fpl__AudioGetDeviceState(commonAudioState) != fpl__AudioDeviceState_Started) {
22870 fplMutexUnlock(&audioState->lock);
22872 }
22873
22874 fpl__AudioSetDeviceState(commonAudioState, fpl__AudioDeviceState_Stopping);
22875
22876 if (audioState->isAsyncBackend) {
22877 // Asynchronous backends (Has their own thread)
22878 fpl__StopAudioDevice(audioState);
22879 } else {
22880 // Synchronous backends
22881
22882 // The audio worker thread is most likely in a wait state, so let it stop properly.
22883 fpl__StopAudioDeviceMainLoop(audioState);
22884
22885 // We need to wait for the worker thread to become available for work before returning.
22886 // @NOTE(final): The audio worker thread will be the one who puts the device into the stopped state.
22887 fpl__WaitForAudioEvent(&audioState->stopEvent);
22889 }
22890 }
22891 fplMutexUnlock(&audioState->lock);
22892
22893 return result;
22894}
22895
22898 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
22899 if (audioState == fpl_null) {
22901 }
22902
22903 fpl__CommonAudioState *commonAudioState = &audioState->common;
22904
22905 if (!fpl__IsAudioDeviceInitialized(commonAudioState)) {
22907 }
22908
22909 if (fpl__AudioGetDeviceState(commonAudioState) == fpl__AudioDeviceState_Started) {
22911 }
22912
22914 fplMutexLock(&audioState->lock);
22915 {
22916 // If device is already in started/starting state we cannot start playback of it
22917 if (fpl__AudioGetDeviceState(commonAudioState) == fpl__AudioDeviceState_Starting) {
22918 fplMutexUnlock(&audioState->lock);
22920 }
22921 if (fpl__AudioGetDeviceState(commonAudioState) == fpl__AudioDeviceState_Started) {
22922 fplMutexUnlock(&audioState->lock);
22924 }
22925
22926 // The device needs to be in a stopped state. If it's not, we just let the caller know the device is busy.
22927 if (fpl__AudioGetDeviceState(commonAudioState) != fpl__AudioDeviceState_Stopped) {
22928 fplMutexUnlock(&audioState->lock);
22930 }
22931
22932 fpl__AudioSetDeviceState(commonAudioState, fpl__AudioDeviceState_Starting);
22933
22934 if (audioState->isAsyncBackend) {
22935 // Asynchronous backends (Has their own thread)
22936 fpl__StartAudioDevice(audioState);
22937 fpl__AudioSetDeviceState(commonAudioState, fpl__AudioDeviceState_Started);
22938 } else {
22939 // Synchronous backends
22940 fpl__SetAudioEvent(&audioState->wakeupEvent);
22941
22942 // Wait for the worker thread to finish starting the device.
22943 // @NOTE(final): The audio worker thread will be the one who puts the device into the started state.
22944 fpl__WaitForAudioEvent(&audioState->startEvent);
22945 result = audioState->workResult;
22946 }
22947 }
22948 fplMutexUnlock(&audioState->lock);
22949
22950 return result;
22951}
22952
22954 FPL__CheckArgumentNull(outFormat, false);
22955 FPL__CheckPlatform(false);
22956 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
22957 if (audioState != fpl_null) {
22958 fplClearStruct(outFormat);
22959 *outFormat = audioState->common.internalFormat;
22960 return true;
22961 }
22962 return false;
22963}
22964
22966 FPL__CheckPlatform(false);
22967 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
22968 if ((audioState != fpl_null) && (audioState->backendType > fplAudioBackendType_Auto)) {
22969 if (fpl__AudioGetDeviceState(&audioState->common) == fpl__AudioDeviceState_Stopped) {
22970 audioState->common.clientReadCallback = newCallback;
22971 audioState->common.clientUserData = userData;
22972 return true;
22973 }
22974 }
22975 return false;
22976}
22977
22978fpl_common_api uint32_t fplGetAudioDevices(fplAudioDeviceInfo *devices, uint32_t maxDeviceCount) {
22979 if (devices != fpl_null) {
22980 FPL__CheckArgumentZero(maxDeviceCount, 0);
22981 }
22982 FPL__CheckPlatform(0);
22983 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
22984 if (audioState == fpl_null) {
22985 return 0;
22986 }
22987 uint32_t result = 0;
22988 if (audioState->backendType > fplAudioBackendType_Auto) {
22989 switch (audioState->backendType) {
22990# if defined(FPL__ENABLE_AUDIO_DIRECTSOUND)
22992 {
22993 result = fpl__GetAudioDevicesDirectSound(&audioState->dsound, devices, maxDeviceCount);
22994 } break;
22995# endif
22996
22997# if defined(FPL__ENABLE_AUDIO_ALSA)
22999 {
23000 result = fpl__GetAudioDevicesAlsa(&audioState->alsa, devices, maxDeviceCount);
23001 } break;
23002# endif
23003
23004 default:
23005 break;
23006 }
23007 }
23008 return(result);
23009}
23010#endif // FPL__ENABLE_AUDIO
23011
23012// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
23013//
23014// > SYSTEM_VIDEO_L2 (Video System, Public Implementation)
23015//
23016// - Video Backbuffer Access
23017// - Frame flipping
23018// - Utiltity functions
23019//
23020// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
23021#if defined(FPL__ENABLE_VIDEO)
23022#define FPL__VIDEOBACKENDTYPE_COUNT FPL__ENUM_COUNT(fplVideoBackendType_First, fplVideoBackendType_Last)
23023fpl_globalvar const char *fpl__globalVideoBackendNameTable[FPL__VIDEOBACKENDTYPE_COUNT] = {
23024 "None", // fplVideoBackendType_None
23025 "Software", // fplVideoBackendType_Software
23026 "OpenGL", // fplVideoBackendType_OpenGL
23027 "Vulkan", // fplVideoBackendType_Vulkan
23028};
23029
23031 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(backendType, fplVideoBackendType_First, fplVideoBackendType_Last);
23032 const char *result = fpl__globalVideoBackendNameTable[index];
23033 return(result);
23034}
23035
23037 FPL__CheckPlatform(fplVideoBackendType_None);
23038 fpl__PlatformAppState *appState = fpl__global__AppState;
23039 fpl__VideoState *videoState = fpl__GetVideoState(appState);
23040 if (videoState != fpl_null) {
23041 return(videoState->backendType);
23042 }
23044}
23045
23047 FPL__CheckPlatform(fpl_null);
23048 fpl__PlatformAppState *appState = fpl__global__AppState;
23049 fpl__VideoState *videoState = fpl__GetVideoState(appState);
23050 fplVideoBackBuffer *result = fpl_null;
23051 if (videoState != fpl_null) {
23052#if defined(FPL__ENABLE_VIDEO_SOFTWARE)
23053 if (appState->currentSettings.video.backend == fplVideoBackendType_Software) {
23054 result = &videoState->data.backbuffer;
23055 }
23056#endif
23057 }
23058 return(result);
23059}
23060
23061fpl_common_api bool fplResizeVideoBackBuffer(const uint32_t width, const uint32_t height) {
23062 FPL__CheckPlatform(false);
23063 fpl__PlatformAppState *appState = fpl__global__AppState;
23064 fpl__VideoState *videoState = fpl__GetVideoState(appState);
23065 bool result = false;
23066 if (videoState != fpl_null) {
23067 fplVideoBackendType backendType = videoState->backendType;
23068 if (backendType != fplVideoBackendType_None && videoState->context.recreateOnResize) {
23069 fpl__ShutdownVideoBackend(appState, videoState);
23070 result = fpl__InitializeVideoBackend(videoState->backendType, &appState->currentSettings.video, width, height, appState, videoState);
23071 }
23072 }
23073 return (result);
23074}
23075
23077 FPL__CheckPlatformNoRet();
23078 fpl__PlatformAppState *appState = fpl__global__AppState;
23079 const fpl__VideoState *videoState = fpl__GetVideoState(appState);
23080 if (videoState != fpl_null && videoState->backendType != fplVideoBackendType_None) {
23081 fplAssert(videoState->context.presentFunc != fpl_null);
23082 videoState->context.presentFunc(appState, &appState->window, &videoState->data, &videoState->activeBackend.base);
23083 }
23084}
23085
23086fpl_common_api const void *fplGetVideoProcedure(const char *procName) {
23087 FPL__CheckPlatform(fpl_null);
23088 fpl__PlatformAppState *appState = fpl__global__AppState;
23089 const fpl__VideoState *videoState = fpl__GetVideoState(appState);
23090 const void *result = fpl_null;
23091 if (videoState != fpl_null && videoState->backendType != fplVideoBackendType_None) {
23092 fplAssert(videoState->context.getProcedureFunc != fpl_null);
23093 result = videoState->context.getProcedureFunc(&videoState->activeBackend.base, procName);
23094 }
23095 return(result);
23096}
23097
23099 FPL__CheckPlatform(fpl_null);
23100 fpl__PlatformAppState *appState = fpl__global__AppState;
23101 const fpl__VideoState *videoState = fpl__GetVideoState(appState);
23102 const fplVideoSurface *result = fpl_null;
23103 if (videoState != fpl_null && videoState->backendType != fplVideoBackendType_None) {
23104 result = &videoState->activeBackend.base.surface;
23105 }
23106 return(result);
23107}
23108
23110 fpl__VideoContext context = fpl__ConstructVideoContext(backendType);
23111 bool result = false;
23112 if (context.getRequirementsFunc != fpl_null) {
23113 result = context.getRequirementsFunc(requirements);
23114 }
23115 return(result);
23116}
23117#endif // FPL__ENABLE_VIDEO
23118
23119// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
23120//
23121// > SYSTEM_INIT
23122//
23123// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
23124#if !defined(FPL__SYSTEM_INIT_DEFINED)
23125#define FPL__SYSTEM_INIT_DEFINED
23126
23127fpl_internal void fpl__ReleasePlatformStates(fpl__PlatformInitState *initState, fpl__PlatformAppState *appState) {
23128 fplAssert(initState != fpl_null);
23129
23130 // Release audio
23131# if defined(FPL__ENABLE_AUDIO)
23132 {
23133 // Auto-Stop audio if enabled
23134 if (appState->currentSettings.audio.stopAuto) {
23135 fpl__AudioState *audioState = fpl__GetAudioState(fpl__global__AppState);
23136 if (audioState != fpl_null) {
23137 fpl__CommonAudioState *commonAudioState = &audioState->common;
23138 fpl__AudioDeviceState deviceState = fpl__AudioGetDeviceState(commonAudioState);
23139 if (deviceState != fpl__AudioDeviceState_Stopped) {
23140 FPL_LOG_DEBUG("Core", "Stop Audio (Auto)");
23141 fplStopAudio();
23142 }
23143 }
23144 }
23145
23146 FPL_LOG_DEBUG("Core", "Release Audio");
23147 fpl__AudioState *audioState = fpl__GetAudioState(appState);
23148 if (audioState != fpl_null) {
23149 fpl__ReleaseAudio(audioState);
23150 }
23151 }
23152# endif
23153
23154 // Shutdown video backend
23155# if defined(FPL__ENABLE_VIDEO)
23156 {
23157 fpl__VideoState *videoState = fpl__GetVideoState(appState);
23158 if (videoState != fpl_null) {
23159 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Shutdown Video Backend '%s'", fplGetVideoBackendName(videoState->backendType));
23160 fpl__ShutdownVideoBackend(appState, videoState);
23161 }
23162 }
23163# endif
23164
23165 // Release window
23166# if defined(FPL__ENABLE_WINDOW)
23167 {
23168 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Window");
23169 fpl__ReleaseWindow(initState, appState);
23170 fpl__ClearInternalEvents();
23171 }
23172# endif
23173
23174 // Release video backend
23175# if defined(FPL__ENABLE_VIDEO)
23176 {
23177 fpl__VideoState *videoState = fpl__GetVideoState(appState);
23178 if (videoState != fpl_null) {
23179 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Destroy surface for Video Backend '%s'", fplGetVideoBackendName(videoState->backendType));
23180 fpl__DestroySurfaceBackend(appState, videoState);
23181
23182 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Video Backend '%s'", fplGetVideoBackendName(videoState->backendType));
23183 fpl__UnloadVideoBackend(appState, videoState);
23184 }
23185 }
23186# endif
23187
23188 if (appState != fpl_null) {
23189 // Release actual platform (There can only be one platform!)
23190 {
23191# if defined(FPL_PLATFORM_WINDOWS)
23192 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Win32 Platform");
23193 fpl__Win32ReleasePlatform(initState, appState);
23194# elif defined(FPL_PLATFORM_LINUX)
23195 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Linux Platform");
23196 fpl__LinuxReleasePlatform(initState, appState);
23197# elif defined(FPL_PLATFORM_UNIX)
23198 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Unix Platform");
23199 fpl__UnixReleasePlatform(initState, appState);
23200# endif
23201 }
23202
23203 // Release sub platforms
23204 {
23205# if defined(FPL_SUBPLATFORM_X11)
23206 FPL_LOG_DEBUG("Core", "Release X11 Subplatform");
23207 fpl__X11ReleaseSubplatform(&appState->x11);
23208# endif
23209# if defined(FPL_SUBPLATFORM_POSIX)
23210 FPL_LOG_DEBUG("Core", "Release POSIX Subplatform");
23211 fpl__PosixReleaseSubplatform(&appState->posix);
23212# endif
23213 }
23214
23215 // Release platform applicatiom state memory
23216 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release allocated Platform App State Memory");
23217 fplMemoryAlignedFree(appState);
23218 fpl__global__AppState = fpl_null;
23219 }
23220
23221 fplClearStruct(initState);
23222}
23223
23224#define FPL__PLATFORMTYPE_COUNT FPL__ENUM_COUNT(fplPlatformType_First, fplPlatformType_Last)
23225fpl_globalvar const char *fpl__globalPlatformTypeNameTable[] = {
23226 "Unknown", // fplPlatformType_Unknown
23227 "Windows", // fplPlatformType_Windows
23228 "Linux", // fplPlatformType_Linux
23229 "Unix", // fplPlatformType_Unix
23230};
23231fplStaticAssert(fplArrayCount(fpl__globalPlatformTypeNameTable) == FPL__PLATFORMTYPE_COUNT);
23232
23234 fpl__PlatformInitState *initState = &fpl__global__InitState;
23235 bool result = initState->isInitialized;
23236 return(result);
23237}
23238
23239fpl_common_api const char *fplGetPlatformName(const fplPlatformType type) {
23240 uint32_t index = FPL__ENUM_VALUE_TO_ARRAY_INDEX(type, fplPlatformType_First, fplPlatformType_Last);
23241 const char *result = fpl__globalPlatformTypeNameTable[index];
23242 return(result);
23243}
23244
23246 fpl__PlatformInitState *initState = &fpl__global__InitState;
23247 return(initState->initResult);
23248}
23249
23250fpl_internal bool fpl__SetPlatformResult(const fplPlatformResultType resultType) {
23251 fpl__PlatformInitState *initState = &fpl__global__InitState;
23252 initState->initResult = resultType;
23253 return(initState->initResult == fplPlatformResultType_Success);
23254}
23255
23257 // Exit out if platform is not initialized
23258 fpl__PlatformInitState *initState = &fpl__global__InitState;
23259 if (!initState->isInitialized) {
23260 FPL__CRITICAL(FPL__MODULE_CORE, "Platform is not initialized");
23261 return;
23262 }
23263 fpl__PlatformAppState *appState = fpl__global__AppState;
23264 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Release Platform");
23265 fpl__ReleasePlatformStates(initState, appState);
23266 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Platform released");
23267}
23268
23269fpl_common_api bool fplPlatformInit(const fplInitFlags initFlags, const fplSettings *initSettings) {
23270 // Exit out if platform is already initialized
23271 if (fpl__global__InitState.isInitialized) {
23272 FPL__CRITICAL(FPL__MODULE_CORE, "Platform is already initialized");
23273 return(fpl__SetPlatformResult(fplPlatformResultType_AlreadyInitialized));
23274 }
23275
23276 fpl__PlatformInitState *initState = &fpl__global__InitState;
23277 fplClearStruct(initState);
23278
23279 // Copy over init settings, such as memory allocation settings, etc.
23280 if (initSettings != fpl_null) {
23281 fplCopyStruct(&initSettings->memory, &initState->initSettings.memorySettings);
23282 }
23283
23284 // Allocate platform app state memory (By boundary of 16-bytes)
23285 size_t platformAppStateSize = fplGetAlignedSize(sizeof(fpl__PlatformAppState), 16);
23286
23287 // Include video/audio state memory in app state memory as well
23288#if defined(FPL__ENABLE_VIDEO)
23289 size_t videoMemoryOffset = 0;
23290 if (initFlags & fplInitFlags_Video) {
23291 platformAppStateSize += FPL__ARBITARY_PADDING;
23292 videoMemoryOffset = platformAppStateSize;
23293 platformAppStateSize += sizeof(fpl__VideoState);
23294 }
23295#endif
23296
23297#if defined(FPL__ENABLE_AUDIO)
23298 size_t audioMemoryOffset = 0;
23299 if (initFlags & fplInitFlags_Audio) {
23300 platformAppStateSize += FPL__ARBITARY_PADDING;
23301 audioMemoryOffset = platformAppStateSize;
23302 platformAppStateSize += sizeof(fpl__AudioState);
23303 }
23304#endif
23305
23306 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Allocate Platform App State Memory of size '%zu':", platformAppStateSize);
23307 fplAssert(fpl__global__AppState == fpl_null);
23308 void *platformAppStateMemory = fplMemoryAlignedAllocate(platformAppStateSize, 16);
23309 if (platformAppStateMemory == fpl_null) {
23310 FPL__CRITICAL(FPL__MODULE_CORE, "Failed Allocating Platform App State Memory of size '%zu'", platformAppStateSize);
23311 return(fpl__SetPlatformResult(fplPlatformResultType_FailedAllocatingMemory));
23312 }
23313
23314 fpl__PlatformAppState *appState = fpl__global__AppState = (fpl__PlatformAppState *)platformAppStateMemory;
23315 appState->initFlags = initFlags;
23316 if (initSettings != fpl_null) {
23317 appState->initSettings = *initSettings;
23318 } else {
23319 fplSetDefaultSettings(&appState->initSettings);
23320 }
23321 appState->currentSettings = appState->initSettings;
23322
23323 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully allocated Platform App State Memory of size '%zu'", platformAppStateSize);
23324
23325 // Application types
23326# if defined(FPL_APPTYPE_WINDOW)
23327 appState->initFlags |= fplInitFlags_Window;
23328# elif defined(FPL_APPTYPE_CONSOLE)
23329 appState->initFlags |= fplInitFlags_Console;
23330# endif
23331
23332 // Force the inclusion of window when Video flags is set or remove the video flags when video is disabled
23333# if defined(FPL__ENABLE_VIDEO)
23334 if (appState->initFlags & fplInitFlags_Video) {
23335 appState->initFlags |= fplInitFlags_Window;
23336 }
23337# else
23338 appState->initFlags = (fplInitFlags)(appState->initFlags & ~fplInitFlags_Video);
23339# endif
23340
23341 // Window flag are removed when windowing is disabled
23342# if !defined(FPL__ENABLE_WINDOW)
23343 appState->initFlags = (fplInitFlags)(appState->initFlags & ~fplInitFlags_Window);
23344# endif
23345
23346 // Initialize sub-platforms
23347# if defined(FPL_SUBPLATFORM_POSIX)
23348 {
23349 FPL_LOG_DEBUG("Core", "Initialize POSIX Subplatform:");
23350 if (!fpl__PosixInitSubplatform(initFlags, initSettings, &initState->posix, &appState->posix)) {
23351 FPL__CRITICAL("Core", "Failed initializing POSIX Subplatform!");
23352 fpl__ReleasePlatformStates(initState, appState);
23353 return(fpl__SetPlatformResult(fplPlatformResultType_FailedPlatform));
23354 }
23355 FPL_LOG_DEBUG("Core", "Successfully initialized POSIX Subplatform");
23356 }
23357# endif // FPL_SUBPLATFORM_POSIX
23358
23359# if defined(FPL_SUBPLATFORM_X11)
23360 {
23361 FPL_LOG_DEBUG("Core", "Initialize X11 Subplatform:");
23362 if (!fpl__X11InitSubplatform(&appState->x11)) {
23363 FPL__CRITICAL("Core", "Failed initializing X11 Subplatform!");
23364 fpl__ReleasePlatformStates(initState, appState);
23365 return(fpl__SetPlatformResult(fplPlatformResultType_FailedPlatform));
23366 }
23367 FPL_LOG_DEBUG("Core", "Successfully initialized X11 Subplatform");
23368 }
23369# endif // FPL_SUBPLATFORM_X11
23370
23371 // Initialize the actual platform (There can only be one at a time!)
23372 bool isInitialized = false;
23373 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Initialize %s Platform:", FPL_PLATFORM_NAME);
23374# if defined(FPL_PLATFORM_WINDOWS)
23375 isInitialized = fpl__Win32InitPlatform(appState->initFlags, &appState->initSettings, initState, appState);
23376# elif defined(FPL_PLATFORM_LINUX)
23377 isInitialized = fpl__LinuxInitPlatform(appState->initFlags, &appState->initSettings, initState, appState);
23378# elif defined(FPL_PLATFORM_UNIX)
23379 isInitialized = fpl__UnixInitPlatform(appState->initFlags, &appState->initSettings, initState, appState);
23380# endif
23381
23382 if (!isInitialized) {
23383 FPL__CRITICAL(FPL__MODULE_CORE, "Failed initializing %s Platform!", FPL_PLATFORM_NAME);
23384 fpl__ReleasePlatformStates(initState, appState);
23385 return(fpl__SetPlatformResult(fplPlatformResultType_FailedPlatform));
23386 }
23387 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully initialized %s Platform", FPL_PLATFORM_NAME);
23388
23389 // Init video state
23390# if defined(FPL__ENABLE_VIDEO)
23391 if (appState->initFlags & fplInitFlags_Video) {
23392 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Init Video State");
23393 appState->video.mem = (uint8_t *)platformAppStateMemory + videoMemoryOffset;
23394 appState->video.memSize = sizeof(fpl__VideoState);
23395 fpl__VideoState *videoState = fpl__GetVideoState(appState);
23396 fplAssert(videoState != fpl_null);
23397
23398 fplVideoBackendType videoBackendType = appState->initSettings.video.backend;
23399
23400 // Construct video context (Function table)
23401 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Construct Video Context:");
23402 videoState->context = fpl__ConstructVideoContext(videoBackendType);
23403 videoState->backendType = videoBackendType;
23404
23405 // Load video backend (API)
23406 const char *videoBackendName = fplGetVideoBackendName(videoBackendType);
23407 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Load Video API for Backend '%s':", videoBackendName);
23408 {
23409 if (!fpl__LoadVideoBackend(appState, videoState)) {
23410 FPL__CRITICAL(FPL__MODULE_CORE, "Failed loading Video API for Backend '%s'!", videoBackendName);
23411 fpl__ReleasePlatformStates(initState, appState);
23412 return(fpl__SetPlatformResult(fplPlatformResultType_FailedVideo));
23413 }
23414 }
23415 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully loaded Video API for Backend '%s'", videoBackendName);
23416 }
23417# endif // FPL__ENABLE_VIDEO
23418
23419 // Init Window & event queue
23420# if defined(FPL__ENABLE_WINDOW)
23421 if (appState->initFlags & fplInitFlags_Window) {
23422 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Init Window:");
23423 fpl__SetupWindowCallbacks winCallbacks = fplZeroInit;
23424 winCallbacks.preSetup = fpl__PrepareVideoWindowDefault;
23425 winCallbacks.postSetup = fpl__FinalizeVideoWindowDefault;
23426 if (!fpl__InitWindow(&appState->initSettings, &appState->currentSettings.window, appState, &winCallbacks)) {
23427 FPL__CRITICAL(FPL__MODULE_CORE, "Failed initializing Window!");
23428 fpl__ReleasePlatformStates(initState, appState);
23429 return(fpl__SetPlatformResult(fplPlatformResultType_FailedWindow));
23430 }
23431 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully initialized Window");
23432 }
23433# endif // FPL__ENABLE_WINDOW
23434
23435 // Init Video
23436# if defined(FPL__ENABLE_VIDEO)
23437 if (appState->initFlags & fplInitFlags_Video) {
23438 fpl__VideoState *videoState = fpl__GetVideoState(appState);
23439 fplAssert(videoState != fpl_null);
23440 fplWindowSize windowSize = fplZeroInit;
23441 fplGetWindowSize(&windowSize);
23442 const char *videoBackendName = fplGetVideoBackendName(appState->initSettings.video.backend);
23443 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Init Video with Backend '%s':", videoBackendName);
23444 if (!fpl__InitializeVideoBackend(appState->initSettings.video.backend, &appState->initSettings.video, windowSize.width, windowSize.height, appState, videoState)) {
23445 FPL__CRITICAL(FPL__MODULE_CORE, "Failed initialization Video with Backend '%s' with settings (Width=%d, Height=%d)", videoBackendName, windowSize.width, windowSize.height);
23446 fpl__ReleasePlatformStates(initState, appState);
23447 return(fpl__SetPlatformResult(fplPlatformResultType_FailedVideo));
23448 }
23449 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully initialized Video with Backend '%s'", videoBackendName);
23450 }
23451# endif // FPL__ENABLE_VIDEO
23452
23453 // Init Audio
23454# if defined(FPL__ENABLE_AUDIO)
23455 if (appState->initFlags & fplInitFlags_Audio) {
23456 appState->audio.mem = (uint8_t *)platformAppStateMemory + audioMemoryOffset;
23457 appState->audio.memSize = sizeof(fpl__AudioState);
23458 const char *audioBackendName = fplGetAudioBackendName(appState->initSettings.audio.backend);
23459 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Init Audio with Backend '%s':", audioBackendName);
23460 fpl__AudioState *audioState = fpl__GetAudioState(appState);
23461 fplAssert(audioState != fpl_null);
23462 fplAudioResultType initAudioResult = fpl__InitAudio(&appState->initSettings.audio, audioState);
23463 if (initAudioResult != fplAudioResultType_Success) {
23464 const char *initAudioResultName = fplGetAudioResultName(initAudioResult);
23465 const char *audioFormatName = fplGetAudioFormatName(initSettings->audio.targetFormat.type);
23466 FPL__CRITICAL(FPL__MODULE_CORE, "Failed initialization audio with Backend '%s' settings (Format=%s, SampleRate=%d, Channels=%d) -> %s",
23467 audioBackendName,
23468 audioFormatName,
23469 initSettings->audio.targetFormat.sampleRate,
23470 initSettings->audio.targetFormat.channels,
23471 initAudioResultName);
23472 fpl__ReleasePlatformStates(initState, appState);
23473 return(fpl__SetPlatformResult(fplPlatformResultType_FailedAudio));
23474 }
23475 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Successfully initialized Audio with Backend '%s'", audioBackendName);
23476
23477 // Auto play audio if needed
23478 if (appState->initSettings.audio.startAuto && (appState->initSettings.audio.clientReadCallback != fpl_null)) {
23479 FPL_LOG_DEBUG(FPL__MODULE_CORE, "Play Audio (Auto)");
23480 fplAudioResultType playResult = fplPlayAudio();
23481 if (playResult != fplAudioResultType_Success) {
23482 FPL__CRITICAL(FPL__MODULE_CORE, "Failed auto-play of audio, code: %d!", playResult);
23483 fpl__ReleasePlatformStates(initState, appState);
23484 return(fpl__SetPlatformResult(fplPlatformResultType_FailedAudio));
23485 }
23486 }
23487 }
23488# endif // FPL__ENABLE_AUDIO
23489
23490 initState->isInitialized = true;
23491 return(fpl__SetPlatformResult(fplPlatformResultType_Success));
23492}
23493
23495 fplPlatformType result;
23496#if defined(FPL_PLATFORM_WINDOWS)
23497 result = fplPlatformType_Windows;
23498#elif defined(FPL_PLATFORM_LINUX)
23499 result = fplPlatformType_Linux;
23500#elif defined(FPL_PLATFORM_UNIX)
23501 result = fplPlatformType_Unix;
23502#else
23503 result = fplPlatformType_Unknown;
23504#endif
23505 return(result);
23506}
23507
23508#endif // FPL__SYSTEM_INIT_DEFINED
23509
23510#if defined(FPL_COMPILER_MSVC)
23512# pragma warning( pop )
23513#elif defined(FPL_COMPILER_GCC)
23515# pragma GCC diagnostic pop
23516#elif defined(FPL_COMPILER_CLANG)
23518# pragma clang diagnostic pop
23519#endif
23520
23521#endif // FPL_IMPLEMENTATION && !FPL__IMPLEMENTED
23522
23523// ****************************************************************************
23524//
23525// Entry-Points Implementation
23526//
23527// ****************************************************************************
23528#if defined(FPL_ENTRYPOINT) && !defined(FPL__ENTRYPOINT_IMPLEMENTED)
23529# define FPL__ENTRYPOINT_IMPLEMENTED
23530
23531// ***************************************************************
23532//
23533// Platform includes for entry point
23534//
23535// ***************************************************************
23536#if !defined(FPL__HAS_PLATFORM_INCLUDES)
23537# define FPL__HAS_PLATFORM_INCLUDES
23538
23539# if defined(FPL_PLATFORM_WINDOWS)
23540 // @NOTE(final): windef.h defines min/max macros in lowerspace, this will break for example std::min/max so we have to tell the header we dont want this!
23541# if !defined(NOMINMAX)
23542# define NOMINMAX
23543# endif
23544 // @NOTE(final): For now we dont want any network, com or gdi stuff at all, maybe later who knows.
23545# if !defined(WIN32_LEAN_AND_MEAN)
23546# define WIN32_LEAN_AND_MEAN 1
23547# endif
23548 // @STUPID(final): Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here"
23549struct IUnknown;
23550# include <windows.h> // Win32 api
23551# if _WIN32_WINNT < 0x0600
23552# error "Windows Vista or higher required!"
23553# endif
23554# endif // FPL_PLATFORM_WINDOWS
23555
23556#endif // !FPL__HAS_PLATFORM_INCLUDES
23557
23558// ***************************************************************
23559//
23560// Win32 Entry point
23561//
23562// ***************************************************************
23563# if defined(FPL_PLATFORM_WINDOWS)
23564
23565#define FPL__FUNC_WIN32_CommandLineToArgvW(name) LPWSTR* WINAPI name(LPCWSTR lpCmdLine, int *pNumArgs)
23566typedef FPL__FUNC_WIN32_CommandLineToArgvW(fpl__win32_func_CommandLineToArgvW);
23567
23568typedef struct fpl__Win32CommandLineUTF8Arguments {
23569 void *mem;
23570 char **args;
23571 uint32_t count;
23572} fpl__Win32CommandLineUTF8Arguments;
23573
23574fpl_internal fpl__Win32CommandLineUTF8Arguments fpl__Win32ParseWideArguments(LPWSTR cmdLine, const bool appendExecutable) {
23575 fpl__Win32CommandLineUTF8Arguments args = fplZeroInit;
23576
23577 // @NOTE(final): Temporary load and unload shell32 for parsing the arguments
23578 HMODULE shellapiLibrary = LoadLibraryA("shell32.dll");
23579 if (shellapiLibrary != fpl_null) {
23580 fpl__win32_func_CommandLineToArgvW *commandLineToArgvW = (fpl__win32_func_CommandLineToArgvW *)GetProcAddress(shellapiLibrary, "CommandLineToArgvW");
23581 if (commandLineToArgvW != fpl_null) {
23582 // Parse executable arguments
23583 int cmdLineLen = lstrlenW(cmdLine);
23584 int executableFilePathArgumentCount = 0;
23585 wchar_t **executableFilePathArgs = NULL;
23586 size_t executableFilePathLen = 0;
23587 if (appendExecutable || (cmdLineLen == 0)) {
23588 executableFilePathArgumentCount = 0;
23589 executableFilePathArgs = commandLineToArgvW(L"", &executableFilePathArgumentCount);
23590 executableFilePathLen = 0;
23591 for (int i = 0; i < executableFilePathArgumentCount; ++i) {
23592 if (i > 0) {
23593 // Include whitespace
23594 executableFilePathLen++;
23595 }
23596 size_t sourceLen = lstrlenW(executableFilePathArgs[i]);
23597 int destLen = WideCharToMultiByte(CP_UTF8, 0, executableFilePathArgs[i], (int)sourceLen, fpl_null, 0, fpl_null, fpl_null);
23598 executableFilePathLen += destLen;
23599 }
23600 }
23601
23602 // Parse arguments and add to total UTF8 string length
23603 int actualArgumentCount = 0;
23604 wchar_t **actualArgs = fpl_null;
23605 size_t actualArgumentsLen = 0;
23606 if (cmdLine != fpl_null && cmdLineLen > 0) {
23607 actualArgs = commandLineToArgvW(cmdLine, &actualArgumentCount);
23608 for (int i = 0; i < actualArgumentCount; ++i) {
23609 size_t sourceLen = lstrlenW(actualArgs[i]);
23610 int destLen = WideCharToMultiByte(CP_UTF8, 0, actualArgs[i], (int)sourceLen, fpl_null, 0, fpl_null, fpl_null);
23611 actualArgumentsLen += destLen;
23612 }
23613 }
23614
23615 // Calculate argument
23616 uint32_t totalArgumentCount = 0;
23617 if (executableFilePathArgumentCount > 0) {
23618 totalArgumentCount++;
23619 }
23620 totalArgumentCount += actualArgumentCount;
23621
23622 // @NOTE(final): We allocate one memory block that contains
23623 // - The arguments as one string, each terminated by zero-character -> char*
23624 // - A padding
23625 // - The size of the string array -> char**
23626 size_t totalStringLen = executableFilePathLen + actualArgumentsLen + totalArgumentCount;
23627 size_t singleArgStringSize = sizeof(char) * (totalStringLen);
23628 size_t arbitaryPadding = 64;
23629 size_t argArraySize = sizeof(char **) * totalArgumentCount;
23630 size_t totalArgSize = singleArgStringSize + arbitaryPadding + argArraySize;
23631
23632 // @NOTE(final): We cannot use fpl__AllocateDynamicMemory here, because the main function is not called yet - therefore we dont have any fplMemorySettings set at this point.
23633 args.count = totalArgumentCount;
23634 args.mem = (uint8_t *)fplMemoryAllocate(totalArgSize);
23635 args.args = (char **)((uint8_t *)args.mem + singleArgStringSize + arbitaryPadding);
23636
23637 // Convert executable path to UTF8 and add it, if needed
23638 char *destArg = (char *)args.mem;
23639 int startArgIndex = 0;
23640 if (executableFilePathArgumentCount > 0)
23641 {
23642 args.args[startArgIndex++] = destArg;
23643 for (int i = 0; i < executableFilePathArgumentCount; ++i) {
23644 if (i > 0) {
23645 *destArg++ = ' ';
23646 }
23647 wchar_t *sourceArg = executableFilePathArgs[i];
23648 size_t sourceArgLen = lstrlenW(sourceArg);
23649 int destArgLen = WideCharToMultiByte(CP_UTF8, 0, sourceArg, (int)sourceArgLen, fpl_null, 0, fpl_null, fpl_null);
23650 WideCharToMultiByte(CP_UTF8, 0, sourceArg, (int)sourceArgLen, destArg, destArgLen, fpl_null, fpl_null);
23651 destArg += destArgLen;
23652 }
23653 *destArg++ = 0;
23654 LocalFree(executableFilePathArgs);
23655 }
23656
23657 // Convert actual arguments to UTF8 and add it, if needed
23658 if (actualArgumentCount > 0) {
23659 fplAssert(actualArgs != fpl_null);
23660 for (int i = 0; i < actualArgumentCount; ++i) {
23661 args.args[startArgIndex++] = destArg;
23662 wchar_t *sourceArg = actualArgs[i];
23663 size_t sourceArgLen = lstrlenW(sourceArg);
23664 int destArgLen = WideCharToMultiByte(CP_UTF8, 0, sourceArg, (int)sourceArgLen, fpl_null, 0, fpl_null, fpl_null);
23665 WideCharToMultiByte(CP_UTF8, 0, sourceArg, (int)sourceArgLen, destArg, destArgLen, fpl_null, fpl_null);
23666 destArg += destArgLen;
23667 *destArg++ = 0;
23668 }
23669 LocalFree(actualArgs);
23670 }
23671 }
23672 FreeLibrary(shellapiLibrary);
23673 shellapiLibrary = fpl_null;
23674 }
23675 return(args);
23676}
23677
23678fpl_internal fpl__Win32CommandLineUTF8Arguments fpl__Win32ParseAnsiArguments(LPSTR cmdLine, const bool appendExecutable) {
23679 fpl__Win32CommandLineUTF8Arguments result;
23680 if (cmdLine != fpl_null) {
23681 size_t ansiSourceLen = fplGetStringLength(cmdLine);
23682 int wideDestLen = MultiByteToWideChar(CP_ACP, 0, cmdLine, (int)ansiSourceLen, fpl_null, 0);
23683 // @NOTE(final): We cannot use fpl__AllocateDynamicMemory here, because the main function is not called yet - therefore we dont have any fplMemorySettings set at this point.
23684 wchar_t *wideCmdLine = (wchar_t *)fplMemoryAllocate(sizeof(wchar_t) * (wideDestLen + 1));
23685 MultiByteToWideChar(CP_ACP, 0, cmdLine, (int)ansiSourceLen, wideCmdLine, wideDestLen);
23686 wideCmdLine[wideDestLen] = 0;
23687 result = fpl__Win32ParseWideArguments(wideCmdLine, appendExecutable);
23688 fplMemoryFree(wideCmdLine);
23689 } else {
23690 wchar_t tmp[1] = { 0 };
23691 result = fpl__Win32ParseWideArguments(tmp, appendExecutable);
23692 }
23693 return(result);
23694}
23695
23696#if !defined(FPL_NO_CRT)
23697# include <stdio.h>
23698#endif
23699
23700fpl_internal void fpl__Win32FreeConsole() {
23701 HWND consoleHandle = GetConsoleWindow();
23702 if (consoleHandle != fpl_null) {
23703 FreeConsole();
23704 }
23705}
23706
23707fpl_internal void fpl__Win32InitConsole() {
23708 HWND consoleHandle = GetConsoleWindow();
23709 if (consoleHandle == fpl_null) {
23710 // Create or attach console
23711 AllocConsole();
23712 AttachConsole(GetCurrentProcessId());
23713
23714 // Redirect out/in/err to console
23715 HANDLE hConOut = CreateFileW(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
23716 HANDLE hConIn = CreateFileW(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
23717 SetStdHandle(STD_OUTPUT_HANDLE, hConOut);
23718 SetStdHandle(STD_ERROR_HANDLE, hConOut);
23719 SetStdHandle(STD_INPUT_HANDLE, hConIn);
23720
23721#if !defined(FPL_NO_CRT)
23722 FILE *dummy;
23723 freopen_s(&dummy, "CONIN$", "r", stdin);
23724 freopen_s(&dummy, "CONOUT$", "w", stderr);
23725 freopen_s(&dummy, "CONOUT$", "w", stdout);
23726#endif
23727 }
23728}
23729
23730# if defined(FPL_NO_CRT)
23731//
23732// Win32 without CRT
23733//
23734# if defined(FPL_APPTYPE_WINDOW)
23735# if defined(UNICODE)
23736void __stdcall wWinMainCRTStartup(void) {
23737 fpl__Win32InitConsole();
23738 LPWSTR argsW = GetCommandLineW();
23739 int result = wWinMain(GetModuleHandleW(fpl_null), fpl_null, argsW, SW_SHOW);
23740 fpl__Win32FreeConsole();
23741 ExitProcess(result);
23742}
23743# else
23744void __stdcall WinMainCRTStartup(void) {
23745 fpl__Win32InitConsole();
23746 LPSTR argsA = GetCommandLineA();
23747 int result = WinMain(GetModuleHandleA(fpl_null), fpl_null, argsA, SW_SHOW);
23748 fpl__Win32FreeConsole();
23749 ExitProcess(result);
23750}
23751# endif // UNICODE
23752# elif defined(FPL_APPTYPE_CONSOLE)
23753void __stdcall mainCRTStartup(void) {
23754 fpl__Win32InitConsole();
23755 fpl__Win32CommandLineUTF8Arguments args;
23756# if defined(UNICODE)
23757 LPWSTR argsW = GetCommandLineW();
23758 args = fpl__Win32ParseWideArguments(argsW, false);
23759# else
23760 LPSTR argsA = GetCommandLineA();
23761 args = fpl__Win32ParseAnsiArguments(argsA, false);
23762# endif
23763 int result = main(args.count, args.args);
23764 fplMemoryFree(args.mem);
23765 fpl__Win32FreeConsole();
23766 ExitProcess(result);
23767}
23768# else
23769# error "Application type not set!"
23770# endif // FPL_APPTYPE
23771
23772# else
23773//
23774// Win32 with CRT
23775//
23776# if defined(UNICODE)
23777int WINAPI wWinMain(HINSTANCE appInstance, HINSTANCE prevInstance, LPWSTR cmdLine, int cmdShow) {
23778 fpl__Win32InitConsole();
23779 fpl__Win32CommandLineUTF8Arguments args = fpl__Win32ParseWideArguments(cmdLine, true);
23780 int result = main(args.count, args.args);
23781 fplMemoryFree(args.mem);
23782 fpl__Win32FreeConsole();
23783 return(result);
23784}
23785# else
23786int WINAPI WinMain(HINSTANCE appInstance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow) {
23787 fpl__Win32InitConsole();
23788 fpl__Win32CommandLineUTF8Arguments args = fpl__Win32ParseAnsiArguments(cmdLine, true);
23789 int result = main(args.count, args.args);
23790 fplMemoryFree(args.mem);
23791 fpl__Win32FreeConsole();
23792 return(result);
23793}
23794# endif // UNICODE
23795
23796# endif // FPL_NO_CRT
23797
23798# endif // FPL_PLATFORM_WINDOWS
23799
23800#endif // FPL_ENTRYPOINT && !FPL__ENTRYPOINT_IMPLEMENTED
23801
23802// Undef useless constants for callers
23803#if !defined(FPL_NO_UNDEF)
23804
23805# if defined(FPL_SUBPLATFORM_X11)
23806# undef Bool
23807# undef Expose
23808# undef KeyPress
23809# undef KeyRelease
23810# undef FocusIn
23811# undef FocusOut
23812# undef FontChange
23813# undef None
23814# undef Success
23815# undef Status
23816# undef Unsorted
23817# endif // FPL_SUBPLATFORM_X11
23818
23819# if defined(FPL_PLATFORM_WINDOWS)
23820# undef TRUE
23821# undef FALSE
23822# undef far
23823# undef near
23824# undef IN
23825# undef OUT
23826# undef OPTIONAL
23827# endif // FPL_SUBPLATFORM_X11
23828
23829#endif // !FPL_NO_UNDEF
23830
23832
23833// end-of-file
int32_t fpl_b32
32-bit boolean
#define fpl_null
Null.
#define fplAsm
A assembler compiler instruction (asm)
#define FPL_ENUM_AS_FLAGS_OPERATORS(type)
Macro for optionally adding enum operators for bitwise and/or/xor.
fpl_platform_api int64_t fplAtomicExchangeS64(volatile int64_t *target, const int64_t value)
Replaces a 64-bit signed integer with the given value atomically.
fpl_common_api void * fplAtomicLoadPtr(volatile void **source)
Loads the pointer value atomically and returns the value.
fpl_platform_api uint32_t fplAtomicExchangeU32(volatile uint32_t *target, const uint32_t value)
Replaces a 32-bit unsigned integer with the given value atomically.
fpl_platform_api uint32_t fplAtomicIncrementU32(volatile uint32_t *dest)
Increments the given 32-bit unsigned integer by one atomically.
fpl_platform_api uint64_t fplAtomicExchangeU64(volatile uint64_t *target, const uint64_t value)
Replaces a 64-bit unsigned integer with the given value atomically.
fpl_platform_api bool fplAtomicIsCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange)
Compares a 32-bit unsigned integer with a comparand and swaps it when comparand matches the destinati...
fpl_platform_api uint64_t fplAtomicFetchAndAddU64(volatile uint64_t *value, const uint64_t addend)
Adds a 64-bit unsigned integer to the value by the given addend atomically.
fpl_platform_api uint64_t fplAtomicIncrementU64(volatile uint64_t *dest)
Increments the given 64-bit unsigned integer by one atomically.
fpl_platform_api int64_t fplAtomicIncrementS64(volatile int64_t *dest)
Increments the given 64-bit signed integer by one atomically.
fpl_platform_api void fplAtomicStoreS32(volatile int32_t *dest, const int32_t value)
Overwrites the 32-bit signed value atomically.
fpl_common_api size_t fplAtomicAddAndFetchSize(volatile size_t *dest, const size_t addend)
Adds the addend to destination size atomically and returns the result after the addition.
fpl_platform_api int32_t fplAtomicLoadS32(volatile int32_t *source)
Loads the 32-bit signed value atomically and returns the value.
fpl_common_api void * fplAtomicExchangePtr(volatile void **target, const void *value)
Replaces a pointer with the given value atomically.
fpl_platform_api uint64_t fplAtomicAddAndFetchU64(volatile uint64_t *dest, const uint64_t addend)
Adds the addend to destination 64-bit unsigned integer atomically and returns the result after the ad...
fpl_platform_api uint64_t fplAtomicLoadU64(volatile uint64_t *source)
Loads the 64-bit unsigned value atomically and returns the value.
fpl_common_api void * fplAtomicCompareAndSwapPtr(volatile void **dest, const void *comparand, const void *exchange)
Compares a pointer with a comparand and swaps it when comparand matches the destination.
fpl_common_api size_t fplAtomicIncrementSize(volatile size_t *dest)
Increments the given size by one atomically.
fpl_platform_api bool fplAtomicIsCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange)
Compares a 32-bit signed integer with a comparand and swaps it when comparand matches the destination...
fpl_common_api void * fplAtomicAddAndFetchPtr(volatile void **dest, const intptr_t addend)
Adds the addend to destination pointer atomically and returns the result after the addition.
fpl_common_api size_t fplAtomicExchangeSize(volatile size_t *target, const size_t value)
Replaces a size with the given value atomically.
fpl_common_api void fplAtomicStorePtr(volatile void **dest, const void *value)
Overwrites the pointer value atomically.
fpl_platform_api uint32_t fplAtomicAddAndFetchU32(volatile uint32_t *dest, const uint32_t addend)
Adds the addend to destination 32-bit unsigned integer atomically and returns the result after the ad...
fpl_platform_api bool fplAtomicIsCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange)
Compares a 64-bit unsigned integer with a comparand and swaps it when comparand matches the destinati...
fpl_common_api void fplAtomicStoreSize(volatile size_t *dest, const size_t value)
Overwrites the size value atomically.
fpl_platform_api void fplAtomicReadWriteFence()
Inserts a memory read and write fence/barrier.
fpl_platform_api int32_t fplAtomicAddAndFetchS32(volatile int32_t *dest, const int32_t addend)
Adds the addend to destination 32-bit signed integer atomically and returns the result after the addi...
fpl_platform_api uint32_t fplAtomicCompareAndSwapU32(volatile uint32_t *dest, const uint32_t comparand, const uint32_t exchange)
Compares a 32-bit unsigned integer with a comparand and swaps it when comparand matches the destinati...
fpl_platform_api int64_t fplAtomicCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange)
Compares a 64-bit signed integer with a comparand and swaps it when comparand matches the destination...
fpl_platform_api int32_t fplAtomicCompareAndSwapS32(volatile int32_t *dest, const int32_t comparand, const int32_t exchange)
Compares a 32-bit signed integer with a comparand and swaps it when comparand matches the destination...
fpl_platform_api uint32_t fplAtomicFetchAndAddU32(volatile uint32_t *value, const uint32_t addend)
Adds a 32-bit unsigned integer to the value by the given addend atomically.
fpl_platform_api void fplAtomicStoreU64(volatile uint64_t *dest, const uint64_t value)
Overwrites the 64-bit unsigned value atomically.
fpl_common_api size_t fplAtomicCompareAndSwapSize(volatile size_t *dest, const size_t comparand, const size_t exchange)
Compares a size with a comparand and swaps it when comparand matches the destination.
fpl_platform_api void fplAtomicReadFence()
Inserts a memory read fence/barrier.
fpl_common_api bool fplAtomicIsCompareAndSwapPtr(volatile void **dest, const void *comparand, const void *exchange)
Compares a pointer with a comparand and swaps it when comparand matches the destination and returns a...
fpl_platform_api uint32_t fplAtomicLoadU32(volatile uint32_t *source)
Loads the 32-bit unsigned value atomically and returns the value.
fpl_platform_api int32_t fplAtomicFetchAndAddS32(volatile int32_t *value, const int32_t addend)
Adds a 32-bit signed integer to the value by the given addend atomically.
fpl_platform_api int64_t fplAtomicLoadS64(volatile int64_t *source)
Loads the 64-bit signed value atomically and returns the value.
fpl_platform_api int64_t fplAtomicAddAndFetchS64(volatile int64_t *dest, const int64_t addend)
Adds the addend to destination 64-bit signed integer atomically and returns the result after the addi...
fpl_common_api size_t fplAtomicFetchAndAddSize(volatile size_t *dest, const size_t addend)
Adds a size to the value by the given addend atomically.
fpl_platform_api int32_t fplAtomicIncrementS32(volatile int32_t *dest)
Increments the given 32-bit signed integer by one atomically.
fpl_platform_api void fplAtomicWriteFence()
Inserts a memory write fence/barrier.
fpl_platform_api bool fplAtomicIsCompareAndSwapS64(volatile int64_t *dest, const int64_t comparand, const int64_t exchange)
Compares a 64-bit signed integer with a comparand and swaps it when comparand matches the destination...
fpl_common_api void * fplAtomicFetchAndAddPtr(volatile void **dest, const intptr_t addend)
Adds a addend to the pointer atomically and returns the initial value before the add.
fpl_platform_api int64_t fplAtomicFetchAndAddS64(volatile int64_t *value, const int64_t addend)
Adds a 64-bit signed integer to the value by the given addend atomically.
fpl_common_api size_t fplAtomicLoadSize(volatile size_t *source)
Loads the size value atomically and returns the value.
fpl_common_api bool fplAtomicIsCompareAndSwapSize(volatile size_t *dest, const size_t comparand, const size_t exchange)
Compares a size with a comparand and swaps it when comparand matches the destination and returns a bo...
fpl_platform_api uint64_t fplAtomicCompareAndSwapU64(volatile uint64_t *dest, const uint64_t comparand, const uint64_t exchange)
Compares a 64-bit unsigned integer with a comparand and swaps it when comparand matches the destinati...
fpl_common_api void * fplAtomicIncrementPtr(volatile void **dest)
Increments/Advances the given pointer by one atomically.
fpl_platform_api void fplAtomicStoreU32(volatile uint32_t *dest, const uint32_t value)
Overwrites the 32-bit unsigned value atomically.
fpl_platform_api void fplAtomicStoreS64(volatile int64_t *dest, const int64_t value)
Overwrites the 64-bit signed value atomically.
fpl_platform_api int32_t fplAtomicExchangeS32(volatile int32_t *target, const int32_t value)
Replaces a 32-bit signed integer with the given value atomically.
fpl_common_api uint32_t fplGetAudioBufferSizeInMilliseconds(uint32_t sampleRate, uint32_t frameCount)
Computes the duration in milliseconds for the given sample rate and frame count.
fpl_common_api bool fplGetAudioHardwareFormat(fplAudioDeviceFormat *outFormat)
Retrieves the native format for the current audio device.
fpl_common_api fplAudioResultType fplPlayAudio()
Start playing asynchronous audio.
fpl_common_api uint32_t fplGetAudioDevices(fplAudioDeviceInfo *devices, uint32_t maxDeviceCount)
Retrieves all playback audio devices.
fpl_common_api const char * fplGetAudioFormatName(const fplAudioFormatType format)
Gets the string that represents the given audio format type.
fpl_common_api void fplConvertAudioTargetFormatToDeviceFormat(const fplAudioTargetFormat *inFormat, fplAudioDeviceFormat *outFormat)
Converts a fplAudioTargetFormat into a fplAudioDeviceFormat structure.
fpl_common_api uint32_t fplGetAudioSampleSizeInBytes(const fplAudioFormatType format)
Computes the number of bytes required to write one sample with one channel.
fpl_common_api fplAudioResultType fplStopAudio()
Stop playing asynchronous audio.
fpl_common_api uint32_t fplGetAudioBufferSizeInFrames(uint32_t sampleRate, uint32_t bufferSizeInMilliSeconds)
Computes the total number of frames for given sample rate and buffer size.
fpl_common_api fplAudioBackendType fplGetAudioBackendType()
Gets the current audio backend type.
fpl_common_api uint32_t fplGetAudioBufferSizeInBytes(const fplAudioFormatType format, const uint32_t channelCount, const uint32_t frameCount)
Computes the total number of bytes for the buffer and the given parameters.
fpl_common_api const char * fplGetAudioBackendName(fplAudioBackendType backendType)
Gets the string that represents the given audio backend type.
fplAudioResultType
An enumeration of audio results.
fpl_common_api uint32_t fplGetAudioFrameSizeInBytes(const fplAudioFormatType format, const uint32_t channelCount)
Computes the number of bytes required for one interleaved audio frame - containing all the channels.
fpl_common_api bool fplSetAudioClientReadCallback(fpl_audio_client_read_callback *newCallback, void *userData)
Overwrites the audio client read callback.
@ fplAudioResultType_DeviceBusy
The audio device is busy/waiting.
@ fplAudioResultType_PlatformNotInitialized
The platform is not initialized.
@ fplAudioResultType_Failed
Unknown error.
@ fplAudioResultType_NoDeviceFound
No audio device is found.
@ fplAudioResultType_BackendAlreadyInitialized
The audio backend is already initialized.
@ fplAudioResultType_DeviceNotInitialized
The audio device is not initialized.
@ fplAudioResultType_DeviceAlreadyStopped
The audio device is already stopped.
@ fplAudioResultType_Last
Last fplAudioResultType.
@ fplAudioResultType_First
First fplAudioResultType.
@ fplAudioResultType_UnsetAudioChannels
The number of audio channels is not set.
@ fplAudioResultType_None
No result.
@ fplAudioResultType_Success
Success.
@ fplAudioResultType_UnsetAudioSampleRate
The sample rate is not set.
@ fplAudioResultType_UnsetAudioBufferSize
The audio buffer size is not set.
@ fplAudioResultType_UnsetAudioFormat
The fplAudioFormatType is not set.
@ fplAudioResultType_ApiFailed
Failed to load the audio api.
@ fplAudioResultType_DeviceAlreadyStarted
The audio device is already started.
fpl_platform_api void fplConsoleOut(const char *text)
Writes the given text to the standard output console buffer.
fpl_common_api void fplConsoleFormatOut(const char *format,...)
Writes the given formatted text to the standard output console buffer.
fpl_platform_api char fplConsoleWaitForCharInput()
Wait for a character to be typed in the console input and return it.
fpl_platform_api void fplConsoleError(const char *text)
Writes the given text to the standard error console buffer.
fpl_common_api void fplConsoleFormatError(const char *format,...)
Writes the given formatted text to the standard error console buffer.
#define FPL_MAX_PATH_LENGTH
Maximum length of a path.
#define FPL_MAX_FILENAME_LENGTH
Maximum length of a filename.
#define FPL_PATH_SEPARATOR
Path separator character.
#define FPL_MAX_NAME_LENGTH
Maximum length of a name (in characters)
#define FPL_MAX_BUFFER_LENGTH
Maximum length of an internal buffer (in bytes)
#define FPL_FILE_EXT_SEPARATOR
File extension character.
fpl_platform_api void fplDebugOut(const char *text)
Writes the given text into the debugger output stream.
#define fplStaticAssert(exp)
Breaks the compilation, when the specified expression evaluates to false.
#define fplAssert(exp)
Breaks with an runtime assertion, when the specified expression evaluates to false.
fpl_common_api void fplDebugFormatOut(const char *format,...)
Writes the given formatted text into the debugger output stream.
fpl_platform_api bool fplDynamicLibraryLoad(const char *libraryFilePath, fplDynamicLibraryHandle *outHandle)
Loads a dynamic library and returns if the load was successful or not.
fpl_platform_api void * fplGetDynamicLibraryProc(const fplDynamicLibraryHandle *handle, const char *name)
Returns the dynamic library procedure address for the given procedure name.
fpl_platform_api void fplDynamicLibraryUnload(fplDynamicLibraryHandle *handle)
Unloads the loaded library and resets the handle to zero.
fpl_common_api const char * fplGetLastError()
Gets the last internal error string.
fpl_common_api const char * fplGetErrorByIndex(const size_t index)
Gets the last error string from the given index.
fpl_common_api void fplClearErrors()
Clears all the current errors in the platform.
fpl_common_api size_t fplGetErrorCount()
Gets the count of total last errors.
fpl_platform_api uint64_t fplFileWriteBlock64(const fplFileHandle *fileHandle, void *sourceBuffer, const uint64_t sourceSize)
Writes a block to the given file and returns the number of written bytes.
fpl_platform_api bool fplDirectoryRemove(const char *path)
Deletes the given empty directory without confirmation and returns true when the deletion was success...
fpl_platform_api bool fplFileFlush(fplFileHandle *fileHandle)
Flushes the buffers of the given file and causes all buffered data to be written to a file.
fpl_platform_api uint64_t fplFileGetSizeFromPath64(const char *filePath)
Gets the file size in bytes for the given file.
fpl_platform_api bool fplDirectoryExists(const char *path)
Checks if the given directory exists and returns a boolean indicating its existence.
fpl_platform_api bool fplFileMove(const char *sourceFilePath, const char *targetFilePath)
Movies the given source file to the target file and returns true when the move was successful.
fpl_platform_api bool fplDirectoryListNext(fplFileEntry *entry)
Gets the next file entry from iterating through files/directories.
fpl_platform_api uint32_t fplFileGetSizeFromHandle32(const fplFileHandle *fileHandle)
Gets the file size in bytes for an opened file.
uint64_t fplFileTimeStamp
The elapsed seconds since the UNIX epoch (1970-01-01 00:00:00)
fpl_platform_api bool fplFileCreateBinary(const char *filePath, fplFileHandle *outHandle)
Create a binary file for writing to the given string path and returns the handle of it.
fpl_platform_api bool fplDirectoryListBegin(const char *path, const char *filter, fplFileEntry *entry)
Iterates through files/directories in the given directory.
fpl_platform_api uint32_t fplFileGetSizeFromPath32(const char *filePath)
Gets the file size in bytes for the given file.
fplFilePermissionMasks
An enumeration of file permission types.
fpl_platform_api bool fplFileExists(const char *filePath)
Checks if the file exists and returns a boolean indicating the existence.
fpl_platform_api void fplDirectoryListEnd(fplFileEntry *entry)
Releases opened resources from iterating through files/directories.
fpl_platform_api uint64_t fplFileGetPosition64(const fplFileHandle *fileHandle)
Gets the current file position in bytes.
fpl_platform_api uint32_t fplFileGetPosition32(const fplFileHandle *fileHandle)
Gets the current file position in bytes.
fpl_platform_api size_t fplFileGetSizeFromPath(const char *filePath)
Gets the file size in bytes for the given file.
fpl_common_api size_t fplFileGetSizeFromHandle(const fplFileHandle *fileHandle)
Gets the file size in bytes for an opened file.
fpl_platform_api bool fplFileGetTimestampsFromPath(const char *filePath, fplFileTimeStamps *outStamps)
Gets the timestamps for the given file.
fpl_platform_api size_t fplFileReadBlock(const fplFileHandle *fileHandle, const size_t sizeToRead, void *targetBuffer, const size_t maxTargetBufferSize)
Reads a block from the given file and returns the number of bytes read.
fpl_platform_api bool fplFileSetTimestamps(const char *filePath, const fplFileTimeStamps *timeStamps)
Sets the timestamps for the given file.
fplFileAttributeFlags
An enumeratation of file attribute flags (Normal, Readonly, Hidden, etc.)
fpl_platform_api uint64_t fplFileGetSizeFromHandle64(const fplFileHandle *fileHandle)
Gets the file size in bytes for an opened file.
fplFilePermissionFlags
An enumeration of file permission flags.
fpl_platform_api bool fplFileGetTimestampsFromHandle(const fplFileHandle *fileHandle, fplFileTimeStamps *outStamps)
Gets the timestamps for an opened file.
fpl_platform_api bool fplDirectoriesCreate(const char *path)
Creates all the directories in the given path.
fpl_platform_api uint32_t fplFileWriteBlock32(const fplFileHandle *fileHandle, void *sourceBuffer, const uint32_t sourceSize)
Writes a block to the given file and returns the number of written bytes.
fpl_platform_api uint64_t fplFileSetPosition64(const fplFileHandle *fileHandle, const int64_t position, const fplFilePositionMode mode)
Sets the current file position by the given position, depending on the mode it's absolute or relative...
fpl_platform_api bool fplFileDelete(const char *filePath)
Deletes the given file without confirmation and returns true when the deletion was successful.
fplFileEntryType
An enumeration of file entry types (File, Directory, etc.)
fpl_common_api size_t fplFileSetPosition(const fplFileHandle *fileHandle, const intptr_t position, const fplFilePositionMode mode)
Sets the current file position by the given position, depending on the mode it's absolute or relative...
fpl_common_api size_t fplFileWriteBlock(const fplFileHandle *fileHandle, void *sourceBuffer, const size_t sourceSize)
Writes a block to the given file and returns the number of written bytes.
fpl_platform_api uint32_t fplFileReadBlock32(const fplFileHandle *fileHandle, const uint32_t sizeToRead, void *targetBuffer, const uint32_t maxTargetBufferSize)
Reads a block from the given file and returns the number of bytes read.
fpl_platform_api void fplFileClose(fplFileHandle *fileHandle)
Closes the given file and releases the underlying resources and clears the handle to zero.
fpl_platform_api bool fplFileCopy(const char *sourceFilePath, const char *targetFilePath, const bool overwrite)
Copies the given source file to the target path and returns true when the copy was successful.
fpl_common_api size_t fplFileGetPosition(const fplFileHandle *fileHandle)
Gets the current file position in bytes.
fpl_platform_api uint32_t fplFileSetPosition32(const fplFileHandle *fileHandle, const int32_t position, const fplFilePositionMode mode)
Sets the current file position by the given position, depending on the mode it's absolute or relative...
fpl_platform_api bool fplFileOpenBinary(const char *filePath, fplFileHandle *outHandle)
Opens a binary file for reading from a string path and returns the handle of it.
fpl_platform_api uint64_t fplFileReadBlock64(const fplFileHandle *fileHandle, const uint64_t sizeToRead, void *targetBuffer, const uint64_t maxTargetBufferSize)
Reads a block from the given file and returns the number of bytes read.
fplFilePositionMode
An enumeration of file position modes (Beginning, Current, End)
@ fplFilePermissionMasks_Group
Group.
@ fplFilePermissionMasks_User
User.
@ fplFilePermissionMasks_Owner
Owner.
@ fplFilePermissionMasks_None
No mask.
@ fplFileAttributeFlags_Archive
Archive.
@ fplFileAttributeFlags_None
No attributes.
@ fplFileAttributeFlags_Normal
Normal.
@ fplFileAttributeFlags_System
System.
@ fplFileAttributeFlags_Hidden
Hidden.
@ fplFilePermissionFlags_CanExecuteSearch
CanExecute.
@ fplFilePermissionFlags_CanRead
CanRead.
@ fplFilePermissionFlags_CanWrite
CanWrite.
@ fplFilePermissionFlags_All
All (Read, Write, Execute, Search)
@ fplFileEntryType_Unknown
Unknown entry type.
@ fplFileEntryType_Directory
Entry is a directory.
@ fplFileEntryType_File
Entry is a file.
@ fplFilePositionMode_Beginning
Starts from the beginning.
@ fplFilePositionMode_Current
Starts from the current position.
@ fplFilePositionMode_End
Starts from the end.
fpl_common_api uint64_t fplCPUXCR0()
Gets the x86 extended control register for index zero.
fpl_common_api void fplCPUID(fplCPUIDLeaf *outLeaf, const uint32_t functionId)
Queries the x86 CPUID leaf register (EAX, EBX, ECX, EDX) for the given function id.
fpl_common_api const char * fplCPUGetArchName(const fplCPUArchType type)
Gets the string representation of the given architecture type.
fpl_common_api size_t fplCPUGetName(char *destBuffer, const size_t maxDestBufferLen)
Retrieves the name of the processor.
fpl_platform_api size_t fplCPUGetCoreCount()
Retrieves the total number of processor cores.
fplCPUArchType
An enumeration of architecture types.
fpl_common_api uint64_t fplCPURDTSC()
Reads the current time stamp counter (RDTSC)
fpl_common_api bool fplCPUGetCapabilities(fplCPUCapabilities *outCaps)
Gets the capabilities of the processor.
fpl_platform_api fplCPUArchType fplCPUGetArchitecture()
Gets the processor architecture type.
@ fplCPUArchType_x64
X64 only architecture.
@ fplCPUArchType_Arm32
ARM32 architecture.
@ fplCPUArchType_First
First fplCPUArchType.
@ fplCPUArchType_x86_64
X86 with 64-bit architecture.
@ fplCPUArchType_Unknown
Unknown architecture.
@ fplCPUArchType_Arm64
ARM64 architecture.
@ fplCPUArchType_Last
Last fplCPUArchType.
@ fplCPUArchType_x86
X86 architecture.
#define FPL_MAX_KEYBOARD_STATE_COUNT
Max number of keyboard states.
fpl_platform_api bool fplPollGamepadStates(fplGamepadStates *outStates)
Polls the current gamepad states and writes it out into the output structure.
#define FPL_MAX_GAMEPAD_STATE_COUNT
Max number of gamepad states.
fpl_platform_api bool fplPollMouseState(fplMouseState *outState)
Polls the current mouse state and writes it out into the output structure.
fpl_platform_api bool fplQueryCursorPosition(int32_t *outX, int32_t *outY)
Queries the cursor position in screen coordinates, relative to the root screen.
fpl_platform_api bool fplPollKeyboardState(fplKeyboardState *outState)
Polls the current keyboard state and writes it out into the output structure.
fplLocaleFormat
A enumeration of locale formats.
fpl_platform_api size_t fplGetInputLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen)
Gets the input locale in the given target format.
fpl_platform_api size_t fplGetUserLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen)
Gets the user locale in the given target format.
fpl_platform_api size_t fplGetSystemLocale(const fplLocaleFormat targetFormat, char *buffer, const size_t maxBufferLen)
Gets the system locale in the given target format.
@ fplLocaleFormat_ISO639
ISO-639 format (de-DE, en-US, etc.)
@ fplLocaleFormat_None
No locale format.
fplLogLevel
An enumeration of log levels.
fpl_common_api void fplSetMaxLogLevel(const fplLogLevel maxLevel)
Changes the current maximum log level to the given value.
fpl_common_api const fplLogSettings * fplGetLogSettings()
Gets the current log settings.
fpl_common_api void fplSetLogSettings(const fplLogSettings *params)
Overwrites the current log settings.
fpl_common_api fplLogLevel fplGetMaxLogLevel()
Gets the current maximum allowed log level.
void() fpl_log_func_callback(const char *funcName, const int lineNumber, const fplLogLevel level, const char *message)
A callback for printing a log message.
fplLogWriterFlags
An enumeration of log writer flags.
@ fplLogLevel_Warning
Warning.
@ fplLogLevel_Last
Last fplLogLevel.
@ fplLogLevel_Trace
Trace.
@ fplLogLevel_All
All.
@ fplLogLevel_Verbose
Verbose.
@ fplLogLevel_Info
Info.
@ fplLogLevel_Debug
Debug.
@ fplLogLevel_Critical
Critical.
@ fplLogLevel_Error
Error.
@ fplLogLevel_First
First fplLogLevel.
@ fplLogWriterFlags_ErrorConsole
Error-Console output.
@ fplLogWriterFlags_DebugOut
Debug output.
@ fplLogWriterFlags_Custom
Custom output.
@ fplLogWriterFlags_None
No appender flags.
@ fplLogWriterFlags_StandardConsole
Standard-Console output.
#define fplMin(a, b)
Returns the smallest value of A and B.
#define fplIsBitSet(value, bit)
Returns true when the given value has the given bit set.
#define fplMax(a, b)
Returns the biggest value of A and B.
#define fplIsBigEndian()
Returns true when the given platform is big-endian.
#define fplIsAligned(ptr, alignment)
Returns true when the given pointer address is aligned to the given alignment.
#define fplGetAlignedSize(size, alignment)
Returns the given size, extended to satisfy the given alignment boundary.
#define fplZeroInit
Initializes a struct to zero.
#define fplCopyStruct(src, dst)
Copies the given source struct into the destination struct.
#define fplArrayCount(arr)
Returns the element count from a static array. This should ideally produce a compile error when passi...
#define fplStructInit
Initializes a struct by the given type.
#define fplClearStruct(ptr)
Clears the given struct pointer to zero.
fpl_platform_api bool fplMemoryGetInfos(fplMemoryInfos *outInfos)
Retrieves the current system memory usage.
fpl_common_api void fplMemoryCopy(const void *sourceMem, const size_t sourceSize, void *targetMem)
Copies the given source memory with its length to the target memory.
fpl_platform_api void * fplMemoryAllocate(const size_t size)
Allocates memory from the operating system by the given size.
fpl_common_api void fplMemorySet(void *mem, const uint8_t value, const size_t size)
Sets the given memory by the given size to the given value.
fpl_platform_api void fplMemoryFree(void *ptr)
Releases the memory allocated from the operating system.
fpl_common_api void fplMemoryClear(void *mem, const size_t size)
Clears the given memory by the given size to zero.
fpl_common_api void * fplMemoryAlignedAllocate(const size_t size, const size_t alignment)
Allocates aligned memory from the operating system by the given alignment.
fpl_common_api void fplMemoryAlignedFree(void *ptr)
Releases the aligned memory allocated from the operating system.
fpl_platform_api size_t fplSessionGetUsername(char *nameBuffer, const size_t maxNameBufferLen)
Gets the username of the current logged-in user from the session.
fpl_platform_api size_t fplGetExecutableFilePath(char *destPath, const size_t maxDestLen)
Gets the full path to this executable, including the executable file name.
fpl_common_api size_t fplChangeFileExtension(const char *filePath, const char *newFileExtension, char *destPath, const size_t maxDestLen)
Changes the file extension on the given source path and writes the result into a destination buffer.
fpl_common_api size_t fplPathCombine(char *destPath, const size_t maxDestPathLen, const size_t pathCount,...)
Combines all given paths by the platforms path separator for a fixed number of arguments.
fpl_common_api const char * fplExtractFileExtension(const char *sourcePath)
Extracts the file extension from the given source path.
fpl_platform_api size_t fplGetHomePath(char *destPath, const size_t maxDestLen)
Gets the full path to your home directory.
fpl_common_api size_t fplExtractFilePath(const char *sourcePath, char *destPath, const size_t maxDestLen)
Extracts the directory path from the given file path.
fpl_common_api const char * fplExtractFileName(const char *sourcePath)
Extracts the file name including the file extension from the given source path.
fpl_common_api fplPlatformType fplGetPlatformType()
Gets the type of the platform.
fpl_common_api void fplPlatformRelease()
Releases the resources allocated by the platform layer.
fpl_common_api bool fplPlatformInit(const fplInitFlags initFlags, const fplSettings *initSettings)
Initializes the platform layer.
fpl_common_api fplPlatformResultType fplGetPlatformResult()
Gets the result type of the platform initialization.
fpl_common_api bool fplIsPlatformInitialized()
Returns true when the platform is initialized, or false if not.
fpl_common_api const char * fplGetPlatformName(const fplPlatformType type)
Gets the string representation of the given platform type.
char fplVersionNumberPart[4+1]
A type definition for mapping a part of a version number.
fpl_platform_api bool fplOSGetVersionInfos(fplOSVersionInfos *outInfos)
Gets version informations from the operating system.
fpl_common_api void fplSetDefaultAudioSettings(fplAudioSettings *audio)
Resets the given audio settings to default settings (S16 PCM, 48 kHz, 2 Channels)
fpl_common_api void fplSetDefaultInputSettings(fplInputSettings *input)
Resets the given input settings container to default values.
fplImageType
An enumeration of image types.
void() fpl_memory_release_callback(void *userData, void *ptr)
Custom memory release callback.
fplInitFlags
An enumeration of initialization flags.
fplPlatformResultType
An enumeration of platform result types.
fplVulkanValidationLayerMode
The validation layer modes for Vulkan.
fplAudioFormatType
An enumeration of audio format types.
fpl_common_api const fplSettings * fplGetCurrentSettings()
Gets the current settings.
fplAudioLatencyMode
An enumeration of audio latency modes.
fplMemoryAllocationMode
A enumeration of dynamic memory allocation modes.
fplOpenGLCompabilityFlags
An enumeration of OpenGL compability flags.
fpl_window_event_callback fpl_window_exposed_callback
A callback executed when the window needs to be exposed/repainted.
uint32_t() fpl_audio_client_read_callback(const fplAudioDeviceFormat *deviceFormat, const uint32_t frameCount, void *outputSamples, void *userData)
A callback for reading audio samples from the client.
fpl_inline fplColor32 fplCreateColorRGBA(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a)
Creates a fplColor32 from the specified r, g, b, a components.
fpl_common_api fplSettings fplMakeDefaultSettings()
Creates a full settings structure containing default values.
fpl_common_api void fplSetDefaultConsoleSettings(fplConsoleSettings *console)
Resets the given console settings container to default settings.
fplAudioDefaultFields
An enumeration of audio default fields.
void() fplVulkanValidationLayerCallback(void *userData, const char *message, const uint32_t messageSeverity, const uint32_t messageType, const void *debugUtilsMessengerCallbackData)
The debug callback called when the validation layer writes something.
fplAudioBackendType
An enumeration of audio backend types.
fpl_common_api void fplSetDefaultSettings(fplSettings *settings)
Resets the given settings container to default values for window, video, audio, etc.
fplVideoBackendType
An enumeration of video backend types.
fplPlatformType
An enumeration of platform types.
fpl_common_api const char * fplPlatformGetResultName(const fplPlatformResultType type)
Gets the string representation of a platform result type.
void *() fpl_memory_allocate_callback(void *userData, const size_t size, const size_t alignment)
Custom memory allocation callback.
bool() fpl_window_event_callback(const fplPlatformType platformType, void *windowState, void *rawEventData, void *userData)
A callback executed for each raw window event.
fpl_common_api void fplSetDefaultWindowSettings(fplWindowSettings *window)
Resets the given window settings container to default settings.
fpl_common_api void fplSetDefaultVideoSettings(fplVideoSettings *video)
Resets the given video settings to default values.
fplVulkanValidationSeverity
The validation layer logging severity for Vulkan.
@ fplImageType_None
No image type.
@ fplImageType_RGBA
RGBA image type.
@ fplInitFlags_None
No init flags.
@ fplInitFlags_Audio
Use asynchronous audio playback.
@ fplInitFlags_All
All init flags.
@ fplInitFlags_Window
Create a single window.
@ fplInitFlags_Video
Use a video backbuffer (This flag ensures that fplInitFlags_Window is included always)
@ fplInitFlags_GameController
Support for game controllers.
@ fplInitFlags_Console
Create a console window.
@ fplPlatformResultType_FailedPlatform
Platform initialization failed.
@ fplPlatformResultType_Last
Last fplPlatformResultType.
@ fplPlatformResultType_FailedWindow
Window creation failed.
@ fplPlatformResultType_Success
Everything is fine.
@ fplPlatformResultType_FailedAudio
Audio initialization failed.
@ fplPlatformResultType_First
First fplPlatformResultType.
@ fplPlatformResultType_AlreadyInitialized
Platform is already initialized.
@ fplPlatformResultType_FailedVideo
Video initialization failed.
@ fplPlatformResultType_NotInitialized
Platform is not initialized.
@ fplPlatformResultType_FailedAllocatingMemory
Failed allocating required memory.
@ fplVulkanValidationLayerMode_Required
Enable validations and stop when its not supported.
@ fplVulkanValidationLayerMode_Disabled
Do not use the validation.
@ fplVulkanValidationLayerMode_Optional
Enable validations when its possible.
@ fplAudioFormatType_U8
Unsigned 8-bit integer PCM.
@ fplAudioFormatType_F64
64-bit IEEE_FLOAT
@ fplAudioFormatType_S24
Signed 24-bit integer PCM.
@ fplAudioFormatType_Last
Last fplAudioFormatType.
@ fplAudioFormatType_S16
Signed 16-bit integer PCM.
@ fplAudioFormatType_None
No audio format.
@ fplAudioFormatType_S32
Signed 32-bit integer PCM.
@ fplAudioFormatType_F32
32-bit IEEE_FLOAT
@ fplAudioFormatType_S64
Signed 64-bit integer PCM.
@ fplAudioFormatType_First
First fplAudioFormatType.
@ fplAudioLatencyMode_Conservative
Conservative latency.
@ fplAudioLatencyMode_Low
Low latency.
@ fplMemoryAllocationMode_Custom
Use custom memory allocation.
@ fplMemoryAllocationMode_Automatic
Use OS memory allocation.
@ fplOpenGLCompabilityFlags_Legacy
Use legacy context.
@ fplOpenGLCompabilityFlags_Compability
Use compability profile.
@ fplOpenGLCompabilityFlags_Core
Use core profile.
@ fplOpenGLCompabilityFlags_Forward
Remove features marked as deprecated.
@ fplAudioDefaultFields_Channels
Number of channels is default.
@ fplAudioDefaultFields_Periods
Number of periods is default.
@ fplAudioDefaultFields_SampleRate
Samples per seconds is default.
@ fplAudioDefaultFields_Type
Audio format is default.
@ fplAudioDefaultFields_None
No default fields.
@ fplAudioDefaultFields_BufferSize
Buffer size is default.
@ fplAudioBackendType_First
First fplAudioBackendType.
@ fplAudioBackendType_Last
Last fplAudioBackendType.
@ fplAudioBackendType_Alsa
ALSA.
@ fplAudioBackendType_None
No audio backend.
@ fplAudioBackendType_Auto
Auto detect.
@ fplAudioBackendType_DirectSound
DirectSound.
@ fplVideoBackendType_Vulkan
Vulkan.
@ fplVideoBackendType_None
No video backend.
@ fplVideoBackendType_Software
Software.
@ fplVideoBackendType_First
First fplVideoBackendType.
@ fplVideoBackendType_OpenGL
OpenGL.
@ fplVideoBackendType_Last
Last fplVideoBackendType.
@ fplPlatformType_Linux
Linux platform.
@ fplPlatformType_Unknown
Unknown platform.
@ fplPlatformType_Last
Last fplPlatformType.
@ fplPlatformType_Windows
Windows platform.
@ fplPlatformType_First
First fplPlatformType.
@ fplPlatformType_Unix
Unix platform.
@ fplVulkanValidationSeverity_Verbose
Log warning, error, info, verbose.
@ fplVulkanValidationSeverity_Off
Log nothing.
@ fplVulkanValidationSeverity_All
Log out everything.
@ fplVulkanValidationSeverity_Info
Log warning, error, infos.
@ fplVulkanValidationSeverity_Error
Log error only.
@ fplVulkanValidationSeverity_Warning
Log warning and error.
#define fpl_globalvar
Global persistent variable.
#define fpl_force_inline
Always inlines this function.
#define fpl_internal
Private/Internal function.
#define fpl_platform_api
Platform api.
#define fpl_main
Main entry point api definition.
#define fpl_inline
Inline function.
#define fpl_common_api
Common api.
fpl_common_api size_t fplStringFormatArgs(char *destBuffer, const size_t maxDestBufferLen, const char *format, va_list argList)
Fills out the given destination string buffer with a formatted string, using the format specifier and...
fpl_common_api char * fplCopyStringLen(const char *source, const size_t sourceLen, char *dest, const size_t maxDestLen)
Copies the given source string with a constrained length into a destination string.
fpl_common_api bool fplIsStringEqualLen(const char *a, const size_t aLen, const char *b, const size_t bLen)
Compares two strings with constrained lengths and returns a boolean indicating the equality.
fpl_common_api char * fplStringAppend(const char *appended, char *buffer, size_t maxBufferLen)
Appends the source string to the given buffer.
fpl_common_api int32_t fplStringToS32Len(const char *str, const size_t len)
Converts the given string into a 32-bit integer constrained by string length.
fpl_common_api size_t fplS32ToString(const int32_t value, char *buffer, const size_t maxBufferLen)
Converts the given 32-bit integer value into a string.
fpl_common_api char * fplCopyString(const char *source, char *dest, const size_t maxDestLen)
Copies the given source string into a destination string.
fpl_common_api char * fplEnforcePathSeparatorLen(char *path, size_t maxPathLen)
Ensures that the given string always ends with a path separator with length constrained.
fpl_common_api bool fplIsStringEqual(const char *a, const char *b)
Compares two strings and returns a boolean indicating the equality.
fpl_common_api char * fplStringAppendLen(const char *appended, const size_t appendedLen, char *buffer, size_t maxBufferLen)
Appends the source string to the given buffer constrained by length.
fpl_common_api size_t fplStringFormat(char *destBuffer, const size_t maxDestBufferLen, const char *format,...)
Fills out the given destination string buffer with a formatted string, using the format specifier and...
fpl_platform_api size_t fplWideStringToUTF8String(const wchar_t *wideSource, const size_t wideSourceLen, char *utf8Dest, const size_t maxUtf8DestLen)
Converts the given 16-bit source wide string with length in an 8-bit UTF-8 ANSI string.
fpl_platform_api size_t fplUTF8StringToWideString(const char *utf8Source, const size_t utf8SourceLen, wchar_t *wideDest, const size_t maxWideDestLen)
Converts the given 8-bit UTF-8 source ANSI string with length in a 16-bit wide string.
fpl_common_api bool fplIsStringMatchWildcard(const char *source, const char *wildcard)
Matches the given string by the given wildcard and returns a boolean indicating the match.
fpl_common_api size_t fplGetStringLength(const char *str)
Counts the number of characters without including the zero terminator.
fpl_common_api int32_t fplStringToS32(const char *str)
Converts the given string into a 32-bit integer.
fpl_common_api char * fplEnforcePathSeparator(char *path)
Ensures that the given string always ends with a path separator.
fpl_platform_api bool fplConditionBroadcast(fplConditionVariable *condition)
Wakes up all threads that wait on the given condition.
fpl_platform_api fplThreadHandle * fplThreadCreateWithParameters(fplThreadParameters *parameters)
Creates and starts a thread from the specified fplThreadParameters and returns the handle to it.
uint32_t fplThreadState
A type definition for mapping fplThreadState into a 32-bit integer.
fpl_platform_api void fplSemaphoreDestroy(fplSemaphoreHandle *semaphore)
Releases the internal semaphore resources.
fpl_platform_api bool fplMutexInit(fplMutexHandle *mutex)
Initializes the given mutex.
fpl_platform_api bool fplSignalWaitForAny(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout)
Waits until any of the given signals wakes up or the timeout has been reached.
fplSignalValue
An enumeration of signal values.
fpl_platform_api void fplThreadSleep(const uint32_t milliseconds)
Let the current thread sleep for the given amount of milliseconds.
fpl_platform_api bool fplSignalInit(fplSignalHandle *signal, const fplSignalValue initialValue)
Initializes the given signal.
fpl_platform_api bool fplConditionWait(fplConditionVariable *condition, fplMutexHandle *mutex, const fplTimeoutValue timeout)
Sleeps on the given condition and releases the mutex when done.
fpl_platform_api bool fplMutexUnlock(fplMutexHandle *mutex)
Unlocks the given mutex.
fpl_platform_api bool fplThreadYield()
Let the current thread yield execution to another thread that is ready to run on this core.
fpl_platform_api bool fplSignalSet(fplSignalHandle *signal)
Sets the signal and wakes up the given signal.
fpl_platform_api bool fplSemaphoreRelease(fplSemaphoreHandle *semaphore)
Increments the semaphore value by one.
fpl_platform_api fplThreadPriority fplGetThreadPriority(fplThreadHandle *thread)
Retrieves the current thread priority from the OS from the given fplThreadHandle .
fpl_platform_api bool fplSetThreadPriority(fplThreadHandle *thread, const fplThreadPriority newPriority)
Changes the thread priority to the given one, for the given fplThreadHandle .
fpl_common_api fplThreadState fplGetThreadState(fplThreadHandle *thread)
Gets the current thread state for the given thread.
fpl_common_api size_t GetUsedThreadCount()
Gets the number of used/active threads.
fpl_common_api const fplThreadHandle * fplGetMainThread()
Gets the thread handle for the main thread.
fplThreadStates
An enumeration of thread states.
fplThreadPriority
Defines all possible thread priorities.
void() fpl_run_thread_callback(const fplThreadHandle *thread, void *data)
A callback to execute user code inside another thread.
fpl_platform_api bool fplSemaphoreWait(fplSemaphoreHandle *semaphore, const fplTimeoutValue timeout)
Waits for the semaphore until it gets signaled or the timeout has been reached.
fpl_platform_api bool fplSignalWaitForAll(fplSignalHandle **signals, const size_t count, const size_t stride, const fplTimeoutValue timeout)
Waits until all the given signals are waked up.
fpl_platform_api bool fplConditionSignal(fplConditionVariable *condition)
Wakes up one thread that waits on the given condition.
fpl_platform_api fplThreadHandle * fplThreadCreate(fpl_run_thread_callback *runFunc, void *data)
Creates and starts a thread and returns the handle to it.
fpl_platform_api bool fplThreadWaitForAll(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout)
Wait until all given threads are done running or the given timeout has been reached.
fpl_platform_api void fplConditionDestroy(fplConditionVariable *condition)
Releases the given condition and clears the structure to zero.
fpl_platform_api void fplMutexDestroy(fplMutexHandle *mutex)
Releases the given mutex and clears the structure to zero.
fpl_common_api size_t GetAvailableThreadCount()
Gets the number of available threads.
fpl_platform_api int32_t fplSemaphoreValue(fplSemaphoreHandle *semaphore)
Gets the current semaphore value.
fpl_platform_api bool fplSemaphoreInit(fplSemaphoreHandle *semaphore, const uint32_t initialValue)
Initializes the semaphore with the given initial value.
fpl_platform_api bool fplThreadWaitForOne(fplThreadHandle *thread, const fplTimeoutValue timeout)
Wait until the given thread is done running or the given timeout has been reached.
fpl_platform_api bool fplSemaphoreTryWait(fplSemaphoreHandle *semaphore)
Tries to wait for the semaphore until it gets signaled or return immediately.
fpl_platform_api bool fplSignalWaitForOne(fplSignalHandle *signal, const fplTimeoutValue timeout)
Waits until the given signal is waked up.
fpl_platform_api bool fplMutexLock(fplMutexHandle *mutex)
Locks the given mutex and blocks any other threads.
fpl_platform_api bool fplThreadWaitForAny(fplThreadHandle **threads, const size_t count, const size_t stride, const fplTimeoutValue timeout)
Wait until one of the given threads is done running or the given timeout has been reached.
fpl_platform_api void fplSignalDestroy(fplSignalHandle *signal)
Releases the given signal and clears the structure to zero.
fpl_platform_api bool fplSignalReset(fplSignalHandle *signal)
Resets the signal.
fpl_platform_api bool fplThreadTerminate(fplThreadHandle *thread)
Forces the given thread to stop and release all underlying resources.
fpl_platform_api uint32_t fplGetCurrentThreadId()
Gets the thread id for the current thread.
fpl_platform_api bool fplMutexTryLock(fplMutexHandle *mutex)
Tries to lock the given mutex without blocking other threads.
fpl_platform_api bool fplConditionInit(fplConditionVariable *condition)
Initialize s the given condition.
@ fplSignalValue_Unset
Value is unset.
@ fplSignalValue_Set
Value is set.
@ fplThreadState_Starting
Thread is being started.
@ fplThreadState_Stopping
Thread is being stopped.
@ fplThreadState_Stopped
Thread is stopped.
@ fplThreadState_Running
Thread is still running.
@ fplThreadPriority_Low
Low priority.
@ fplThreadPriority_First
First fplThreadPriority.
@ fplThreadPriority_Idle
Idle priority (Only when nothing is going on)
@ fplThreadPriority_Lowest
Lowest fplThreadPriority.
@ fplThreadPriority_High
High priority.
@ fplThreadPriority_Normal
Normal priority.
@ fplThreadPriority_RealTime
Realtime priority (Time critical)
@ fplThreadPriority_Highest
Highest fplThreadPriority.
@ fplThreadPriority_Unknown
Unknown priority.
@ fplThreadPriority_Last
Last fplThreadPriority.
uint64_t fplMilliseconds
A type definition for milliseconds / 64-bit.
fpl_platform_api fplMilliseconds fplMillisecondsQuery()
Gets the current system clock in milliseconds, since some fixed starting point (OS start,...
double fplSeconds
A type definition for seconds / 64-bit.
fpl_platform_api fplSeconds fplTimestampElapsed(const fplTimestamp start, const fplTimestamp finish)
Gets the delta value from two fplTimestamp values in seconds.
fpl_platform_api fplTimestamp fplTimestampQuery()
Gets the current fplTimestamp with most precision, used for time delta measurements only.
uint32_t fplTimeoutValue
A type definition for a timeout value in milliseconds.
#define FPL_TIMEOUT_INFINITE
Infinite timeout constant.
fpl_common_api fplVideoBackendType fplGetVideoBackendType()
Gets the current video backend.
fpl_common_api const fplVideoSurface * fplGetVideoSurface()
Gets the current fplVideoSurface that stores all handles used for the active video backend.
fpl_common_api const void * fplGetVideoProcedure(const char *procName)
Gets the procedure by the specified name from the active video backend.
fpl_inline fplVideoRect fplCreateVideoRectFromLTRB(int32_t left, int32_t top, int32_t right, int32_t bottom)
Makes a video rectangle from a LTRB rectangle.
fpl_common_api bool fplResizeVideoBackBuffer(const uint32_t width, const uint32_t height)
Resizes the current video backbuffer.
fpl_common_api bool fplGetVideoRequirements(const fplVideoBackendType backendType, fplVideoRequirements *requirements)
Gets the video requirements for the specified video backend.
fpl_common_api fplVideoBackBuffer * fplGetVideoBackBuffer()
Retrieves the pointer to the current video backbuffer.
fpl_common_api const char * fplGetVideoBackendName(fplVideoBackendType backendType)
Gets a string that represents the given video backend.
fpl_common_api void fplVideoFlip()
Forces the window to be redrawn or to swap the back/front buffer.
fpl_platform_api bool fplWindowUpdate()
Clears the internal event queue and updates input devices if needed.
fpl_platform_api bool fplSetWindowState(const fplWindowState newState)
Changes the current window state.
fpl_platform_api bool fplGetWindowPosition(fplWindowPosition *outPos)
Retrieves the absolute window position.
fpl_platform_api void fplSetWindowFloating(const bool value)
Enables or disables the window floating (Top-most)
fpl_platform_api bool fplSetWindowFullscreenRect(const bool value, const int32_t x, const int32_t y, const int32_t width, const int32_t height)
Enables or disables fullscreen mode based on the given rectangle.
fpl_platform_api bool fplDisableWindowFullscreen()
Switches the window back to window mode.
fpl_platform_api bool fplIsWindowFloating()
Gets the window floating state as boolean.
fpl_platform_api void fplSetWindowDecorated(const bool value)
Enables or disables the window decoration (Titlebar, Border, etc.).
fpl_platform_api void fplSetWindowResizeable(const bool value)
Enables or disables the ability to resize the window.
fpl_platform_api bool fplEnableWindowFullscreen()
Enables fullscreen mode on the nearest display.
fplWindowState
An enumeration containg the states of a window.
fpl_platform_api fplWindowState fplGetWindowState()
Gets the current window state.
fpl_platform_api void fplSetWindowPosition(const int32_t left, const int32_t top)
Changes the window absolute position to the given coordinates.
fpl_platform_api void fplSetWindowTitle(const char *title)
Changes the window title to the given string.
fpl_platform_api bool fplGetWindowSize(fplWindowSize *outSize)
Retrieves the inner window size.
fpl_platform_api bool fplIsWindowResizable()
Gets the window resizable state as boolean.
fpl_platform_api bool fplSetWindowFullscreenSize(const bool value, const uint32_t fullscreenWidth, const uint32_t fullscreenHeight, const uint32_t refreshRate)
Enables or disables fullscreen mode based on the given size and the current display.
fpl_common_api char * fplGetWindowTitle(char *outTitle, const size_t maxOutTitleLength)
Retrieves the window title and writes it into the output string.
fpl_platform_api void fplWindowShutdown()
Closes the window and stops the event loop.
fpl_platform_api void fplSetWindowSize(const uint32_t width, const uint32_t height)
Resizes the window to fit the inner size based on the given size.
fpl_platform_api void fplSetWindowCursorEnabled(const bool value)
Enables or disables the window cursor.
fpl_platform_api bool fplIsWindowRunning()
Gets the window running state as a boolean.
fpl_platform_api bool fplIsWindowDecorated()
Gets the window decorated state as boolean.
fpl_common_api void fplSetWindowInputEvents(const bool enabled)
Enables or Disables the input events for the window entirely.
fpl_platform_api bool fplIsWindowFullscreen()
Gets the window fullscreen state as boolean.
fplWindowVisibilityState
An enumeration containing the visibility state of a window.
@ fplWindowState_Unknown
Unknown state.
@ fplWindowState_Fullscreen
Fullscreen state.
@ fplWindowState_Normal
Normal window state.
@ fplWindowState_Maximize
Maximize window state.
@ fplWindowState_Iconify
Iconify/Minimize window state.
@ fplWindowVisibilityState_Unknown
Unknown state.
@ fplWindowVisibilityState_Show
Window is visible.
@ fplWindowVisibilityState_Hide
Window is hidden.
fpl_platform_api bool fplSetClipboardText(const char *text)
Overwrites the current clipboard text with the given one.
fpl_platform_api bool fplGetClipboardText(char *dest, const uint32_t maxDestLen)
Retrieves the current clipboard text.
fpl_platform_api bool fplGetDisplayFromPosition(const int32_t x, const int32_t y, fplDisplayInfo *outInfo)
Finds the display from a cursor position and retrieves the information for it.
fpl_platform_api size_t fplGetDisplays(fplDisplayInfo *outDisplays, const size_t maxDisplayCount)
Gets informations about all active displays.
fpl_platform_api size_t fplGetDisplayModes(const char *id, fplDisplayMode *outModes, const size_t maxDisplayModeCount)
Gets the information about the available display modes for the given display id.
fpl_platform_api size_t fplGetDisplayCount()
Gets the number of active displays.
fpl_platform_api bool fplGetWindowDisplay(fplDisplayInfo *outInfo)
Gets information about the display for the FPL window.
fpl_platform_api bool fplGetPrimaryDisplay(fplDisplayInfo *outInfo)
Gets information about the primary display.
fplWindowEventType
An enumeration of window event types (Resized, PositionChanged, etc.)
fpl_platform_api bool fplPollEvent(fplEvent *ev)
Polls the next event from the internal event queue or from the OS, handles them, and removes it from ...
fpl_platform_api void fplPollEvents()
Polls all the events from the OS and clears the internal event queue.
fplButtonState
An enumeration of button states.
fplMouseEventType
An enumeration of mouse event types (Move, ButtonDown, ...)
fplKey
An enumeration of mapped keys (Based on MS Virtual-Key-Codes, mostly directly mapped from ASCII)
fplGamepadButtonType
An enumeration of gamepad buttons.
fplMouseButtonType
An enumeration of mouse button types (Left, Right, ...)
fplGamepadEventType
An enumeration of gamepad event types (Connected, Disconnected, StateChanged, etc....
fplKeyboardEventType
An enumeration of keyboard event types.
fplEventType
An enumeration of event types (Window, Keyboard, Mouse, ...)
fplKeyboardModifierFlags
An enumeration of keyboard modifier flags.
@ fplWindowEventType_PositionChanged
Window was moved.
@ fplWindowEventType_Minimized
Window has been minimized.
@ fplWindowEventType_LostFocus
Window lost focus.
@ fplWindowEventType_Shown
Window was shown.
@ fplWindowEventType_None
None window event type.
@ fplWindowEventType_Exposed
Window was exposed.
@ fplWindowEventType_Restored
Window has been restored.
@ fplWindowEventType_Hidden
Window was hidden.
@ fplWindowEventType_Resized
Window has been resized.
@ fplWindowEventType_Closed
Window was closed.
@ fplWindowEventType_DroppedFiles
Dropped one or more files into the window.
@ fplWindowEventType_Maximized
Window has been maximized.
@ fplWindowEventType_GotFocus
Window got focus.
@ fplButtonState_Press
Key pressed.
@ fplButtonState_Release
Key released.
@ fplButtonState_Repeat
Key is hold down.
@ fplMouseEventType_Move
Mouse position has been changed.
@ fplMouseEventType_Button
Mouse button event.
@ fplMouseEventType_Wheel
Mouse wheel event.
@ fplMouseEventType_None
No mouse event type.
@ fplKey_Oem3
'‘~’ for US
@ fplKey_Oem4
'[{' for US
@ fplKey_Oem5
'|' for US
@ fplKey_Oem7
''"' for US
@ fplKey_OemPlus
'+' for any country
@ fplKey_OemMinus
'-' for any country
@ fplKey_Oem1
'/?' for US
@ fplKey_OemPeriod
'.' for any country
@ fplKey_Oem6
']}' for US
@ fplKey_OemComma
',' for any country
@ fplKey_Oem2
'/?' for US
@ fplGamepadButtonType_LeftShoulder
Left-Shoulder.
@ fplGamepadButtonType_LeftThumb
Left-Thumb.
@ fplGamepadButtonType_ActionY
Action-Y.
@ fplGamepadButtonType_DPadRight
DPad right.
@ fplGamepadButtonType_Start
Start.
@ fplGamepadButtonType_RightShoulder
Right-Shoulder.
@ fplGamepadButtonType_DPadDown
DPad down.
@ fplGamepadButtonType_ActionA
Action-A.
@ fplGamepadButtonType_ActionX
Action-X.
@ fplGamepadButtonType_RightThumb
Right-Thumb.
@ fplGamepadButtonType_Back
Back.
@ fplGamepadButtonType_DPadLeft
DPad left.
@ fplGamepadButtonType_ActionB
Action-B.
@ fplGamepadButtonType_DPadUp
DPad up.
@ fplMouseButtonType_Middle
Middle mouse button.
@ fplMouseButtonType_MaxCount
Max mouse button count.
@ fplMouseButtonType_Right
Right mouse button.
@ fplMouseButtonType_Left
Left mouse button.
@ fplMouseButtonType_None
No mouse button.
@ fplGamepadEventType_StateChanged
Gamepad state updated.
@ fplGamepadEventType_Disconnected
Gamepad disconnected.
@ fplGamepadEventType_Connected
Gamepad connected.
@ fplGamepadEventType_None
No gamepad event.
@ fplKeyboardEventType_None
None key event type.
@ fplKeyboardEventType_Input
Character was entered.
@ fplKeyboardEventType_Button
Key button event.
@ fplEventType_None
None event type.
@ fplEventType_Keyboard
Keyboard event.
@ fplEventType_Window
Window event.
@ fplEventType_Mouse
Mouse event.
@ fplEventType_Gamepad
Gamepad event.
@ fplKeyboardModifierFlags_RAlt
Right alt key is down.
@ fplKeyboardModifierFlags_LAlt
Left alt key is down.
@ fplKeyboardModifierFlags_None
No modifiers.
@ fplKeyboardModifierFlags_NumLock
Numlock state is active.
@ fplKeyboardModifierFlags_RCtrl
Right ctrl key is down.
@ fplKeyboardModifierFlags_LShift
Left shift key is down.
@ fplKeyboardModifierFlags_ScrollLock
Scrolllock state is active.
@ fplKeyboardModifierFlags_RShift
Right shift key is down.
@ fplKeyboardModifierFlags_LSuper
Left super key is down.
@ fplKeyboardModifierFlags_RSuper
Right super key is down.
@ fplKeyboardModifierFlags_CapsLock
Capslock state is active.
@ fplKeyboardModifierFlags_LCtrl
Left ctrl key is down.
A structure containing audio device format runtime properties, such as type, samplerate,...
uint32_t bufferSizeInFrames
Buffer size in frames.
fplAudioDefaultFields defaultFields
Default fields.
fplAudioFormatType type
Format.
uint32_t sampleRate
Samples per seconds.
fplAudioBackendType backend
Audio backend.
uint32_t channels
Number of channels.
fpl_b32 preferExclusiveMode
Is exclusive mode preferred.
uint32_t periods
Number of periods.
A structure containing the name and the id of the audio device.
char name[FPL_MAX_NAME_LENGTH]
Device name.
fplAudioDeviceID id
Device id.
A structure containing audio settings, such as format, device info, callbacks, backend,...
fplAudioDeviceInfo targetDevice
The target device.
fpl_b32 startAuto
Start playing of audio samples after platform initialization automatically.
void * userData
User data pointer for client read callback.
fpl_audio_client_read_callback * clientReadCallback
The callback for retrieving audio data from the client.
fpl_b32 stopAuto
Stop playing of audio samples before platform release automatically.
fplAudioBackendType backend
The targeted backend.
fplAudioTargetFormat targetFormat
The target format.
fplSpecificAudioSettings specific
Specific settings.
A structure containing audio target format configurations, such as type, sample rate,...
fpl_b32 preferExclusiveMode
Is exclusive mode preferred.
fplAudioLatencyMode latencyMode
Latency mode.
uint32_t periods
Number of periods (uses default of 3 when zero)
uint32_t bufferSizeInMilliseconds
Buffer size in milliseconds (Second choice)
uint32_t sampleRate
Samples per seconds (uses default of 44100 when zero)
fplAudioFormatType type
Audio format (uses default of S16 when zero)
uint32_t bufferSizeInFrames
Buffer size in frames (First choice)
uint32_t channels
Number of channels (uses default of 2 when zero)
A structure that containing the processor capabilities, like MMX,SSE,AVX etc.
fpl_b32 hasAVX
Is AVX supported.
fpl_b32 hasMMX
Is MMX supported.
fpl_b32 hasSSE2
Is SSE-2 supported.
fpl_b32 hasSSE3
Is SSE-3 supported.
fpl_b32 hasAVX512
Is AVX-512 supported.
fpl_b32 hasSSE
Is SSE supported.
fpl_b32 hasSSE4_1
Is SSE-4.1 supported.
fpl_b32 hasSSE4_2
Is SSE-4.2 supported.
fpl_b32 hasFMA3
Is FMA-3 supported.
fpl_b32 hasSSSE3
Is SSSE-3 supported.
fpl_b32 hasAVX2
Is AVX-2 supported.
The condition variable structure.
fpl_b32 isValid
Is it valid.
fplInternalConditionVariable internalHandle
The internal condition handle.
A structure containing the title and options for the console.
char title[FPL_MAX_NAME_LENGTH]
Console title.
A struct containing informations about a display.
char id[FPL_MAX_NAME_LENGTH]
ID of the display.
fplWindowSize virtualSize
Virtual size in screen coordinates.
fpl_b32 isPrimary
Is primary display.
fplWindowPosition virtualPosition
Virtual position in screen coordinates.
fplWindowSize physicalSize
Actual absolute size in screen coordinates.
A structure containing one set of display mode settings, such as size, refresh rate,...
uint32_t colorBits
Color depth in bits per pixel.
uint32_t height
The height in screen coordinates.
uint32_t width
The width in screen coordinates.
uint32_t refreshRate
The refresh rate in Hz.
A structure containing the internal handle to a dynamic library.
fpl_b32 isValid
Library opened successfully.
fplInternalDynamicLibraryHandle internalHandle
Internal library handle.
A structure containing event data for all event types (Window, Keyboard, Mouse, etc....
fplMouseEvent mouse
Mouse event data.
fplKeyboardEvent keyboard
Keyboard event data.
fplWindowEvent window
Window event data.
fplGamepadEvent gamepad
Gamepad event data.
fplEventType type
Event type.
A structure containing the informations for a file or directory (name, type, attributes,...
fplInternalFileRootInfo internalRoot
Internal root info.
fplFileEntryType type
Entry type.
fplFilePermissions permissions
Permissions.
size_t size
Size (Zero when not a file)
fplInternalFileEntryHandle internalHandle
Internal filehandle.
char name[FPL_MAX_FILENAME_LENGTH]
Name.
fplFileTimeStamps timeStamps
Time stamps.
fplFileAttributeFlags attributes
Attributes.
The filehandle structure.
fplInternalFileHandle internalHandle
Internal filehandle.
fpl_b32 isValid
File opened successfully.
A structure containing filestamps for creation/access/modify date.
fplFileTimeStamp creationTime
Creation timestamp.
fplFileTimeStamp lastAccessTime
Last access timestamp.
fplFileTimeStamp lastModifyTime
Last modify timestamp.
A structure containing properties for a gamepad button (IsDown, etc.)
fpl_b32 isDown
Is button down.
A structure containing gamepad event data (Type, Device, State, etc.)
const char * deviceName
Device name of the controller.
uint32_t deviceIndex
Gamepad device index.
fplGamepadEventType type
Gamepad event type.
fplGamepadState state
Full gamepad state.
A structure containing the entire gamepad state.
fplGamepadButton rightThumb
Analog right thumb button.
fplGamepadButton leftShoulder
Left shoulder button.
float leftTrigger
Analog left trigger in range (0.0 to 1.0f)
fplGamepadButton buttons[14]
All gamepad buttons.
fpl_b32 isActive
Is this device active, which means are any buttons pressed or positions stick changed.
fpl_b32 isConnected
Is device physical connected.
float leftStickX
Analog left thumb X in range (-1.0 to 1.0f)
fplGamepadButton back
Back button.
float rightStickY
Analog right thumb Y in range (-1.0 to 1.0f)
float leftStickY
Analog left thumb Y in range (-1.0 to 1.0f)
float rightTrigger
Analog right trigger in range (0.0 to 1.0f)
fplGamepadButton dpadUp
Digital button up.
fplGamepadButton dpadRight
Digital button right.
float rightStickX
Analog right thumb X in range (-1.0 to 1.0f)
fplGamepadButton start
Start button.
fplGamepadButton actionX
Action button X.
fplGamepadButton actionA
Action button A.
const char * deviceName
Device name.
fplGamepadButton dpadDown
Digital button down.
fplGamepadButton rightShoulder
Right shoulder button.
fplGamepadButton dpadLeft
Digital button left.
fplGamepadButton actionB
Action button B.
fplGamepadButton actionY
Action button Y.
fplGamepadButton leftThumb
Analog left thumb button.
A struct containing the full state for all gamepad devices.
fplGamepadState deviceStates[FPL_MAX_GAMEPAD_STATE_COUNT]
Device states.
A union that contains graphics api settings.
fplOpenGLSettings opengl
OpenGL settings.
fplVulkanSettings vulkan
Vulkan settings.
int dummy
Field for preventing union to be empty.
A structure containing data for working with a image source.
uint32_t width
Width in pixels.
fplImageType type
Image type.
const uint8_t * data
Pointer to the source data.
uint32_t height
Height in pixels.
A structure containing input settings.
uint32_t controllerDetectionFrequency
Frequency in ms for detecting new or removed controllers (Default: 200)
fpl_b32 disabledEvents
Disable input events entirely (Default: false)
A structure containing the internal root file informations.
const char * rootPath
Saved root path.
const char * filter
Saved filter wildcard.
A structure containing keyboard event data (Type, Keycode, Mapped key, etc.)
uint64_t keyCode
Raw ascii key code or 32-bit unicode for text input.
fplKeyboardEventType type
Keyboard event type.
fplKey mappedKey
Mapped key.
fplButtonState buttonState
Button state.
fplKeyboardModifierFlags modifiers
Keyboard modifiers.
A struct containing the full keyboard state.
fplButtonState buttonStatesMapped[FPL_MAX_KEYBOARD_STATE_COUNT]
Mapped button states.
fplKeyboardModifierFlags modifiers
Modifier flags.
fpl_b32 keyStatesRaw[FPL_MAX_KEYBOARD_STATE_COUNT]
Key states.
A structure containing log settings.
fplLogWriter writers[1]
Single writer.
fplLogLevel maxLevel
Maximum log level.
fpl_b32 isInitialized
Is initialized (When set to false all values will be set to default values)
A structure containing console logging properties.
int dummy
Field for preventing struct to be empty.
A structure containing properties custom logging properties.
fpl_log_func_callback * callback
User callback.
A structure containing log writer settings.
fplLogWriterFlags flags
Flags.
fplLogWriterConsole console
Console.
fplLogWriterCustom custom
Custom.
A structure for setting up memory allocation usage.
fpl_memory_release_callback * releaseCallback
Callback for releasing memory.
void * userData
User data passed through callbacks.
fpl_memory_allocate_callback * allocateCallback
Callback for allocating memory.
fplMemoryAllocationMode mode
Memory allocation mode.
Defines a memory block.
size_t size
The size of the allocated memory.
void * base
The base pointer.
A structure that contains informations about current memory usage.
uint64_t installedPhysicalSize
Size of physical installed memory in bytes.
uint64_t freePageCount
Number of available memory pages.
uint64_t totalCacheSize
Total size of memory cache in bytes.
uint64_t totalPhysicalSize
Total size of physical memory in bytes (May be less than size of installed physical memory,...
uint64_t totalPageCount
Total number of memory pages.
uint64_t freePhysicalSize
Available physical memory in bytes.
uint64_t pageSize
Page size in bytes.
uint64_t freeCacheSize
Available size of the memory cache in bytes.
A structure for setting up memory settings for dynamic and temporary allocations.
fplMemoryAllocationSettings dynamic
Dynamic memory allocation settings.
fplMemoryAllocationSettings temporary
Temporary memory allocation settings.
A structure containing mouse event data (Type, Button, Position, etc.)
fplMouseButtonType mouseButton
Mouse button.
int32_t mouseX
Mouse X-Position.
fplMouseEventType type
Mouse event type.
fplButtonState buttonState
Button state.
int32_t mouseY
Mouse Y-Position.
float wheelDelta
Mouse wheel delta.
A struct containing the full mouse state.
int32_t y
Y-Position in pixels.
fplButtonState buttonStates[fplMouseButtonType_MaxCount]
Mouse button states mapped to fplMouseButtonType.
int32_t x
X-Position in pixels.
The mutex handle structure.
fplInternalMutexHandle internalHandle
The internal mutex handle.
fpl_b32 isValid
Is it valid.
A structure that contains the version information for the operating system.
char osName[FPL_MAX_NAME_LENGTH]
Name of the operating system.
char distributionName[FPL_MAX_NAME_LENGTH]
Name of the distribution (May be empty)
fplVersionInfo osVersion
Version of the operating system.
fplVersionInfo distributionVersion
Version of the distribution (May be empty)
A structure that contains OpenGL video settings.
uint32_t minorVersion
Desired minor version.
const char * libraryFile
Custom OpenGL driver library file name/path (null = Default OpenGL library)
fplOpenGLCompabilityFlags compabilityFlags
Compability flags.
uint8_t multiSamplingCount
Multisampling count.
uint32_t majorVersion
Desired major version.
The semaphore handle structure.
fplInternalSemaphoreHandle internalHandle
The internal semaphore handle.
fpl_b32 isValid
Is it valid.
A structure containing settings, such as window, video, etc.
fplWindowSettings window
Window settings.
fplAudioSettings audio
Audio settings.
fplMemorySettings memory
Memory settings.
fplConsoleSettings console
Console settings.
fplInputSettings input
Input settings.
fplVideoSettings video
Video settings.
The signal handle structure.
fplInternalSignalHandle internalHandle
The internal signal handle.
fpl_b32 isValid
Is it valid.
The thread handle structure.
fplInternalThreadHandle internalHandle
The internal thread handle.
fplThreadParameters parameters
The initial fplThreadParameters.
volatile fpl_b32 isValid
Is this thread valid.
uint32_t id
The identifier of the thread.
volatile fplThreadState currentState
Thread state.
volatile fpl_b32 isStopping
Is this thread stopping.
Contains creation parameters for fplThreadCreateWithParameters()
size_t stackSize
The stack size in bytes or zero for using the default size.
fplThreadPriority priority
The fplThreadPriority.
fpl_run_thread_callback * runFunc
The fpl_run_thread_callback.
void * userData
The user data passed to the run callback.
A structure that contains version informations.
fplVersionNumberPart fix
Fix version.
fplVersionNumberPart build
Build version.
fplVersionNumberPart values[4]
Version number parts.
fplVersionNumberPart major
Major version.
fplVersionNumberPart minor
Minor version.
char fullName[FPL_MAX_NAME_LENGTH]
Full name.
A structure containing video backbuffer properties.
uint32_t * pixels
The 32-bit pixel top-down array, format: 0xAABBGGRR. Do not modify before WindowUpdate.
fplVideoRect outputRect
The output rectangle for displaying the backbuffer (Size may not match backbuffer size!...
size_t pixelStride
The size of one entire pixel with all 4 components in bytes. Do not modify, it will be set automatica...
uint32_t height
The height of the backbuffer in pixels. Do not modify, it will be set automatically.
size_t lineWidth
The width of one line in bytes. Do not modify, it will be set automatically.
uint32_t width
The width of the backbuffer in pixels. Do not modify, it will be set automatically.
fpl_b32 useOutputRect
Set this to true to actually use the output rectangle.
A structure defining a video rectangles position and size.
int32_t x
Left position in pixels.
int32_t height
Height in pixels.
int32_t y
Top position in pixels.
int32_t width
Width in pixels.
Stores the requirements for the Vulkan video backend.
const char * instanceExtensions[2]
The required instance extensions.
uint32_t instanceExtensionCount
The number of required instance extensions.
A structure that contains video settings such as backend, v-sync, API-settings, etc.
fplVideoBackendType backend
video backend type
fpl_b32 isAutoSize
Is backbuffer automatically resized. Usable only for software rendering!
fpl_b32 isVSync
Is vertical synchronization enabled. Usable only for hardware rendering!
fplGraphicsApiSettings graphics
Graphics API settings.
Stores the surface properties for the active video backend.
int dummy
Field for preventing union to be empty.
fplVideoWindow window
The video window.
fplVideoSurfaceVulkan vulkan
The Vulkan surface properties.
fplVideoSurfaceOpenGL opengl
The OpenGL surface properties.
Stores the surface properties for the OpenGL video backend.
void * renderingContext
The OpenGL rendering context (HGLRC or XRC)
Stores the surface properties for the Vulkan video backend.
void * surfaceKHR
The Vulkan Surface KHR (VkSurfaceKHR)
void * instance
The Vulkan Instance (VkInstance)
A structure that contains Vulkan video settings.
fplVersionInfo engineVersion
The engine version (Only required if fplVulkanSettings::instanceHandle is fpl_null)
fplVersionInfo apiVersion
The preferred Vulkan api version (Only required if fplVulkanSettings::instanceHandle is fpl_null)
fplVulkanValidationLayerMode validationLayerMode
The fplVulkanValidationLayerMode.
const char * appName
The application name (Only required if fplVulkanSettings::instanceHandle is fpl_null)
void * userData
User data passed to any callbacks.
void * instanceHandle
The vulkan instance (VkInstance), when null it will be automatically created.
fplVersionInfo appVersion
The application version (Only required if fplVulkanSettings::instanceHandle is fpl_null)
const char * engineName
The engine name (Only required if fplVulkanSettings::instanceHandle is fpl_null)
const char * libraryFile
Custom Vulkan driver library file name/path (null = Default Vulkan library)
fplVulkanValidationSeverity validationSeverity
The fplVulkanValidationSeverity.
const void * allocator
The vulkan allocator (VkAllocationCallbacks)
fplVulkanValidationLayerCallback * validationLayerCallback
The validation layer callback fplVulkanValidationLayerCallback.
A structure containing the window callbacks.
void * eventUserData
User data pointer for the event callback.
fpl_window_event_callback * eventCallback
Expose callback.
void * exposedUserData
User data pointer for the expose callback.
fpl_window_exposed_callback * exposedCallback
Expose callback.
A structure containing number and dropped files informations.
fplMemoryBlock internalMemory
The internal memory block.
size_t fileCount
Number of dropped in files.
const char ** files
File paths (Do not release this memory, its automatically released after the event is processed)
A structure containing window event data (Size, Position, etc.)
fplWindowSize size
Window size.
fplWindowDropFiles dropFiles
Drop files.
fplWindowEventType type
Window event type.
fplWindowPosition position
Window position.
A structure containing the position of a window.
int32_t left
Left position in screen coordinates.
int32_t top
Top position in screen coordinates.
A structure containing window settings, such as size, title etc.
fpl_b32 isFloating
Is floating.
fplWindowSize windowSize
Window size in screen coordinates.
fplWindowSize fullscreenSize
Fullscreen size in screen coordinates.
fpl_b32 isResizable
Is window resizable.
fpl_b32 isScreenSaverPrevented
Is screen saver prevented (true: prevents the screensaver to kick-in, false: system behavior)
fpl_b32 isMonitorPowerPrevented
Is monitor power change prevented (true: prevents the monitor for powering off automatically,...
fplColor32 background
The background fplColor32 for the window when using fplVideoBackendType_Software or fplVideoBackendTy...
fplWindowCallbacks callbacks
Window callbacks.
fpl_b32 isFullscreen
Is window in fullscreen mode.
fplImageSource icons[2]
Window icons (0 = Small, 1 = Large)
uint32_t fullscreenRefreshRate
Fullscreen refresh rate in Hz.
char title[FPL_MAX_NAME_LENGTH]
Window title.
fpl_b32 isDecorated
Is window decorated.
A structure containing the size of a window.
uint32_t width
Width in screen coordinates.
uint32_t height
Height in screen coordinates.
A union containing a id of the underlying backend.
int dummy
Field for preventing union to be empty.
A structure containing the 4-registers (EAX, EBX, ECX, EDX) for a CPU-Leaf.
uint32_t edx
The 32-bit EDX Register.
uint32_t ecx
The 32-bit ECX Register.
uint32_t eax
The 32-bit EAX Register.
uint32_t raw[4]
The raw 32-bit register array.
uint32_t ebx
The 32-bit EBX Register.
Defines a 32-bit color in format BGRA.
uint8_t a
The 8-bit alpha component.
uint32_t value
The 32-bit color value in format 0xBBGGRRAA.
uint8_t g
The 8-bit green component.
uint8_t r
The 8-bit red component.
uint8_t m[4]
The color components array, stored as B, G, R, A.
uint8_t b
The 8-bit blue component.
A union containing the file permissions (UMask)
uint8_t owner
Owner flags.
uint8_t user
User flags.
uint8_t group
Group flags.
A union containing the internal condition variable for any platform.
int dummy
Field for preventing union to be empty.
A union containing the library handle for any platform.
A union containing the internal filehandle for any platform.
A union containing the internal filehandle for any platform.
A union containing the internal mutex handle for any platform.
A union containing the internal semaphore handle for any platform.
A union containing the internal signal handle for any platform.
A union containing the thread handle for any platform.
A union containing backend specific audio settings.
int dummy
Field for preventing union to be empty.
A structure storing a timestamp, used for delta measurements only.
uint64_t unused
Field for preventing union to be empty.
Stores the video requirements for the desired video backend.
fplVideoRequirementsVulkan vulkan
The requirements for Vulkan backend.
Stores the video window handles.
int dummy
Field for preventing union to be empty.